Skip to content

Commit c02c125

Browse files
committed
Configure Azure Developer Pipeline
1 parent 6b0f1dd commit c02c125

10 files changed

Lines changed: 573 additions & 4 deletions

File tree

.github/workflows/azure-dev.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Deploy App
2+
3+
on:
4+
workflow_dispatch:
5+
workflow_run:
6+
workflows: ["Run Tests"]
7+
branches: [main]
8+
types: [completed]
9+
10+
permissions:
11+
id-token: write
12+
contents: read
13+
14+
jobs:
15+
deploy:
16+
runs-on: ubuntu-latest
17+
if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success'
18+
concurrency:
19+
group: deploy-production
20+
cancel-in-progress: false
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
25+
- name: Install azd
26+
uses: Azure/setup-azd@v2
27+
28+
- name: Log in with Azure (Federated Credentials)
29+
run: |
30+
azd auth login \
31+
--client-id "${{ vars.AZURE_CLIENT_ID }}" \
32+
--federated-credential-provider "github" \
33+
--tenant-id "${{ vars.AZURE_TENANT_ID }}"
34+
35+
- name: Provision and deploy
36+
run: azd up --no-prompt
37+
env:
38+
AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }}
39+
AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }}
40+
AZURE_LOCATION: ${{ vars.AZURE_LOCATION }}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ htmlcov/
4141

4242
# playwright
4343
client/test-results/
44-
client/playwright-report/
44+
client/playwright-report/
45+
.azure

azure.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
3+
name: pets-workshop
4+
metadata:
5+
template: azd-init@1.23.7
6+
services:
7+
client:
8+
project: client
9+
host: containerapp
10+
language: ts
11+
docker:
12+
path: Dockerfile
13+
server:
14+
project: server
15+
host: containerapp
16+
language: python
17+
resources:
18+
client:
19+
type: host.containerapp
20+
port: 4321
21+
server:
22+
type: host.containerapp
23+
port: 80

content/github-actions/6-deploy-azure.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,20 @@ Let's set up the Azure Developer CLI and scaffold the infrastructure for our pro
6767
> [!TIP]
6868
> Bicep is Azure's domain-specific language for defining infrastructure as code. If you have GitHub Copilot, try asking it to explain the generated Bicep files!
6969

70+
The generated `infra/` directory contains several Bicep files that work together:
71+
72+
- **`main.bicep`** — The entry point. It defines the deployment's parameters (like location and environment name) and orchestrates the other files.
73+
- **`main.parameters.json`** — Default parameter values passed to `main.bicep` at deployment time.
74+
- **`resources.bicep`** — The core of the infrastructure. It defines the Azure Container Apps environment and the individual container apps for the client and server, including their Docker images, environment variables, ingress settings, and scaling rules.
75+
- **`modules/`** — Helper modules referenced by the main files (e.g., for fetching container image metadata).
76+
- **`abbreviations.json`** — A lookup table `azd` uses to generate consistent, short resource names following Azure naming conventions.
77+
7078
## Configure the infrastructure
7179
7280
The generated Bicep files define the Azure Container Apps that will host the client and server. We need to add an environment variable so the client knows where to find the API server.
7381
7482
1. Open `infra/resources.bicep` in your codespace.
75-
2. Find the section (around line 130) that reads:
83+
2. Find the section (around line 109) that reads:
7684
7785
```bicep
7886
{
@@ -168,12 +176,22 @@ Now let's let `azd` configure the pipeline credentials. Because the workflow fil
168176
azd pipeline config
169177
```
170178

