Azure でサーバー管理を行っていて開発用に同じ環境を何度も作るのが嫌になる。作った環境の設定ミスに気づくのに時間がかかった。という経験はございませんか?
そんな私の様なあなたに Bicep をご紹介します。
Bicep とは
Bicep は Azure リソースの作成に特化した固有言語です。
Bicep と Terraform
Bicep と同様に Azure リソースの作成ができる Terraform という言語があり、こちらは Azure と AWS の両方に対応しています。
実は Terraform にも手を出してみたことがあるのですが、あまり時間もなかったのでよくわかっていないです。
その状態での私の感想は以下のとおりです。
Bicep の利点
- Azure リソースに特化しているため、新しいサービスや機能への対応が速い(らしい)。
- プロパティ名が ARM テンプレートの値と同じだったりするため、行き詰まった時はポータルで作った環境の ARM テンプレートを参考にできたりして便利。
Terraform の利点
- AWS でも使えるので1つ習得しておくだけで 2 つの環境で使える。
本当はもっとたくさんの利点があると思うのですが、ちょっと使ってみたレベルの感想としてはこんな感じです。
Bicep を使う準備
Bicep を動かすために事前にインストールが必要です。
- Azure CLI をインストールする。
- VS Code をインストールする。
- VS Code と PowerShell の拡張機能をインストールする - [Microsoft Learn]
- VS Code の拡張機能に
PowerShell
をインストールする - VS Code の拡張機能に
Bicep
をインストールする
Bicep を作る
今回は ↑ の画像の様な環境を作成しました。
Bicep ファイル(main.bicep
)は以下の通り。
// --------------------------- 外から指定してもらう引数 --------------------------- @minLength(1) @description('データベースへの接続文字列') @secure() param sqlConnectionString string @minLength(1) param otherApiUri string @minLength(1) @secure() param apimSubscriptionKey string @minLength(1) @maxLength(15) @description('特定環境用に各リソース名の末尾に付ける名前です。例:"app-{optionName}}"') param optionName string param location string = resourceGroup().location param planName string = 'my-${optionName}' param planSku string = 'P1v2' param storageAccountName string = 'hoge' param storageAccountRGName string = 'hoge' param siteName string = 'my-api-${optionName}' param functionName string = 'my-func-${optionName}' @maxLength(32) param functionsWebHostId string = 'func${replace(toLower(optionName), '-', '')}' @description('Storage アカウントを取得') resource storageaccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = { name: storageAccountName scope: resourceGroup(storageAccountRGName) } @description('Application Insights を作成(App Service用)') resource siteLogAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: siteName location: location } resource siteApplicationInsights 'Microsoft.Insights/components@2020-02-02' = { name: siteName location: location kind: 'web' properties: { Application_Type: 'web' Request_Source: 'rest' WorkspaceResourceId: siteLogAnalytics.id } } @description('Application Insights を作成(Functions用)') resource functionLogAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: functionName location: location } resource functionApplicationInsights 'Microsoft.Insights/components@2020-02-02' = { name: functionName location: location kind: 'web' properties: { Application_Type: 'web' Request_Source: 'rest' WorkspaceResourceId: siteLogAnalytics.id } } @description('App Service プランを作成') resource appServicePlan 'Microsoft.Web/serverfarms@2020-12-01' = { name: planName location: location sku: { name: planSku capacity: 1 } kind: 'linux' properties: { reserved: true } } var storageConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${storageaccount.listKeys().keys[0].value}' @description('App Service を作成') resource site 'Microsoft.Web/sites@2023-12-01' = { name: siteName location: location kind: 'app,linux' properties: { serverFarmId: appServicePlan.id siteConfig: { numberOfWorkers: 1 linuxFxVersion: 'DOTNETCORE|8.0' acrUseManagedIdentityCreds: false alwaysOn: true publishingUsername: 'REDACTED' appSettings: [ { name:'APPINSIGHTS_INSTRUMENTATIONKEY' value: siteApplicationInsights.properties.InstrumentationKey } { name:'OtherApiUri' value: otherApiUri } { name:'ApimSubscriptionKey' value: apimSubscriptionKey } ] connectionStrings: [ { name: 'Storage' connectionString: storageConnectionString type: 'Custom' } { name: 'Sql' connectionString: sqlConnectionString type: 'SQLAzure' } ] } httpsOnly: true scmSiteAlsoStopped: false clientAffinityEnabled: false } } @description('Functions のアプリ設定の一部をスロット固定にする') resource siteConfig 'Microsoft.Web/sites/config@2021-03-01' = { name: 'slotConfigNames' parent: site properties: { appSettingNames: [ 'APPINSIGHTS_INSTRUMENTATIONKEY' ] } } @description('Functions を作成') resource functions 'Microsoft.Web/sites@2023-12-01' = { name: functionName location: location kind: 'functionapp,linux' properties: { enabled: true reserved: true serverFarmId: appServicePlan.id siteConfig: { linuxFxVersion : 'DOTNET-ISOLATED|8.0' alwaysOn: true appSettings: [ { name: 'AzureWebJobsDashboard' value: storageConnectionString } { name: 'AzureWebJobsStorage' value: storageConnectionString } { name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' value: storageConnectionString } { name: 'FUNCTIONS_EXTENSION_VERSION' value: '~4' } { name: 'FUNCTIONS_WORKER_RUNTIME' value: 'dotnet-isolated' } { name: 'WEBSITE_CONTENTSHARE' value: toLower(functionName) } { name:'APPINSIGHTS_INSTRUMENTATIONKEY' value: functionApplicationInsights.properties.InstrumentationKey } { name: 'AzureFunctionsWebHost__hostid' value: functionsWebHostId } { name:'OtherApiUri' value: otherApiUri } { name:'ApimSubscriptionKey' value: apimSubscriptionKey } ] connectionStrings: [ { name: 'Storage' connectionString: storageConnectionString type: 'Custom' } { name: 'Sql' connectionString: sqlConnectionString type: 'SQLAzure' } ] } httpsOnly: true } } @description('Functions のアプリ設定の一部をスロット固定にする') resource functionConig 'Microsoft.Web/sites/config@2021-03-01' = { name: 'slotConfigNames' parent: functions properties: { appSettingNames: [ 'APPINSIGHTS_INSTRUMENTATIONKEY' 'AzureFunctionsWebHost__hostid' ] } }
Bicep を呼ぶための PowerShell ファイル(run.ps1
)は以下の通り。
### ------------------------------ 作成したい環境によって変えてください。 ------------------------------ # ストレージのリソースグループ $storageAccountResourceGroupName = '' # 15文字以内 $optionName = '' # 外部 API URI $otherApiUri = '' # API Management サブスクリプションキー(File API) $apimSubscriptionKey = '' # SQL データベースの接続文字列 $sqlConnectionString = '' ### -------------------------------------------------------------------------------------------------- # シェルのパスを実行中の ps1 ファイルの位置に移動する $path = Split-Path -Parent $MyInvocation.MyCommand.Path Set-Location $path $resourceGroupName = 'my-' + $optionName # Azure へのログイン az login --tenant '《テナントID》' # アクティブなサブスクリプションを設定 az account set --subscription '《サブスクリプション名》' # ストレージのリソースグループがいるか確認 if(-not (az group exists --name $storageAccountResourceGroupName)) { exist; } # デプロイ用のリソースグループがいるか確認 if(-not (az group exists --name $resourceGroupName)) { # いなかったら作成 az group create --name $resourceGroupName --location 'JapanWest' } # Bicep を使用してリソースを作成 az deployment group create --resource-group $resourceGroupName --template-file '.\main.bicep' --parameters ` optionName=$optionName ` otherApiUri=$otherApiUri ` apimSubscriptionKey=$apimSubscriptionKey ` sqlConnectionString=$sqlConnectionString
run.ps1
と main.bicep
を同じフォルダーに配置し、 run.ps1
を実行するとこでリソースが作成されます。
反省点
SQL 接続
本来は実行時に接続文字列パラメーターとして渡すのではなく、Key Vault に設定した接続文字列をマネージドIDで取得できるようにする予定でした。 しかし Bicep で App Service にユーザー割り当ての ID を設定することができず、断念しました。
API Management のサブスクリプションキー
これは単純に存在を忘れていて、時間がなかったので後付けでパラメーターを渡すようにしました。
モジュール化
1つのファイルにすべてのリソースを書いている状態ですが、リソースごとにモジュール化したいと考えています。こちらは今後対応していきたいと思っています。
まとめ
今回は今まで Azure リソースの作成をポータルで行っていたのを Bicep 管理に移行する第1歩としてやってみました。
まだまだ改善点はある状態ですが、後は部内で広げてブラッシュアップしていけたらと思っています。
Azure リソースを管理しているみなさんも是非挑戦してみてください。
おわりに
KENTEMでは、様々な拠点でエンジニアを大募集しています!
建設×ITにご興味頂いた方は、是非下記のリンクからご応募ください。
recruit.kentem.jp
career.kentem.jp