使用 AZD 配置 Azure 资源 章节导航: 课程主页:AZD 初学者指南 当前章节:第 4 章 - 基础设施即代码与部署 ⬅️ 上一章:部署指南 ➡️ 下一章:第 5 章:多代理 AI 解决方案 相关内容:第 6 章:部署前验证 简介 本指南全面介绍如何使用 Azure Developer CLI 配置和管理 Azure 资源。从基础资源创建到使用 Bicep、ARM 模板、Terraform 和 Pulumi 实现企业级基础设施架构的高级模式,学习如何实施基础设施即代码(IaC)模式。
章节导航:
本指南全面介绍如何使用 Azure Developer CLI 配置和管理 Azure 资源。从基础资源创建到使用 Bicep、ARM 模板、Terraform 和 Pulumi 实现企业级基础设施架构的高级模式,学习如何实施基础设施即代码(IaC)模式。
完成本指南后,您将能够:
完成后,您将能够:
Azure Developer CLI 支持多种基础设施即代码(IaC)提供商:
Azure Account └── Subscriptions └── Resource Groups └── Resources (App Service, Storage, Database, etc.)
// infra/main.bicep @description('The name of the environment') param environmentName string @description('The location for all resources') param location string = resourceGroup().location @description('The name of the application') param applicationName string = 'myapp' // Variables var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) var tags = { 'azd-env-name': environmentName 'azd-app': applicationName } // Resource Group (created automatically by azd) resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' existing = { name: '${applicationName}-${environmentName}-rg' } // App Service Plan resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { name: '${applicationName}-${environmentName}-plan' location: location tags: tags sku: { name: 'B1' capacity: 1 } properties: { reserved: true // Linux App Service Plan } } // Web App resource webApp 'Microsoft.Web/sites@2022-03-01' = { name: '${applicationName}-web-${resourceToken}' location: location tags: tags properties: { serverFarmId: appServicePlan.id siteConfig: { linuxFxVersion: 'NODE|18-lts' alwaysOn: true ftpsState: 'Disabled' minTlsVersion: '1.2' appSettings: [ { name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE' value: 'false' } { name: 'NODE_ENV' value: 'production' } ] } httpsOnly: true } } // Output values for azd output WEB_URL string = 'https://${webApp.properties.defaultHostName}' output WEB_NAME string = webApp.name
// infra/modules/app-service.bicep @description('App Service configuration') param name string param location string param planId string param appSettings array = [] resource webApp 'Microsoft.Web/sites@2022-03-01' = { name: name location: location properties: { serverFarmId: planId siteConfig: { appSettings: appSettings linuxFxVersion: 'NODE|18-lts' alwaysOn: true } httpsOnly: true } } output hostname string = webApp.properties.defaultHostName output principalId string = webApp.identity.principalId
// infra/main.bicep - Using modules module webAppModule 'modules/app-service.bicep' = { name: 'webApp' params: { name: '${applicationName}-web-${resourceToken}' location: location planId: appServicePlan.id appSettings: [ { name: 'API_URL' value: apiModule.outputs.endpoint } { name: 'DATABASE_URL' value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-url)' } ] } }
@description('Whether to create a database') param createDatabase bool = true @description('Database SKU') param databaseSku string = 'Basic' resource database 'Microsoft.Sql/servers/databases@2021-11-01' = if (createDatabase) { name: '${sqlServer.name}/${applicationName}-db' location: location sku: { name: databaseSku tier: databaseSku == 'Basic' ? 'Basic' : 'Standard' } properties: { collation: 'SQL_Latin1_General_CP1_CI_AS' } }
resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = { name: '${applicationName}-cosmos-${resourceToken}' location: location tags: tags kind: 'GlobalDocumentDB' properties: { databaseAccountOfferType: 'Standard' locations: [ { locationName: location failoverPriority: 0 isZoneRedundant: false } ] capabilities: [ { name: 'EnableServerless' } ] } } resource cosmosDatabase 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2023-04-15' = { parent: cosmosAccount name: '${applicationName}db' properties: { resource: { id: '${applicationName}db' } } } resource todoContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-04-15' = { parent: cosmosDatabase name: 'todos' properties: { resource: { id: 'todos' partitionKey: { paths: ['/userId'] kind: 'Hash' } } } }
resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { name: '${applicationName}-postgres-${resourceToken}' location: location tags: tags sku: { name: 'Standard_B1ms' tier: 'Burstable' } properties: { version: '14' administratorLogin: 'dbadmin' administratorLoginPassword: databasePassword storage: { storageSizeGB: 32 } backup: { backupRetentionDays: 7 geoRedundantBackup: 'Disabled' } highAvailability: { mode: 'Disabled' } } } resource postgresDatabase 'Microsoft.DBforPostgreSQL/flexibleServers/databases@2022-12-01' = { parent: postgresServer name: '${applicationName}db' properties: { charset: 'utf8' collation: 'en_US.utf8' } } // Allow Azure services to connect resource firewallRule 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2022-12-01' = { parent: postgresServer name: 'AllowAzureServices' properties: { startIpAddress: '0.0.0.0' endIpAddress: '0.0.0.0' } }
resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = { name: '${applicationName}-kv-${resourceToken}' location: location tags: tags properties: { sku: { family: 'A' name: 'standard' } tenantId: subscription().tenantId enableRbacAuthorization: true enableSoftDelete: true softDeleteRetentionInDays: 7 } } // Grant Key Vault access to the web app resource webAppKeyVaultAccess 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(keyVault.id, webApp.id, 'Key Vault Secrets User') scope: keyVault properties: { roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6' // Key Vault Secrets User ) principalId: webApp.identity.principalId principalType: 'ServicePrincipal' } } // Store database connection string in Key Vault resource databaseConnectionSecret 'Microsoft.KeyVault/vaults/secrets@2023-02-01' = { parent: keyVault name: 'database-connection-string' properties: { value: 'Server=${postgresServer.properties.fullyQualifiedDomainName};Database=${postgresDatabase.name};Port=5432;User Id=${postgresServer.properties.administratorLogin};Password=${databasePassword};' } }
resource webApp 'Microsoft.Web/sites@2022-03-01' = { name: '${applicationName}-web-${resourceToken}' location: location identity: { type: 'SystemAssigned' } properties: { serverFarmId: appServicePlan.id siteConfig: { appSettings: [ { name: 'DATABASE_CONNECTION_STRING' value: '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-connection-string)' } { name: 'AZURE_CLIENT_ID' value: webApp.identity.principalId } ] } } }
resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' = { name: '${applicationName}-vnet-${resourceToken}' location: location tags: tags properties: { addressSpace: { addressPrefixes: ['10.0.0.0/16'] } subnets: [ { name: 'app-subnet' properties: { addressPrefix: '10.0.1.0/24' serviceEndpoints: [ { service: 'Microsoft.Storage' } { service: 'Microsoft.KeyVault' } ] } } { name: 'db-subnet' properties: { addressPrefix: '10.0.2.0/24' delegations: [ { name: 'postgres-delegation' properties: { serviceName: 'Microsoft.DBforPostgreSQL/flexibleServers' } } ] } } ] } } // Private DNS Zone for PostgreSQL resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { name: '${applicationName}.postgres.database.azure.com' location: 'global' tags: tags } resource privateDnsZoneLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { parent: privateDnsZone name: '${applicationName}-dns-link' location: 'global' properties: { registrationEnabled: false virtualNetwork: { id: vnet.id } } }
resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: '${applicationName}-agw-pip-${resourceToken}' location: location tags: tags sku: { name: 'Standard' tier: 'Regional' } properties: { publicIPAllocationMethod: 'Static' } } resource applicationGateway 'Microsoft.Network/applicationGateways@2023-04-01' = { name: '${applicationName}-agw-${resourceToken}' location: location tags: tags properties: { sku: { name: 'Standard_v2' tier: 'Standard_v2' capacity: 1 } gatewayIPConfigurations: [ { name: 'appGatewayIpConfig' properties: { subnet: { id: '${vnet.id}/subnets/gateway-subnet' } } } ] frontendIPConfigurations: [ { name: 'appGatewayFrontendIP' properties: { publicIPAddress: { id: publicIP.id } } } ] frontendPorts: [ { name: 'port80' properties: { port: 80 } } { name: 'port443' properties: { port: 443 } } ] } }
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { name: '${applicationName}-logs-${resourceToken}' location: location tags: tags properties: { sku: { name: 'PerGB2018' } retentionInDays: 30 } } resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { name: '${applicationName}-ai-${resourceToken}' location: location tags: tags kind: 'web' properties: { Application_Type: 'web' WorkspaceResourceId: logAnalytics.id } } // Output connection string for applications output APPLICATION_INSIGHTS_CONNECTION_STRING string = applicationInsights.properties.ConnectionString
resource cpuAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = { name: '${applicationName}-cpu-alert' location: 'global' tags: tags properties: { description: 'Alert when CPU usage is high' severity: 2 enabled: true scopes: [webApp.id] evaluationFrequency: 'PT5M' windowSize: 'PT5M' criteria: { 'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria' allOf: [ { name: 'CPU Usage' metricName: 'CpuPercentage' operator: 'GreaterThan' threshold: 80 timeAggregation: 'Average' } ] } actions: [ { actionGroupId: actionGroup.id } ] } }
// infra/main.parameters.dev.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}" }, "appServiceSku": { "value": "B1" }, "databaseSku": { "value": "Standard_B1ms" }, "enableBackup": { "value": false } } }
// infra/main.parameters.prod.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}" }, "appServiceSku": { "value": "P1v3" }, "databaseSku": { "value": "Standard_D2s_v3" }, "enableBackup": { "value": true }, "replicaCount": { "value": 3 } } }
@description('Environment type (dev, staging, prod)') @allowed(['dev', 'staging', 'prod']) param environmentType string = 'dev' // Development resources resource devStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = if (environmentType == 'dev') { name: '${applicationName}devstorage${resourceToken}' location: location kind: 'StorageV2' sku: { name: 'Standard_LRS' } } // Production resources with geo-redundancy resource prodStorage 'Microsoft.Storage/storageAccounts@2023-01-01' = if (environmentType == 'prod') { name: '${applicationName}prodstorage${resourceToken}' location: location kind: 'StorageV2' sku: { name: 'Standard_GRS' } properties: { accessTier: 'Hot' supportsHttpsTrafficOnly: true minimumTlsVersion: 'TLS1_2' } }
@description('Primary region') param primaryLocation string = 'eastus2' @description('Secondary region') param secondaryLocation string = 'westus2' // Primary region resources module primaryRegion 'modules/region.bicep' = { name: 'primary-region' params: { location: primaryLocation isPrimary: true applicationName: applicationName environmentName: environmentName } } // Secondary region resources module secondaryRegion 'modules/region.bicep' = { name: 'secondary-region' params: { location: secondaryLocation isPrimary: false applicationName: applicationName environmentName: environmentName } } // Traffic Manager for global load balancing resource trafficManager 'Microsoft.Network/trafficmanagerprofiles@2022-04-01' = { name: '${applicationName}-tm-${resourceToken}' location: 'global' properties: { profileStatus: 'Enabled' trafficRoutingMethod: 'Priority' dnsConfig: { relativeName: '${applicationName}-${environmentName}' ttl: 30 } monitorConfig: { protocol: 'HTTPS' port: 443 path: '/health' } endpoints: [ { name: 'primary-endpoint' type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints' properties: { targetResourceId: primaryRegion.outputs.webAppId priority: 1 } } { name: 'secondary-endpoint' type: 'Microsoft.Network/trafficManagerProfiles/azureEndpoints' properties: { targetResourceId: secondaryRegion.outputs.webAppId priority: 2 } } ] } }
// infra/test/main.test.bicep param location string = resourceGroup().location module mainTemplate '../main.bicep' = { name: 'main-template-test' params: { environmentName: 'test' location: location applicationName: 'testapp' } } // Test assertions resource testScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { name: 'test-deployment' location: location kind: 'AzurePowerShell' properties: { azPowerShellVersion: '8.0' scriptContent: ''' $webAppName = "${mainTemplate.outputs.WEB_NAME}" $response = Invoke-WebRequest -Uri "https://${mainTemplate.outputs.WEB_URL}/health" -UseBasicParsing if ($response.StatusCode -ne 200) { throw "Health check failed" } Write-Output "All tests passed!" ''' timeout: 'PT10M' cleanupPreference: 'OnSuccess' retentionInterval: 'P1D' } }
azd provision --preview 功能允许您在实际部署资源之前模拟基础设施配置。它类似于 terraform plan 或 bicep what-if,为您提供 Azure 环境中将进行的更改的预览视图。
# Preview infrastructure changes before deployment azd provision --preview # Preview with detailed output azd provision --preview --output json # Preview for specific environment azd provision --preview --environment production
此命令帮助您:
特别适用于以下场景:
$ azd provision --preview Previewing infrastructure changes... The following resources will be created: + azurerm_resource_group.rg + azurerm_app_service_plan.plan + azurerm_linux_web_app.web + azurerm_cosmosdb_account.cosmos The following resources will be modified: ~ azurerm_key_vault.kv ~ access_policy (forces replacement) The following resources will be destroyed: - azurerm_storage_account.old_storage Estimated monthly cost: $45.67 ⚠️ Warning: 1 resource will be replaced ✅ Preview completed successfully!
# Preview infrastructure changes first (RECOMMENDED) azd provision --preview # Apply changes incrementally after preview azd provision --confirm-with-no-prompt # Rollback if needed azd provision --rollback
resource migrationScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { name: 'database-migration' location: location kind: 'AzureCLI' properties: { azCliVersion: '2.40.0' scriptContent: ''' # Install database migration tools npm install -g db-migrate db-migrate-pg # Run migrations db-migrate up --config database.json --env production echo "Database migration completed successfully" ''' environmentVariables: [ { name: 'DATABASE_URL' secureValue: databaseConnectionString } ] timeout: 'PT30M' cleanupPreference: 'OnSuccess' } }
var naming = { resourceGroup: 'rg-${applicationName}-${environmentName}-${location}' appService: '${applicationName}-web-${resourceToken}' database: '${applicationName}-db-${resourceToken}' storage: '${take(replace(applicationName, '-', ''), 15)}${environmentName}sa${take(resourceToken, 8)}' keyVault: '${take(applicationName, 15)}-kv-${take(resourceToken, 8)}' }
var commonTags = { 'azd-env-name': environmentName 'azd-app': applicationName 'environment': environmentName 'cost-center': 'engineering' 'owner': 'platform-team' 'project': applicationName 'created-date': utcNow('yyyy-MM-dd') }
@description('Environment name') @minLength(3) @maxLength(20) param environmentName string @description('Location for resources') @allowed(['eastus2', 'westus2', 'centralus']) param location string @description('App Service SKU') @allowed(['B1', 'B2', 'S1', 'S2', 'P1v3', 'P2v3']) param appServiceSku string = 'B1'
// Service endpoints output WEB_URL string = 'https://${webApp.properties.defaultHostName}' output API_URL string = 'https://${apiApp.properties.defaultHostName}' // Resource identifiers output WEB_APP_NAME string = webApp.name output API_APP_NAME string = apiApp.name output DATABASE_NAME string = database.name // Connection strings (for secure reference) output DATABASE_CONNECTION_STRING_KEY string = '@Microsoft.KeyVault(VaultName=${keyVault.name};SecretName=database-connection-string)'
导航
免责声明:
本文档使用AI翻译服务Co-op Translator进行翻译。尽管我们努力确保翻译的准确性,但请注意,自动翻译可能包含错误或不准确之处。原始语言的文档应被视为权威来源。对于关键信息,建议使用专业人工翻译。我们不对因使用此翻译而产生的任何误解或误读承担责任。