171-
This command will:
179+
2. Follow the prompts — here's what to expect:
180+
181+
| Prompt | What to select |
182+
|--------|---------------|
183+
| **Select a provider** | Choose **GitHub** |
184+
| **Enter a unique environment name** | Enter a short name (e.g., `<HANDLE>-pets-workshop`) — this names your Azure resource group |
185+
| **Select an Azure subscription** | Choose the subscription you want to deploy to |
186+
| **Select an Azure location** | Pick a region close to you (e.g., `eastus2`) |
187+
| **Select how to authenticate the pipeline to Azure** | Choose **Federated Service Principal (SP + OIDC)** |
188+
189+
After you answer these, `azd` will:
172190
- Create OIDC credentials in Azure for passwordless authentication
173191
- Store the necessary secrets and variables in your repository automatically
174192
- Detect your existing workflow file and configure it
175193
176-
2. When prompted to commit and push your local changes, say **yes**.
194+
3. When prompted to commit and push your local changes, say **yes**.
177195
178196
> [!TIP]
179197
> After `azd pipeline config` completes, navigate to **Settings** > **Secrets and variables** > **Actions** > **Variables** tab to see the repository variables it created (like `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, etc.). These are the `vars.*` values your workflow references.

infra/abbreviations.json

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
{
2+
"aiFoundryAccounts": "aif",
3+
"analysisServicesServers": "as",
4+
"apiManagementService": "apim-",
5+
"appConfigurationStores": "appcs-",
6+
"appManagedEnvironments": "cae-",
7+
"appContainerApps": "ca-",
8+
"authorizationPolicyDefinitions": "policy-",
9+
"automationAutomationAccounts": "aa-",
10+
"blueprintBlueprints": "bp-",
11+
"blueprintBlueprintsArtifacts": "bpa-",
12+
"cacheRedis": "redis-",
13+
"cdnProfiles": "cdnp-",
14+
"cdnProfilesEndpoints": "cdne-",
15+
"cognitiveServicesAccounts": "cog-",
16+
"cognitiveServicesFormRecognizer": "cog-fr-",
17+
"cognitiveServicesTextAnalytics": "cog-ta-",
18+
"computeAvailabilitySets": "avail-",
19+
"computeCloudServices": "cld-",
20+
"computeDiskEncryptionSets": "des",
21+
"computeDisks": "disk",
22+
"computeDisksOs": "osdisk",
23+
"computeGalleries": "gal",
24+
"computeSnapshots": "snap-",
25+
"computeVirtualMachines": "vm",
26+
"computeVirtualMachineScaleSets": "vmss-",
27+
"containerInstanceContainerGroups": "ci",
28+
"containerRegistryRegistries": "cr",
29+
"containerServiceManagedClusters": "aks-",
30+
"databricksWorkspaces": "dbw-",
31+
"dataFactoryFactories": "adf-",
32+
"dataLakeAnalyticsAccounts": "dla",
33+
"dataLakeStoreAccounts": "dls",
34+
"dataMigrationServices": "dms-",
35+
"dBforMySQLServers": "mysql-",
36+
"dBforPostgreSQLServers": "psql-",
37+
"devicesIotHubs": "iot-",
38+
"devicesProvisioningServices": "provs-",
39+
"devicesProvisioningServicesCertificates": "pcert-",
40+
"documentDBDatabaseAccounts": "cosmos-",
41+
"documentDBMongoDatabaseAccounts": "cosmon-",
42+
"eventGridDomains": "evgd-",
43+
"eventGridDomainsTopics": "evgt-",
44+
"eventGridEventSubscriptions": "evgs-",
45+
"eventHubNamespaces": "evhns-",
46+
"eventHubNamespacesEventHubs": "evh-",
47+
"hdInsightClustersHadoop": "hadoop-",
48+
"hdInsightClustersHbase": "hbase-",
49+
"hdInsightClustersKafka": "kafka-",
50+
"hdInsightClustersMl": "mls-",
51+
"hdInsightClustersSpark": "spark-",
52+
"hdInsightClustersStorm": "storm-",
53+
"hybridComputeMachines": "arcs-",
54+
"insightsActionGroups": "ag-",
55+
"insightsComponents": "appi-",
56+
"keyVaultVaults": "kv-",
57+
"kubernetesConnectedClusters": "arck",
58+
"kustoClusters": "dec",
59+
"kustoClustersDatabases": "dedb",
60+
"logicIntegrationAccounts": "ia-",
61+
"logicWorkflows": "logic-",
62+
"machineLearningServicesWorkspaces": "mlw-",
63+
"managedIdentityUserAssignedIdentities": "id-",
64+
"managementManagementGroups": "mg-",
65+
"migrateAssessmentProjects": "migr-",
66+
"networkApplicationGateways": "agw-",
67+
"networkApplicationSecurityGroups": "asg-",
68+
"networkAzureFirewalls": "afw-",
69+
"networkBastionHosts": "bas-",
70+
"networkConnections": "con-",
71+
"networkDnsZones": "dnsz-",
72+
"networkExpressRouteCircuits": "erc-",
73+
"networkFirewallPolicies": "afwp-",
74+
"networkFirewallPoliciesWebApplication": "waf",
75+
"networkFirewallPoliciesRuleGroups": "wafrg",
76+
"networkFrontDoors": "fd-",
77+
"networkFrontdoorWebApplicationFirewallPolicies": "fdfp-",
78+
"networkLoadBalancersExternal": "lbe-",
79+
"networkLoadBalancersInternal": "lbi-",
80+
"networkLoadBalancersInboundNatRules": "rule-",
81+
"networkLocalNetworkGateways": "lgw-",
82+
"networkNatGateways": "ng-",
83+
"networkNetworkInterfaces": "nic-",
84+
"networkNetworkSecurityGroups": "nsg-",
85+
"networkNetworkSecurityGroupsSecurityRules": "nsgsr-",
86+
"networkNetworkWatchers": "nw-",
87+
"networkPrivateDnsZones": "pdnsz-",
88+
"networkPrivateLinkServices": "pl-",
89+
"networkPublicIPAddresses": "pip-",
90+
"networkPublicIPPrefixes": "ippre-",
91+
"networkRouteFilters": "rf-",
92+
"networkRouteTables": "rt-",
93+
"networkRouteTablesRoutes": "udr-",
94+
"networkTrafficManagerProfiles": "traf-",
95+
"networkVirtualNetworkGateways": "vgw-",
96+
"networkVirtualNetworks": "vnet-",
97+
"networkVirtualNetworksSubnets": "snet-",
98+
"networkVirtualNetworksVirtualNetworkPeerings": "peer-",
99+
"networkVirtualWans": "vwan-",
100+
"networkVpnGateways": "vpng-",
101+
"networkVpnGatewaysVpnConnections": "vcn-",
102+
"networkVpnGatewaysVpnSites": "vst-",
103+
"notificationHubsNamespaces": "ntfns-",
104+
"notificationHubsNamespacesNotificationHubs": "ntf-",
105+
"operationalInsightsWorkspaces": "log-",
106+
"portalDashboards": "dash-",
107+
"powerBIDedicatedCapacities": "pbi-",
108+
"purviewAccounts": "pview-",
109+
"recoveryServicesVaults": "rsv-",
110+
"resourcesResourceGroups": "rg-",
111+
"searchSearchServices": "srch-",
112+
"serviceBusNamespaces": "sb-",
113+
"serviceBusNamespacesQueues": "sbq-",
114+
"serviceBusNamespacesTopics": "sbt-",
115+
"serviceEndPointPolicies": "se-",
116+
"serviceFabricClusters": "sf-",
117+
"signalRServiceSignalR": "sigr",
118+
"sqlManagedInstances": "sqlmi-",
119+
"sqlServers": "sql-",
120+
"sqlServersDataWarehouse": "sqldw-",
121+
"sqlServersDatabases": "sqldb-",
122+
"sqlServersDatabasesStretch": "sqlstrdb-",
123+
"storageStorageAccounts": "st",
124+
"storageStorageAccountsVm": "stvm",
125+
"storSimpleManagers": "ssimp",
126+
"streamAnalyticsCluster": "asa-",
127+
"synapseWorkspaces": "syn",
128+
"synapseWorkspacesAnalyticsWorkspaces": "synw",
129+
"synapseWorkspacesSqlPoolsDedicated": "syndp",
130+
"synapseWorkspacesSqlPoolsSpark": "synsp",
131+
"timeSeriesInsightsEnvironments": "tsi-",
132+
"webServerFarms": "plan-",
133+
"webSitesAppService": "app-",
134+
"webSitesAppServiceEnvironment": "ase-",
135+
"webSitesFunctions": "func-",
136+
"webStaticSites": "stapp-"
137+
}

infra/main.bicep

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
targetScope = 'subscription'
2+
3+
@minLength(1)
4+
@maxLength(64)
5+
@description('Name of the environment that can be used as part of naming resource convention')
6+
param environmentName string
7+
8+
@minLength(1)
9+
@description('Primary location for all resources')
10+
param location string
11+
12+
13+
param clientExists bool
14+
param serverExists bool
15+
16+
@description('Id of the user or app to assign application roles')
17+
param principalId string
18+
19+
@description('Principal type of user or app')
20+
param principalType string
21+
22+
// Tags that should be applied to all resources.
23+
//
24+
// Note that 'azd-service-name' tags should be applied separately to service host resources.
25+
// Example usage:
26+
// tags: union(tags, { 'azd-service-name': <service name in azure.yaml> })
27+
var tags = {
28+
'azd-env-name': environmentName
29+
}
30+
31+
// Organize resources in a resource group
32+
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
33+
name: 'rg-${environmentName}'
34+
location: location
35+
tags: tags
36+
}
37+
38+
module resources 'resources.bicep' = {
39+
scope: rg
40+
name: 'resources'
41+
params: {
42+
location: location
43+
tags: tags
44+
principalId: principalId
45+
principalType: principalType
46+
clientExists: clientExists
47+
serverExists: serverExists
48+
}
49+
}
50+
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = resources.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT
51+
output AZURE_RESOURCE_CLIENT_ID string = resources.outputs.AZURE_RESOURCE_CLIENT_ID
52+
output AZURE_RESOURCE_SERVER_ID string = resources.outputs.AZURE_RESOURCE_SERVER_ID

infra/main.parameters.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"environmentName": {
6+
"value": "${AZURE_ENV_NAME}"
7+
},
8+
"location": {
9+
"value": "${AZURE_LOCATION}"
10+
},
11+
"clientExists": {
12+
"value": "${SERVICE_CLIENT_RESOURCE_EXISTS=false}"
13+
},
14+
"serverExists": {
15+
"value": "${SERVICE_SERVER_RESOURCE_EXISTS=false}"
16+
},
17+
"principalId": {
18+
"value": "${AZURE_PRINCIPAL_ID}"
19+
},
20+
"principalType": {
21+
"value": "${AZURE_PRINCIPAL_TYPE}"
22+
}
23+
}
24+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
param exists bool
2+
param name string
3+
4+
resource existingApp 'Microsoft.App/containerApps@2023-05-02-preview' existing = if (exists) {
5+
name: name
6+
}
7+
8+
output containers array = exists ? existingApp!.properties.template.containers : []

0 commit comments

Comments
 (0)