- name
- aigbb-azd-compliance
- description
- Validate and deploy Azure Developer CLI (azd) projects with compliance-first patterns. Covers azure.yaml configuration, Bicep parameter sync, Container Apps image preservation (IMAGE_NAME/RESOURCE_EXISTS), service-to-resource binding, azd tags, hook scripts, shared subscription safety, and pre-flight validation. Use when setting up azd projects, writing azure.yaml, validating Bicep infrastructure for Container Apps, troubleshooting azd up failures, running compliance checks, or preparing for deployment. Triggers on azd, azure.yaml, azd compliance, azd deploy, azd provision, azd up, Container Apps deployment, IMAGE_NAME, RESOURCE_EXISTS, remoteBuild, azd tags, parameter mismatch, pre-flight check, deployment validation.
# Azure Developer CLI (azd) Compliance & Deployment
Deploy containerized applications to Azure Container Apps with full compliance validation. This skill combines deployment patterns with the checks needed to prevent `azd provision` and `azd deploy` failures.
**Philosophy**: Only flag issues that will cause failures or runtime problems. Skip style preferences.
---
## Quick Start
```bash
azd auth login # Authenticate
azd init # Creates azure.yaml and .azure/ folder
azd env new <env-name> # Create environment (dev, staging, prod)
azd up # Provision infra + build + deploy
```
---
## 1. azure.yaml Configuration
### Minimal Configuration
```yaml
name: my-app
services:
api:
project: ./src/api
language: python
host: containerapp
docker:
path: ./Dockerfile
remoteBuild: true
```
### Full Configuration with Hooks
```yaml
name: my-app
metadata:
template: my-project@1.0.0
infra:
provider: bicep
path: ./infra
services:
web:
project: ./src/web
language: ts
host: containerapp
docker:
path: ./Dockerfile
context: .
remoteBuild: true
api:
project: ./src/api
language: python
host: containerapp
docker:
path: ./Dockerfile
context: .
remoteBuild: true
hooks:
preprovision:
shell: sh
run: python infra/scripts/preprovision.py
postprovision:
shell: sh
run: python infra/scripts/postprovision.py
predeploy:
shell: sh
run: python infra/scripts/predeploy.py
postdeploy:
shell: sh
run: python infra/scripts/postdeploy.py
```
### Critical azure.yaml Rules
| Check | Why It Matters |
|-------|----------------|
| `name` field exists | azd refuses to run without it |
| `infra.path` points to existing directory | `azd provision` fails immediately |
| Service `project` paths exist | `azd deploy` can't find source code |
| Service `host` matches infrastructure | Deploys to wrong/nonexistent resource |
| `remoteBuild: true` on all containerapp services | Local builds fail on ARM Macs, in CI/CD, and require Docker Desktop |
### Service Hosts
| azure.yaml `host` | Required Bicep Resource |
|-------------------|------------------------|
| `containerapp` | Container App with matching name pattern |
| `function` | Function App |
| `appservice` | App Service |
| `staticwebapp` | Static Web App |
### Service Languages
| Language | Value | Package Manager |
|----------|-------|-----------------|
| Python | `python` | requirements.txt or pyproject.toml |
| TypeScript | `ts` | package.json |
| JavaScript | `js` | package.json |
| C# | `csharp` | .csproj |
| Java | `java` | pom.xml or build.gradle |
| Go | `go` | go.mod |
---
## 2. Parameter Sync (Most Common Failure)
Every parameter in `main.bicep` that lacks a default **MUST** have a mapping in `main.parameters.json`.
### main.bicep Parameters
```bicep
// These MUST be in main.parameters.json (no default)
param environmentName string
param location string
// These are OPTIONAL in main.parameters.json (have defaults)
param principalId string = ''
param apiImageName string = ''
param apiExists bool = false
```
### main.parameters.json
```json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": { "value": "${AZURE_ENV_NAME}" },
"location": { "value": "${AZURE_LOCATION}" },
"principalId": { "value": "${AZURE_PRINCIPAL_ID}" },
"apiImageName": { "value": "${SERVICE_API_IMAGE_NAME=}" },
"apiExists": { "value": "${SERVICE_API_RESOURCE_EXISTS=false}" }
}
}
```
### Parameter Injection Syntax
| Syntax | Meaning |
|--------|---------|
| `${AZURE_ENV_NAME}` | Required — azd auto-populates |
| `${AZURE_LOCATION}` | Required — azd auto-populates |
| `${MY_VAR}` | Must be set via `azd env set MY_VAR "value"` |
| `${MY_VAR=default}` | Uses `default` if not set |
| `${SERVICE_API_IMAGE_NAME=}` | Empty string default (for first deploy) |
| `${SERVICE_API_RESOURCE_EXISTS=false}` | Boolean default (for first deploy) |
### Validation
```powershell
# List parameters without defaults in main.bicep
Select-String -Path infra/main.bicep -Pattern "^param\s+\w+\s+\w+$" |
Where-Object { $_ -notmatch "=" }
# List parameters in main.parameters.json
(Get-Content infra/main.parameters.json | ConvertFrom-Json).parameters.PSObject.Properties.Name
# Compare — every param without a default must appear in parameters.json
```
---
## 3. Container Image Preservation (IMAGE_NAME / RESOURCE_EXISTS)
For each service with `host: containerapp` in azure.yaml, azd manages two variables to prevent re-provision from overwriting the deployed container image.
**Reference**: [Deploy to Azure Container Apps using azd](https://learn.microsoft.com/azure/developer/azure-developer-cli/container-apps-workflows)
### Required Variables Per Service
For a service named `api` in azure.yaml:
| Variable | Set By | Purpose |
|----------|--------|---------|
| `SERVICE_API_IMAGE_NAME` | `azd deploy` | Currently deployed image tag |
| `SERVICE_API_RESOURCE_EXISTS` | `azd provision` | Whether Container App already exists |
**Pattern**: `SERVICE_<UPPER_CASE_SERVICE_NAME>_IMAGE_NAME` and `SERVICE_<UPPER_CASE_SERVICE_NAME>_RESOURCE_EXISTS`
### Check 1: main.parameters.json Must Map Both
```json
{
"parameters": {
"apiImageName": {
"value": "${SERVICE_API_IMAGE_NAME=}"
},
"apiExists": {
"value": "${SERVICE_API_RESOURCE_EXISTS=false}"
}
}
}
```
The `=` after the variable name provides a default (empty string or `false`) for first-time deployments.
### Check 2: Bicep Must Declare and Use Both
```bicep
@description('Container image name for the api service')
param apiImageName string = ''
@description('Whether the api Container App already exists')
param apiExists bool = false
module api 'br/public:avm/res/app/container-app:0.18.1' = {
name: 'api'
params: {
name: '${abbrs.appContainerApps}api-${resourceToken}'
tags: union(tags, { 'azd-service-name': 'api' })
environmentResourceId: containerAppsEnvironment.outputs.resourceId
containers: [
{
name: 'main'
image: !empty(apiImageName) ? apiImageName : 'mcr.microsoft.com/k8se/quickstart:latest'
resources: { cpu: json('0.5'), memory: '1Gi' }
}
]
ingressExternal: true
ingressTargetPort: 8000
}
}
```
### Check 3: Container App Module with Image Guard (Recommended)
Use the AVM Container App module directly with the image guard pattern:
```bicep
module api 'br/public:avm/res/app/container-app:0.18.1' = {
params: {
name: '${abbrs.appContainerApps}api-${resourceToken}'
tags: union(tags, { 'azd-service-name': 'api' })
environmentResourceId: containerAppsEnvironment.outputs.resourceId
containers: [
{
name: 'main'
image: !empty(apiImageName) ? apiImageName : 'mcr.microsoft.com/k8se/quickstart:latest'
resources: { cpu: json('0.5'), memory: '1Gi' }
env: [
{ name: 'AZURE_CLIENT_ID', value: managedIdentity.outputs.clientId }
]
}
]
ingressExternal: true
ingressTargetPort: 8000
registries: [
{
server: '${containerRegistry.outputs.name}.azurecr.io'
identity: managedIdentity.outputs.resourceId
}
]
}
}
```
### What Breaks Without This
| Scenario | Without IMAGE_NAME / RESOURCE_EXISTS | With both variables |
|----------|--------------------------------------|---------------------|
| First `azd provision` | ✅ Works (uses default placeholder) | ✅ Works |
| `azd deploy` | ✅ Pushes new image | ✅ Pushes new image |
| Re-run `azd provision` | ❌ **Overwrites deployed image** with placeholder, app breaks | ✅ Preserves current image |
| `azd up` (provision + deploy) | ⚠️ Temporary downtime between provision and deploy | ✅ Image preserved during provision phase |
### Validation Command
```powershell
# Verify both variables exist for each containerapp service
Select-String -Path infra/main.parameters.json -Pattern 'SERVICE_.*_IMAGE_NAME|SERVICE_.*_RESOURCE_EXISTS'
```
---
## 4. Service-to-Resource Binding & Tags
azd uses tags to discover deployed resources. Without them, `azd deploy` and `azd down` can't find your resources.
### Required: Resource Group Tag
```bicep
var tags = {
'azd-env-name': environmentName // REQUIRED — azd uses this to find resources
}
```
### Required: Service Resource Tags
For each service in azure.yaml, the corresponding Bicep resource **must** include `azd-service-name`:
```bicep
tags: union(tags, {
'azd-service-name': 'api' // MUST match service key in azure.yaml
})
```
### Tag-to-Service Mapping
| azure.yaml | Bicep tag required |
|------------|-------------------|
| `services.api:` | `'azd-service-name': 'api'` |
| `services.web:` | `'azd-service-name': 'web'` |
| `services.frontend:` | `'azd-service-name': 'frontend'` |
| `services.backend:` | `'azd-service-name': 'backend'` |
**Why it matters**: Without these tags, azd can't find deployed resources for `azd deploy` or `azd down`.
---
## 5. Bicep Output Naming Convention
azd needs outputs to know where services deployed and to auto-populate `.azure/<env>/.env`.
### Per-Service Outputs (Required)
```bicep
// Pattern: SERVICE_<SERVICE_NAME>_<PROPERTY>
output SERVICE_API_ENDPOINT_URL string = api.outputs.fqdn
output SERVICE_API_NAME string = api.outputs.name
output SERVICE_WEB_ENDPOINT_URL string = web.outputs.fqdn
output SERVICE_WEB_NAME string = web.outputs.name
```
### Infrastructure Outputs (Common)
```bicep
output AZURE_LOCATION string = location
output AZURE_RESOURCE_GROUP string = resourceGroup().name
output AZURE_CLIENT_ID string = userAssignedIdentity.outputs.clientId
output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint
output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.outputs.loginServer
```
All Bicep outputs automatically become azd environment variables in `.azure/<env>/.env`.
---
## 6. Hook Scripts
### Available Hook Points
| Hook | Timing | Use Case |
|------|--------|----------|
| `prerestore` | Before package restore | Pre-install setup |
| `postrestore` | After package restore | Post-install setup |
GitHub에서 보기