Azure 上のインフラを手作業で構築・変更する運用は、環境ごとの差分や設定ミス、担当者の暗黙知への依存といった問題を抱えがちです。Infrastructure as Code(IaC)は、インフラの構成をコードとして定義し、バージョン管理とレビュー、自動デプロイの対象に載せることでこれらの課題に対処します。Azure におけるファーストパーティの IaC 言語が Bicep です。
Bicep は ARM テンプレート(JSON)を抽象化した宣言的ドメイン固有言語で、コンパイル時に ARM テンプレートへ変換されます。JSON に比べて記述量が少なく、型チェックや補完が効くため、可読性と保守性の面で優位です。本稿では Bicep の基本構成要素から、モジュール化、Azure Verified Modules(AVM)の活用、what-if による差分確認、Deployment Stacks によるライフサイクル管理、CI/CD への組み込みまで、2026 年時点での実務的な進め方を解説します。
Bicep の基本構成要素
Bicep のファイルは、主に param、var、resource、module、output の要素で構成されます。それぞれの役割を押さえておくと、テンプレートの読み書きが安定します。
- param — デプロイ時に外部から渡す入力値を宣言します。既定値やデコレーターによる制約を付与できます。
- var — テンプレート内で再利用する計算済みの値を定義します。デプロイ時の入力にはなりません。
- resource — 作成・管理する Azure リソースを宣言します。型は
プロバイダー/リソース種別@API バージョンの形式で指定します。 - module — 別の Bicep ファイルを部品として呼び出します。再利用とスコープ分割の基本単位です。
- output — デプロイ結果として返す値を定義します。他のデプロイやスクリプトから参照できます。
次の例は、パラメータと変数を用いて Storage Account を定義したものです。シークレットに該当しない値でも、環境ごとに変わる項目はパラメータ化しておくと再利用性が高まります。
@description('リソースを配置するリージョン')
param location string = resourceGroup().location
@allowed(['dev', 'stg', 'prod'])
param environment string
var storageName = 'st${environment}${uniqueString(resourceGroup().id)}'
resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: storageName
location: location
sku: {
name: environment == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
}
output storageId string = storage.id
三項演算子で環境ごとに冗長性オプションを切り替え、uniqueString() で名前の一意性を確保しています。minimumTlsVersion や allowBlobPublicAccess のようなセキュリティ既定値も、テンプレート側で明示しておくと構成のばらつきを防げます。

