Đăng vào

Deploy webapp trên Azure bằng ARM Template (Bicep)

Hiện tại mình đang học để thi chứng chỉ AZ-204, trong quá trình học vừa thực hành theo các bài Lab dùng Azure Portal mình cũng tự viết thêm các ARM Template là 1 dạng IaC được sử dụng trên Azure.

Ví dụ này được thực hiện theo Lab 01: Build a web application on Azure platform as a service offering

Tóm tắt

Trong bài lab này nôm na sẽ hướng dẫn chúng ta triển khai một webapp cho phép người dùng xem và upload hình ảnh lên Azure Storage Blob. Ngôn ngữ lập trình sử dụng trong bài Lab này là .NET core.

  • Blob Container để lưu hình ảnh (Azure Storage Account)
  • API xem và upload hình ảnh (Azure App Service)
  • Frontend cho người dùng tương tác (Azure App Service)

Thông thường để dễ dàng quản lý các resource, người ta thường quy định cách đặt tên theo 1 quy tắc nhất quán nào đó. Ví dụ mình xây dựng tên của các resource dựa trên 3 thông số chính projectCode, envappName. Các service, resource của mỗi app được gom vào 1 Resource Group riêng tên là ${projectCode}-${env}-rg-${appName}

Mình sử dụng Location là East US và các lệnh được thực thi trên Azure PowerShell

$projectCode = "az204"
$env = "dev"
$appName = "lab-01"

New-AzDeployment -Location "East US" -env $env -projectCode $projectCode -appName $appName -TemplateFile main.bicep -Verbose

main.bicep

targetScope = 'subscription'

param appName string
param env string
param projectCode string
param region string = 'eastus'

@allowed([
  'Premium_LRS'
  'Premium_ZRS'
  'Standard_GRS'
  'Standard_GZRS'
  'Standard_LRS'
  'Standard_RAGRS'
  'Standard_RAGZRS'
  'Standard_ZRS'
])
param storageAccountType string = 'Standard_LRS'

//Create the resource group.
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
  name: '${projectCode}-${env}-rg-${appName}'
  location: region
}

// Create the storage account.
module storageAccount './storage-account.bicep' = {
  name: 'deploy-${projectCode}-${env}-sta-${appName}'
  params: {
    appName: appName
    env: env
    projectCode: projectCode
    location: rg.location
    storageAccountType: storageAccountType
  }
  scope: resourceGroup(rg.name)
}

// Create App Service Plan.
module appServicePlan './appservice-plan.bicep' = {
  name: 'deploy-${projectCode}-${env}-asp-${appName}'
  params: {
    appName: appName
    env: env
    projectCode: projectCode
    location: rg.location
  }
  scope: resourceGroup(rg.name)
}


// Create API.
module backend './backend-appservice.bicep' = {
  name: 'deploy-${projectCode}-${env}-app-${appName}-api'
  params: {
    appName: appName
    env: env
    projectCode: projectCode
    location: rg.location
    serverFarmId: appServicePlan.outputs.serverFarmId
    storageAccountId: storageAccount.outputs.storageAccountId
    storageAcountApiVersion: storageAccount.outputs.storageAccountApiVersion
  }
  scope: resourceGroup(rg.name)
}


// Create Frontend.
module frontend './frontend-appservice.bicep' = {
  name: 'deploy-${projectCode}-${env}-app-${appName}-web'
  params: {
    appName: appName
    env: env
    projectCode: projectCode
    location: rg.location
    serverFarmId: appServicePlan.outputs.serverFarmId
    apiUrl: backend.outputs.apiUrl
  }
  scope: resourceGroup(rg.name)
}

output apiUrl string = backend.outputs.apiUrl
output frontendUrl string = frontend.outputs.webUrl

storage-account.bicep

targetScope = 'resourceGroup'

param appName string
param env string
param projectCode string
param location string = resourceGroup().location
param storageAccountType string

var storageAccountName = replace('${projectCode}${env}sta${appName}', '-', '')
var containerName = 'images'

resource sa 'Microsoft.Storage/storageAccounts@2021-06-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

resource container 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-06-01' = {
  name: '${sa.name}/default/${containerName}'
  properties: {
    publicAccess: 'Blob'
  }
}

output storageAccountId string = sa.id
output storageAccountApiVersion string = sa.apiVersion

appservice-plan.bicep

targetScope = 'resourceGroup'

param appName string
param env string
param projectCode string
param location string = resourceGroup().location

var appServicePlan = '${projectCode}-${env}-asp-${appName}'

resource asp 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: appServicePlan
  location: location
  sku: {
    name: 'F1'
    tier: 'Free'
  }
}

output serverFarmId string = asp.id

backend-appservice.bicep

targetScope = 'resourceGroup'

param appName string
param env string
param projectCode string
param location string = resourceGroup().location
param serverFarmId string
param storageAccountId string
param storageAcountApiVersion string

var appServiceName = '${projectCode}-${env}-app-${appName}-api'
var storageAccountName = replace('${projectCode}${env}sta${appName}', '-', '')

resource backendApp 'Microsoft.Web/sites@2022-03-01' = {
  name: appServiceName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: serverFarmId
    siteConfig: {
      windowsFxVersion: 'DOTNETCORE|3.1'
      appSettings: [
        {
          name: 'StorageConnectionString'
          value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageAccountId, storageAcountApiVersion).keys[0].value}'
        }
      ]
    }
    httpsOnly: true
  }
}

resource zipSourceApi 'Microsoft.Web/sites/extensions@2022-03-01' = {
  parent: backendApp
  name: 'MSDeploy'
  properties: {
    packageUri: 'https://github.com/MicrosoftLearning/AZ-204-DevelopingSolutionsforMicrosoftAzure/raw/master/Allfiles/Labs/01/Starter/API/api.zip'
  }
}

output apiUrl string = 'https://${backendApp.properties.defaultHostName}'

frontend-appservice.bicep

targetScope = 'resourceGroup'

param appName string
param env string
param projectCode string
param location string = resourceGroup().location
param serverFarmId string
param apiUrl string

var appServiceName = '${projectCode}-${env}-app-${appName}-web'

resource frontendApp 'Microsoft.Web/sites@2022-03-01' = {
  name: appServiceName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    serverFarmId: serverFarmId
    siteConfig: {
      windowsFxVersion: 'DOTNETCORE|3.1'
      appSettings: [
        {
          name: 'ApiUrl'
          value: apiUrl
        }
      ]
    }
    httpsOnly: true
  }
}

resource zipSourceWeb 'Microsoft.Web/sites/extensions@2022-03-01' = {
  parent: frontendApp
  name: 'MSDeploy'
  properties: {
    packageUri: 'https://github.com/MicrosoftLearning/AZ-204-DevelopingSolutionsforMicrosoftAzure/raw/master/Allfiles/Labs/01/Starter/Web/web.zip'
  }
}

output webUrl string = 'https://${frontendApp.properties.defaultHostName}'