AzureのリソースをコードにしようとしてARMテンプレートを開くと、たいていここで手が止まります。仮想マシン1台分のテンプレートをポータルからダウンロードすると、parameters、variables、resources が何百行も続き、どこが自分の入れた値で、どこが自動で付いたものなのか見分けがつきません。

Bicepは、このJSONを人が書ける形にした言語です。そして実際に書いてみると、最初の1本は拍子抜けするほど短く済みます。ストレージアカウントなら、必須の項目は4つしかありません。

resource storage 'Microsoft.Storage/storageAccounts@2025-06-01' = {
  name: 'ebistudydemo001'
  location: 'japaneast'
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

ただし、この4行を白紙から思い出して書く必要はありません。同じものをポータルで1回作れば、Azureがその結果をJSONで見せてくれます。Bicepの最初の1本は、それを書き写す作業です。

もう1つ、最初に知っておくべきことがあります。動画の収録では、保存し忘れた空のファイルをデプロイし、Azureは「成功」と返しました。デプロイの成功は「書いたとおりになった」という意味で、「欲しいものができた」という意味ではありません。 この記事では、ポータルの結果からBicepを書き起こす手順と、書いたコードがAzureにどう解釈されるのかを、つまずく順に整理します。

ポータルで作っても、最後はテンプレートになっている

最初に押さえておくと楽になる事実があります。ポータルで「作成」を押したときも、裏ではテンプレートがAzureに渡されています。

ポータルの作成画面で「確認および作成」まで進むと、「自動化のためのテンプレートをダウンロードする」というリンクがあります。入力した値はパラメーターに、作るものの定義はJSONのテンプレートになっていて、Azure Resource Managerはそれを受け取ってリソースを作ります。リソースグループを作る画面からこのテンプレートをダウンロードする場面は、#0の1分57秒あたり(無料)で見られます。仮想マシンの分をダウンロードして中身を開き、「これを人間が一から書くのか」と眺めるところが7分27秒あたりです。

Bicepも行き着く先は同じです。Bicepのファイルはデプロイのときに同じ形式のJSONへ変換されてから、Azureに渡されます(az bicep build で変換結果のJSONを手元に出すこともできます)。

作り方 Azureに渡るもの
ポータルで「作成」 画面の入力から生成されたARMテンプレート(JSON)
ARMテンプレートを直接デプロイ 書いたJSONそのもの
Bicepをデプロイ BicepからコンパイルされたARMテンプレート(JSON)

入口が3つあるだけで、Azureの側から見ればどれも同じテンプレートです。だからポータルで設定できることは、テンプレートでも指定できるという前提で考えてかまいません。そして、ポータルでの作り方が分かっていれば、Bicepで書くべき中身もすでに半分は分かっています。

最初の1本は、ポータルで1回作ってから書く

1. ポータルで、どれが必須かを見ながら作る

まずはポータルで、Bicepで作りたいものと同じ設定のストレージアカウントを作ります。このとき見るべきなのは、各項目に必須の印が付いているかどうかです。

  • 必須の項目: 名前、リージョン、パフォーマンス、冗長性など。自分で決めないと作れない
  • 必須だが選択肢から選ぶ項目: 最初から1つ選ばれている。何も触らなければ、その既定値になる
  • 必須でない項目: 詳細設定やタグなど。指定しなければ既定値になる

Bicepでも、この区別はそのまま残ります。必須の項目だけ書けば作れて、残りは既定値になります。ポータルの作成画面を上から順に、必須かどうかを確かめながら埋めていく様子は、#1の2分50秒あたりから(無料)見られます。作成ボタンを押した直後に、ポータルの裏でもテンプレートが作られていることを確かめる場面が7分51秒あたりです。

2. 作ったリソースのJSONを見る

作ったリソースの概要ページには「JSONビュー」があります。ここに、Azureがそのリソースをどう記録しているかがそのまま出ます。

{
  "sku": { "name": "Standard_LRS", "tier": "Standard" },
  "kind": "StorageV2",
  "type": "Microsoft.Storage/storageAccounts",
  "location": "japaneast",
  ...
}

ポータルで「Standard」「ローカル冗長ストレージ (LRS)」と選んだものが、Standard_LRS という値になっていることが分かります。Bicepを書いていて「この項目に何を入れればいいのか」で詰まったら、ポータルで作ったもののJSONビューに答えが書いてあります。ポータルの選択肢とJSONの値を突き合わせる場面は9分45秒あたり、リソースのメニューから「テンプレートのエクスポート」でJSONを取り出す場面は11分12秒あたりにあります。

なお、JSONビューの sku には tier も出ていますが、Bicepで書くのは name だけで構いません。現在のリソースリファレンスで sku に指定する項目は name だけで、tier はAzureが付けて返してくる値です。動画では「tierは必須に出てこないが、なぜかは分からない」と話している箇所がありますが、理由はこれです。

3. VS Codeに必須のプロパティを並べさせる

ここまで来れば、書くこと自体はVS CodeのBicep拡張機能がほとんど手伝ってくれます。

  1. resource と書き、続けてシンボル名(ファイルの中で参照するための名前。何でもよい)を書く
  2. リソースの種類を storageacc などと打つと候補が出るので、Microsoft.Storage/storageAccounts を選ぶ
  3. @ の後ろのAPIバージョンも候補から選ぶ。特に理由がなければ最新でよい
  4. = の後ろで required-properties を選ぶと、必須のプロパティが空欄つきで並ぶ
  5. 空欄に、ポータルのJSONビューで見た値を入れる

ストレージアカウントで並ぶのは name、location、sku(の name)、kind の4つです。これが冒頭の4行の正体です。required-properties を選んで4つの項目が並ぶ場面は、#1の21分7秒あたり(無料)で見られます。補完の候補が次々に出てくる様子は、文章で読むより画面で見たほうが「これなら書ける」と感じられるはずです。

プロパティ ポータルでの項目 例 注意
name ストレージアカウント名 'ebistudydemo001' 3〜24文字の英小文字と数字だけ。Azure全体で一意
location リージョン 'japaneast' 作成後は変えられない
sku.name パフォーマンス+冗長性 'Standard_LRS' LRS・ZRS・GRSなどの略語の意味は覚えておく
kind (アカウントの種類) 'StorageV2' 汎用v2。特別な理由がなければこれ

APIバージョン(@2025-06-01 の部分)は、そのリソースの定義の版です。Azureに新しい機能が増えると、日付の新しいバージョンが出てプロパティが増えていきます。日付で版を固定しているので、あとから新しいバージョンが出ても、書いたコードの意味は変わりません。動画は2022年の収録なので、画面に出ているバージョンは古いものです。

動画の外の補足ですが、今のBicep拡張機能には、ARMテンプレートのJSONをBicepに変換して貼り付ける機能(Paste JSON as Bicep)もあります。エクスポートしたテンプレートを丸ごとBicepにできて便利ですが、エクスポートには既定値まで含めて全部の設定が入っています。最初の1本は、必須の4つから書き始めたほうが、何を自分で決めたのかが分かるコードになります。

名前の重複は、書く前から決まっている

ストレージアカウントの名前は、Azure全体で一意でなければなりません。自分のサブスクリプションの中だけでなく、世界中の誰かがすでに使っている名前もエラーになります。ポータルなら入力した時点で教えてくれますが、コードで流すと、デプロイして初めて失敗に気づきます。

このための関数が uniqueString です。

resource storage 'Microsoft.Storage/storageAccounts@2025-06-01' = {
  name: 'ebistudy${uniqueString(resourceGroup().id)}'
  ...
}

動画の23分5秒あたりでは「重複しないようなランダムな文字列をくっつける機能」として紹介していますが、正確にはランダムではありません。uniqueString は、渡した値から作るハッシュで、13文字の文字列を返します。同じ値を渡せば、何度実行しても同じ文字列になります(Microsoft Learn「Bicep の文字列関数」で確認)。

この性質は、むしろありがたいものです。上の例ならリソースグループのIDから名前を作るので、同じリソースグループに何度デプロイしても同じ名前になり、2回目は新しいアカウントを作るのではなく、同じアカウントを更新します。逆にリソースグループを変えれば別の名前になります。本当に毎回ランダムだったら、デプロイするたびにストレージアカウントが1つずつ増えていくことになります。

なお、関数が返すのはハッシュであって、グローバルな一意性は保証されません。重なる可能性はかなり低いものの、ゼロではありません。前に付ける文字(上の例の ebistudy)と合わせて24文字を超えないこと、英小文字と数字だけで書くことにも気をつけます。

どこへ置くかは、ファイルの外で決める

ポータルでは、リソースを作る画面でサブスクリプションとリソースグループを選びました。Bicepのストレージアカウントの定義には、そのどちらも書いていません。どこへ置くかは、デプロイするときのコマンドで決めます。 この「どの範囲にデプロイするか」をスコープと呼びます。

作りたいもの スコープ Azure CLI Azure PowerShell
ストレージアカウント、VMなど リソースグループ az deployment group create New-AzResourceGroupDeployment
リソースグループそのもの サブスクリプション az deployment sub create New-AzSubscriptionDeployment
管理グループへのポリシー割り当てなど 管理グループ/テナント az deployment mg create など New-AzManagementGroupDeployment など

判断の基準は、「できあがったものが、どの箱の中に見えるか」です。ストレージアカウントはリソースグループの中に見えるので、リソースグループのスコープでデプロイします。リソースグループはリソースグループの中には入っていない(サブスクリプションの中に見える)ので、サブスクリプションのスコープで作ります。Bicepファイルの既定のスコープはリソースグループなので、冒頭の4行には何も書く必要がありません。

ポータルで作ったときも、実はこのスコープでデプロイが記録されています。サブスクリプションの「デプロイ」と、リソースグループの「デプロイ」に、それぞれのスコープでの履歴が分かれて並んでいる様子を見ながらスコープを説明しているのが#1の34分13秒あたり(無料)です。ここで履歴の置き場所を一度見ておくと、スコープが「抽象的な概念」ではなく「履歴が残る場所」として分かります。

リソースグループのスコープに流すコマンドは、最小ではこうなります。

az login
az account set --subscription "<サブスクリプション名またはID>"
az deployment group create \
  --resource-group <リソースグループ名> \
  --template-file storage.bicep

az account set を忘れると、どのサブスクリプションに対して実行しているのか分からないまま進むことになります。サブスクリプションを複数持っているなら、必ず先に指定します。コマンドを組み立てて流すところは38分46秒あたりから見られます。

「成功しました」は、書いたとおりになったという意味でしかない

動画の収録で、こんなことが起きました。デプロイのコマンドは成功し、プロビジョニングの状態も「Succeeded」。ところが、待てど暮らせどストレージアカウントが現れません。数分待ったあとで気づいた原因は、VS Codeでファイルを保存していなかったことでした。空のファイルがデプロイされ、Azureは「何も作らない」という指示を正しく実行して、成功を返していたのです。原因に気づく場面は49分26秒あたり、保存し直してデプロイの「テンプレート」にストレージアカウントの定義が載っていることを確かめる場面は53分35秒あたりにあります。

笑い話のようですが、ここには大事な性質が表れています。Bicepは「何を作るか」ではなく「どういう状態であってほしいか」を書く言語です(宣言型)。Azureはテンプレートの状態に合わせ、合わせ終わったら成功を返します。空のテンプレートは「何も追加しなくてよい」という正当な状態なので、成功します。

だから、デプロイが成功したのに期待したものが無いときは、AzureではなくAzureに渡ったものを疑います。

  • リソースグループの「デプロイ」から該当の履歴を開き、「テンプレート」に実際に渡った内容を見る
  • デプロイ名を付けなければ、ファイル名(storage.bicep なら storage)がデプロイ名になる。同じ名前で流すと履歴は上書きされるので、どの実行の結果を見ているのかに注意する
  • 流す前に az deployment group what-if(PowerShellなら -WhatIf)を使うと、作成・変更・削除される予定のリソースを、実際には何も変えずに確かめられる(動画の外の補足)

保存し忘れのような単純な間違いほど、what-if で「何も作られない予定」と表示されれば一目で分かります。

何度流しても同じ。ただし、書かなかったプロパティも指定したことになる

ストレージアカウントができたあと、同じコマンドをもう一度流しても、何も起きずに成功します。すでにテンプレートどおりの状態だからです(55分47秒あたり)。何度流しても結果が同じになることが、コードで管理する一番の価値です。

ただし、ここには動画では触れていない落とし穴があります。Microsoft Learnの「デプロイ モード」には、こう書かれています。既存のリソースを再デプロイすると、テンプレートに書いたプロパティが追加されるのではなく、すべてのプロパティが再適用される。そして、テンプレートに書いていないプロパティは既定値にリセットされる。

つまり、必須の4つだけを書いたテンプレートは、「4つ以外は既定値でよい」という指示です。作ったあとに誰かがポータルで設定を1つ変え、そのことを知らずに同じテンプレートを流し直せば、その設定を既定値へ戻す指示を出したことになります。

状況 書いていないものの扱い
テンプレートに書いていないプロパティ 既定値として扱われる。再デプロイで既定値に戻ることがある
テンプレートに書いていないリソース(同じリソースグループにある別のもの) 既定の増分モードでは触らない。削除されない

ここから導かれる運用の約束は2つです。

  1. Bicepで作ったものは、Bicepで変える。 ポータルで直したら、同じ変更をBicepにも書く。テンプレートを「いつ流しても正しい状態」に保つ
  2. 既存のリソースへ流す前に what-if を見る。 変わる予定のプロパティが一覧で出るので、意図しない巻き戻しに気づける

必須の4つは「作る」には十分ですが、「維持する」には足りません。ポータルで既定値から変えた設定があるなら、それはテンプレートにも書くべき設定です。

Azure CLIとAzure PowerShellで、準備が1つ違う

Bicepのデプロイは、Azure CLIでもAzure PowerShellでもできます。コマンドの形が違うだけで、やっていることは同じです。ただ、準備に1つだけ差があります。

  • Azure CLI: Bicep CLI(BicepをJSONに変換する道具)が、必要になったときに自動でインストールされる
  • Azure PowerShell: Bicep CLIは自動では入らない。手動でインストールする必要がある。しかも、Azure CLIが自動で入れたBicep CLIはPowerShellからは使えない

(どちらもMicrosoft Learn「Bicep 開発とデプロイ環境をインストールする」で確認)

Azure CLIでは何も入れずに流せたので、同じつもりでPowerShellから New-AzResourceGroupDeployment を実行し、Bicepが見つからないと言われてインストールし直す場面が、#1の51分あたり(無料)にあります。PowerShell派の人ほど最初に一度だけ踏む段差なので、知っておけば数分の回り道で済みます。開発環境の準備(VS CodeとBicep拡張機能、Azure CLIとAzure PowerShellの導入)は、#0の14分3秒あたりから順に見られます。

最初の1本で確かめる順番

段階 確かめること
書く前 ポータルで同じものを1回作り、必須の項目とJSONビューの値を見たか
書くとき required-properties で必須を並べ、値をJSONビューから写したか
名前 一意である必要があるリソースか。uniqueString を使うなら、何を元にするか決めたか
流す前 az account set でサブスクリプションを指定したか。スコープとコマンドが合っているか
流す前 ファイルを保存したか。what-if で予定どおりの変更が出るか
流した後 失敗でも成功でも、デプロイ履歴の「テンプレート」で実際に渡った内容を見る
運用 ポータルで変えた設定をテンプレートにも書いたか

まとめ

  • ポータルの「作成」も、ARMテンプレートも、Bicepも、Azureに渡るのは同じテンプレート。だからポータルでできることはBicepでも書ける
  • Bicepの最初の1本は白紙から書かない。ポータルで1回作り、JSONビューの値を、VS Codeが並べる必須のプロパティに書き写す
  • uniqueString はランダムではなく、同じ入力から同じ13文字を返すハッシュ。だから再デプロイで同じリソースを更新できる
  • どこへ置くかはスコープで決まり、スコープはファイルではなくデプロイのコマンドで選ぶ。リソースグループそのものはサブスクリプションのスコープで作る
  • デプロイの「成功」は、渡したテンプレートどおりになったという意味。空のファイルでも成功する。流す前に what-if、流した後はデプロイ履歴のテンプレートを見る
  • 再デプロイでは、書かなかったプロパティは既定値として扱われる。Bicepで作ったものはBicepで変える

この記事では、1つのリソースをリソースグループへ流すところまでを扱いました。ここで出てきた「リソースグループそのものはサブスクリプションのスコープで作る」を実際にコードにするのが、Bicep入門 #2「リソースグループ展開」(メンバー限定)です。リソースグループとその中身を1回のデプロイでまとめて作れるようになると、「空のサブスクリプションから、環境を丸ごと作り直せる」状態にぐっと近づきます。

なお、素材の動画は2022年の収録です。VS Codeの画面、APIバージョン、ツールのインストール方法は現在と異なる部分があります。ポータルの結果を写してBicepを書くこと、スコープを選んでデプロイすること、テンプレートどおりの状態になったら成功を返すという仕組みそのものは変わっていません。