モジュール化と Azure Verified Modules
単一ファイルに全リソースを記述すると、規模が大きくなるにつれて見通しが悪くなります。用途ごとにファイルを分割し、module として呼び出す構成が保守の基本です。ネットワーク、コンピュート、データ、監視といった単位で分けると、変更範囲を局所化できます。
module network './modules/network.bicep' = {
name: 'network-deploy'
params: {
location: location
environment: environment
addressPrefix: '10.0.0.0/16'
}
}
module app './modules/app-service.bicep' = {
name: 'app-deploy'
params: {
location: location
subnetId: network.outputs.subnetId
}
}
モジュール間の依存関係は、あるモジュールの output を別のモジュールの param に渡すことで暗黙的に解決されます。上記では network.outputs.subnetId を参照しているため、network のデプロイが先に完了してから app が実行されます。
自前でモジュールを作り込む前に検討したいのが Azure Verified Modules(AVM)です。AVM は Microsoft が公式に検証・保守するモジュール群で、命名規約やセキュリティ既定値、監視設定といったベストプラクティスがあらかじめ組み込まれています。Bicep レジストリ(br/public)経由で参照できます。
module storage 'br/public:avm/res/storage/storage-account:0.14.0' = {
name: 'storage-avm'
params: {
name: storageName
location: location
skuName: 'Standard_GRS'
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
}
}
AVM を使うと、診断設定やプライベートエンドポイントといった付帯構成を都度書き起こす必要がなくなり、組織内で構成を標準化しやすくなります。バージョンを明示的にピン留めしておくと、意図しない更新の影響を避けられます。組織固有の構成は、AVM をラップした社内モジュールとして private レジストリ(Azure Container Registry)に公開し、チーム間で共有する運用が有効です。
what-if による差分確認
デプロイ前に、テンプレートを適用すると何が変わるのかを把握することは、意図しない削除や置き換えを避けるうえで欠かせません。Azure CLI の what-if 操作は、現在のリソースの状態とテンプレートを比較し、作成・変更・削除の予定を提示します。
# リソースグループスコープで差分を確認
az deployment group what-if \
--resource-group rg-app-prod \
--template-file main.bicep \
--parameters environment=prod
# 問題がなければ本デプロイを実行
az deployment group create \
--resource-group rg-app-prod \
--template-file main.bicep \
--parameters environment=prod
what-if の結果は、記号付きで変更内容を示します。Create、Modify、Delete、NoChange などに分類されるため、レビュー時にリソースの置き換え(Delete と Create の組み合わせ)が発生しないかを重点的に確認します。CI 上で what-if を実行し、その出力をプルリクエストのコメントとして残すと、レビュー担当者が変更の影響を把握しやすくなります。
Deployment Stacks によるライフサイクル管理
従来の az deployment group create は、テンプレートに含まれるリソースを作成・更新しますが、テンプレートから削除したリソースは Azure 上に残り続けます。この「取り残し」を解消するのが Deployment Stacks です。Deployment Stacks は、ひとまとまりのリソース群を単一の管理単位(スタック)として扱い、作成・更新・削除のライフサイクル全体を管理します。GA 済みの機能です。
テンプレートから外したリソースをスタックが自動的に削除できるため、構成のドリフトを抑えられます。削除の挙動は --action-on-unmanage で制御します。
# Deployment Stack を作成/更新する
az stack group create \
--name stack-app-prod \
--resource-group rg-app-prod \
--template-file main.bicep \
--parameters environment=prod \
--action-on-unmanage deleteResources \
--deny-settings-mode denyDelete
--action-on-unmanage deleteResources は、スタックの管理対象から外れたリソースを削除する設定です。--deny-settings-mode denyDelete を付与すると、スタックが管理するリソースをスタック外からの操作で削除できないよう保護でき、ポータルでの誤操作を防げます。環境全体を破棄する際は、スタックを削除するだけで関連リソースをまとめて片付けられます。
CI/CD への組み込み
Bicep の価値は、バージョン管理と自動デプロイに載せることで最大化されます。プルリクエストで what-if を実行してレビューし、マージを契機に本番へ反映する流れが標準的です。認証には、シークレットを保持しない OpenID Connect(OIDC)によるフェデレーション資格情報の利用を推奨します。
GitHub Actions の例を示します。プルリクエストでは what-if のみを実行し、main への push でデプロイを行います。
# GitHub Actions workflow (抜粋)
name: deploy-infra
on:
pull_request:
paths: ['infra/**']
push:
branches: [main]
paths: ['infra/**']
permissions:
id-token: write # OIDC 認証に必要
contents: read
jobs:
preview:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: what-if
run: |
az deployment group what-if \
--resource-group rg-app-prod \
--template-file infra/main.bicep \
--parameters infra/params/prod.bicepparam
Azure Pipelines を利用する場合も、AzureCLI@2 タスクで同じコマンドを実行できます。環境ごとのパラメータは .bicepparam ファイルに分離し、dev.bicepparam、prod.bicepparam のように環境単位で切り替えると、テンプレート本体を共通化したまま構成差分を管理できます。
// prod.bicepparam
using './main.bicep'
param environment = 'prod'
param location = 'japaneast'
他ツールとの比較とベストプラクティス
IaC ツールの選択では、対象範囲と運用体制を踏まえた判断が必要です。ARM テンプレート、Terraform との違いを整理します。
- ARM テンプレート — Bicep のコンパイル先であり、機能的には等価です。Bicep が可読性と保守性で上回るため、Azure 専用の新規開発では Bicep を選ぶのが自然です。
- Terraform — マルチクラウドやサードパーティ SaaS を含めて統一的に扱いたい場合に強みがあります。状態ファイル(state)の管理が前提となる点が Bicep との大きな違いです。Bicep は Azure 側の実状態を基準とするため、別途 state を保持しません。
- Bicep — Azure に閉じた構成であれば、最新の API バージョンへの追従が速く、AVM や Deployment Stacks といった Azure ネイティブの機能を活用できます。
運用にあたっては、次の点を基本方針とすることをおすすめします。
- 命名規約 — リソース名の接頭辞や環境識別子を規約化し、
uniqueString()で一意性を担保します。AVM を使うと命名規約の一部を委ねられます。 - スコープの分離 — サブスクリプション、リソースグループといったデプロイスコープを意識し、モジュールを適切な粒度で分割します。
- シークレットの扱い — 接続文字列やパスワードはテンプレートに直書きせず、Key Vault への参照で解決します。パラメータには
@secure()を付与し、ログへの出力を防ぎます。 - 環境ごとのパラメータ分離 — テンプレート本体は共通化し、環境差分は
.bicepparamに集約します。
Key Vault からシークレットを参照する例を示します。値そのものをテンプレートに残さず、Key Vault のリソース ID とシークレット名だけを指定します。
resource kv 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
name: keyVaultName
}
module sql './modules/sql.bicep' = {
name: 'sql-deploy'
params: {
administratorPassword: kv.getSecret('sqlAdminPassword')
}
}
getSecret() はデプロイ時に Key Vault から値を取得し、シークレットとして安全にモジュールへ渡します。パスワードの実体がテンプレートやデプロイ履歴に残らないため、機密情報の取り扱いとして適切です。
エンハンスド株式会社では、Azure を基盤としたシステムの IaC 導入と運用改善を支援しています。Bicep によるテンプレートの設計、Azure Verified Modules を用いた構成の標準化、Deployment Stacks や CI/CD の整備を通じて、再現性が高く運用しやすいクラウド基盤づくりをお手伝いします。手作業中心のインフラ運用からの脱却や、環境差分の解消をご検討の際は、お気軽にご相談ください。
このテーマの全体像は .NET モダナイゼーション完全ガイド にまとめています。
この記事をシェア
関連記事

Azure Container Appsで構築するマイクロサービスの実務ガイド
マイクロサービスをコンテナで運用したいものの、Kubernetes クラスターの構築と保守までは抱え込みたくない。こうした要望に応えるのが Azure Container Apps(ACA)です。…

.NET / Next.js アプリを Azure へデプロイする実践ガイド
.NET や Next.js のアプリケーションを Azure へ載せる方法は、この数年でかなり整理されました。ホスティングの選択肢が増えた一方で、CI/CD はキーレス(OIDC)が標準となり、…

.NET Modernization for Beginners とは — Copilot でレガシー .NET を .NET 10 へ引き上げる無料コース
Microsoft は 2026 年 7 月 16 日、 .NET Modernization for Beginners という無償の学習コースを公開しました。…
