HardПрактика3 min

Bicep-шаблоны

Bicep и ARM-шаблоны для всей инфраструктуры CRC на Azure

Что такое Bicep

Bicep -- декларативный язык для описания Azure-ресурсов. Это упрощённая альтернатива ARM JSON-шаблонам, компилируемая в ARM при деплое.

Bicep (DSL) → компиляция → ARM JSON → Azure Resource Manager → Ресурсы

Bicep vs ARM JSON

// Bicep: 5 строк
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'ivanpetrovresume'
  location: resourceGroup().location
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
}
// ARM JSON: 15+ строк для того же ресурса
{
  "type": "Microsoft.Storage/storageAccounts",
  "apiVersion": "2023-01-01",
  "name": "ivanpetrovresume",
  "location": "[resourceGroup().location]",
  "sku": { "name": "Standard_LRS" },
  "kind": "StorageV2"
}

Полный Bicep-шаблон CRC

// main.bicep
@description('Location for all resources')
param location string = resourceGroup().location

@description('Custom domain name')
param domainName string = 'resume.ivanpetrov.com'

// ========== STORAGE (Frontend) ==========

resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'ivanpetrovresume'
  location: location
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
  properties: {
    minimumTlsVersion: 'TLS1_2'
    supportsHttpsTrafficOnly: true
  }
}

// ========== COSMOS DB (Backend Database) ==========

resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
  name: 'crc-cosmos-db'
  location: location
  properties: {
    databaseAccountOfferType: 'Standard'
    capabilities: [{ name: 'EnableServerless' }]
    locations: [{ locationName: location, failoverPriority: 0 }]
    consistencyPolicy: { defaultConsistencyLevel: 'Session' }
  }
}

resource cosmosDatabase 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2023-04-15' = {
  parent: cosmosAccount
  name: 'crc-database'
  properties: {
    resource: { id: 'crc-database' }
  }
}

resource cosmosContainer 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers@2023-04-15' = {
  parent: cosmosDatabase
  name: 'counters'
  properties: {
    resource: {
      id: 'counters'
      partitionKey: { paths: ['/id'], kind: 'Hash' }
    }
  }
}

// ========== FUNCTION APP (Backend) ==========

resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
  name: 'crc-plan'
  location: location
  sku: { name: 'Y1', tier: 'Dynamic' }
  kind: 'functionapp'
  properties: { reserved: true }
}

resource funcStorageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: 'crcfuncstorage'
  location: location
  sku: { name: 'Standard_LRS' }
  kind: 'StorageV2'
}

resource functionApp 'Microsoft.Web/sites@2023-01-01' = {
  name: 'crc-visitor-counter'
  location: location
  kind: 'functionapp,linux'
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      linuxFxVersion: 'Python|3.11'
      appSettings: [
        { name: 'AzureWebJobsStorage', value: 'DefaultEndpointsProtocol=https;AccountName=${funcStorageAccount.name};AccountKey=${funcStorageAccount.listKeys().keys[0].value}' }
        { name: 'FUNCTIONS_WORKER_RUNTIME', value: 'python' }
        { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4' }
        { name: 'COSMOS_CONNECTION_STRING', value: cosmosAccount.listConnectionStrings().connectionStrings[0].connectionString }
      ]
      cors: {
        allowedOrigins: ['https://${domainName}']
      }
    }
  }
}

// ========== CDN ==========

resource cdnProfile 'Microsoft.Cdn/profiles@2023-05-01' = {
  name: 'crc-cdn-profile'
  location: 'global'
  sku: { name: 'Standard_Microsoft' }
}

resource cdnEndpoint 'Microsoft.Cdn/profiles/endpoints@2023-05-01' = {
  parent: cdnProfile
  name: 'ivanpetrov-resume'
  location: 'global'
  properties: {
    originHostHeader: '${storageAccount.name}.z6.web.core.windows.net'
    origins: [{
      name: 'blob-origin'
      properties: {
        hostName: '${storageAccount.name}.z6.web.core.windows.net'
      }
    }]
    isCompressionEnabled: true
    contentTypesToCompress: ['text/html', 'text/css', 'application/javascript']
  }
}

// ========== OUTPUTS ==========

output functionAppUrl string = 'https://${functionApp.properties.defaultHostName}/api/count'
output cdnEndpointUrl string = 'https://${cdnEndpoint.name}.azureedge.net'
output storageWebEndpoint string = storageAccount.properties.primaryEndpoints.web

Деплой

# Валидация
az deployment group validate \
  --resource-group crc-resources \
  --template-file main.bicep

# Preview (what-if)
az deployment group what-if \
  --resource-group crc-resources \
  --template-file main.bicep

# Деплой
az deployment group create \
  --resource-group crc-resources \
  --template-file main.bicep \
  --parameters domainName=resume.ivanpetrov.com

# Просмотр outputs
az deployment group show \
  --resource-group crc-resources \
  --name main \
  --query 'properties.outputs'

Модули Bicep

Для организации кода используйте модули:

// modules/cosmos.bicep
param location string
param accountName string

resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2023-04-15' = {
  name: accountName
  location: location
  // ...
}

output connectionString string = cosmos.listConnectionStrings().connectionStrings[0].connectionString
// main.bicep
module cosmos 'modules/cosmos.bicep' = {
  name: 'cosmos-deployment'
  params: {
    location: location
    accountName: 'crc-cosmos-db'
  }
}

Проверь себя

Как Bicep передаёт Cosmos DB connection string в Function App безопасно?

Что делает команда 'az deployment group what-if'?

Почему property 'reserved: true' нужна для App Service Plan на Linux?

Зачем использовать модули Bicep?

Чем Bicep отличается от ARM JSON-шаблонов?

Связанные темы