Azure仮想マシン作成をARMテンプレートで完全自動化|Bicepで再現性を高める実践手順

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Azure > Azure仮想マシン作成をARMテンプレートで完全自動化|Bicepで再現性を高める実践手順
「Azureの仮想マシンをゼロから手順書通りに作るのは一度だけでいい。二度目以降は同じ構成を誰でも確実に再現できるようにしておきたい」
こう感じているなら、ARMテンプレートとBicepを使った自動化に踏み込む価値がある。Azure仮想マシン作成を毎回ポータルで手動操作すると設定の記録が残らず、開発・ステージング・本番と環境が増えるたびに手順書の管理コストが膨らんでいく。

この記事では、ARMテンプレートとBicepを使ってAzure仮想マシン作成を自動化する実践手順を解説する。Ubuntu 24.04 LTS~RHEL 9系を想定環境として説明する。

この記事のポイント

・ ARMテンプレートはJSONでVMリソースを定義し、az deployment group createで繰り返しデプロイできる
・ BicepはARMテンプレートの上位言語で可読性が高く、bicep buildでJSONに変換できる
・ parametersファイルを分けると開発・本番の設定を1コマンドで切り替えられる
・ デプロイ失敗時はaz deployment group show --queryでエラー詳細を素早く確認できる


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜARMテンプレートとBicepを使うのか

Azure仮想マシン作成をポータルのGUIで行うことの問題点は、「再現性がない」という一点に尽きる。クリックした内容は記録されず、後から「あのVMとまったく同じ設定でもう1台作りたい」という要求に応えられない。

ARMテンプレート(Azure Resource Managerテンプレート)はJSONで記述するインフラ定義ファイルだ。このファイルをazコマンドに渡すと、VNetの作成・パブリックIPの取得・NICのアタッチ・VMの起動まで一括で処理できる。同じファイルを使えば何度でも同じ構成が再現できる。

BicepはARMテンプレートの上位言語として2021年にMicrosoftが正式リリースした。JSON特有の冗長な記法をなくし、型安全性を持つ宣言型言語として設計されている。Bicepで書いたファイルはaz bicep buildでARMテンプレート(JSON)に変換できるため、最終的にデプロイするのはARMテンプレートと変わらない。

どちらを使うべきかという問いには、「新規で書くならBicep、既存JSONを引き継ぐならARMテンプレートを読めればよい」という答えが一般的だ。Azureのドキュメントも現在はBicepのサンプルを主として掲載している。

ARMテンプレートによるAzure仮想マシン作成の基本手順

1. テンプレートの基本構造を理解する

ARMテンプレートは以下の最上位キーから構成される。

・ $schema:テンプレートのスキーマURL(固定値)
・ contentVersion:テンプレートのバージョン管理用文字列
・ parameters:実行時に外部から渡す変数の定義
・ variables:テンプレート内で使いまわす計算済みの値
・ resources:作成するAzureリソースの配列(必須)
・ outputs:デプロイ後に取得したい値(VMのIPアドレスなど)

Linux VMを1台起動するには、以下のリソースを resources 配列に定義する必要がある。

・ Microsoft.Network/virtualNetworks(VNet)
・ Microsoft.Network/publicIPAddresses(パブリックIP)
・ Microsoft.Network/networkInterfaces(NIC)
・ Microsoft.Compute/virtualMachines(VM本体)

2. Linux VM用のARMテンプレートを作成する

以下は Ubuntu 24.04 LTS の VM を1台作成する最小構成のARMテンプレートだ。vm-deploy.json として保存する。

{ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "parameters": { "vmName": { "type": "string", "defaultValue": "myLinuxVM" }, "adminUsername": { "type": "string" }, "adminSshPublicKey": { "type": "string" }, "location": { "type": "string", "defaultValue": "[resourceGroup().location]" } }, "variables": { "vnetName": "[concat(parameters('vmName'), '-vnet')]", "subnetName": "default", "nicName": "[concat(parameters('vmName'), '-nic')]", "publicIpName": "[concat(parameters('vmName'), '-pip')]" }, "resources": [ { "type": "Microsoft.Network/virtualNetworks", "apiVersion": "2023-04-01", "name": "[variables('vnetName')]", "location": "[parameters('location')]", "properties": { "addressSpace": { "addressPrefixes": ["10.0.0.0/16"] }, "subnets": [ { "name": "[variables('subnetName')]", "properties": { "addressPrefix": "10.0.0.0/24" } } ] } }, { "type": "Microsoft.Network/publicIPAddresses", "apiVersion": "2023-04-01", "name": "[variables('publicIpName')]", "location": "[parameters('location')]", "sku": { "name": "Standard" }, "properties": { "publicIPAllocationMethod": "Static" } }, { "type": "Microsoft.Network/networkInterfaces", "apiVersion": "2023-04-01", "name": "[variables('nicName')]", "location": "[parameters('location')]", "dependsOn": [ "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]", "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpName'))]" ], "properties": { "ipConfigurations": [ { "name": "ipconfig1", "properties": { "privateIPAllocationMethod": "Dynamic", "publicIPAddress": { "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIpName'))]" }, "subnet": { "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnetName'), variables('subnetName'))]" } } } ] } }, { "type": "Microsoft.Compute/virtualMachines", "apiVersion": "2023-07-01", "name": "[parameters('vmName')]", "location": "[parameters('location')]", "dependsOn": [ "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" ], "properties": { "hardwareProfile": { "vmSize": "Standard_B2s" }, "osProfile": { "computerName": "[parameters('vmName')]", "adminUsername": "[parameters('adminUsername')]", "linuxConfiguration": { "disablePasswordAuthentication": true, "ssh": { "publicKeys": [ { "path": "[concat('/home/', parameters('adminUsername'), '/.ssh/authorized_keys')]", "keyData": "[parameters('adminSshPublicKey')]" } ] } } }, "storageProfile": { "imageReference": { "publisher": "Canonical", "offer": "ubuntu-24_04-lts", "sku": "server", "version": "latest" }, "osDisk": { "createOption": "FromImage", "managedDisk": { "storageAccountType": "Premium_LRS" } } }, "networkProfile": { "networkInterfaces": [ { "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" } ] } } } ], "outputs": { "publicIpAddress": { "type": "string", "value": "[reference(variables('publicIpName')).ipAddress]" } } }

3. az deployment group createでデプロイする

テンプレートのデプロイには az deployment group create を使う。事前にリソースグループが必要なので、まず作成しておく。

# リソースグループを作成する(未作成の場合) az group create --name myResourceGroup --location japaneast # ARMテンプレートをデプロイする az deployment group create \ --resource-group myResourceGroup \ --template-file vm-deploy.json \ --parameters adminUsername=azureuser \ adminSshPublicKey="$(cat ~/.ssh/id_rsa.pub)"

成功すると outputs セクションの publicIpAddress にパブリックIPが表示される。

# 出力例(環境により異なります) { "outputs": { "publicIpAddress": { "type": "String", "value": "20.xxx.xxx.xxx" } }, "provisioningState": "Succeeded" }

このIPアドレスを使ってSSH接続できる。

ssh azureuser@20.xxx.xxx.xxx

BicepでARMテンプレートを書き直す

先ほどのJSONテンプレートと同じVMをBicepで書き直すと、コードが大幅に短くなる。BicepはJSONの冗長な記法(dependsOn の明示的な配列・resourceId 関数の繰り返し)を排除し、型推論でparameterの記述量も減らせる。

1. Bicepのインストールと確認

Azure CLIにBicepが統合されているため、az bicep install で使えるようになる。

# Bicepのインストール az bicep install # バージョン確認 az bicep version # 出力例(環境により異なります) Bicep CLI version 0.30.x (xxxxxxxx)

2. Linux VMのBicepファイルを書く

以下は先ほどのARMテンプレートと同等の構成をBicepで記述したものだ。vm-deploy.bicep として保存する。

param vmName string = 'myLinuxVM' param adminUsername string @secure() param adminSshPublicKey string param location string = resourceGroup().location var vnetName = '${vmName}-vnet' var subnetName = 'default' var nicName = '${vmName}-nic' var publicIpName = '${vmName}-pip' resource vnet 'Microsoft.Network/virtualNetworks@2023-04-01' = { name: vnetName location: location properties: { addressSpace: { addressPrefixes: ['10.0.0.0/16'] } subnets: [ { name: subnetName properties: { addressPrefix: '10.0.0.0/24' } } ] } } resource publicIp 'Microsoft.Network/publicIPAddresses@2023-04-01' = { name: publicIpName location: location sku: { name: 'Standard' } properties: { publicIPAllocationMethod: 'Static' } } resource nic 'Microsoft.Network/networkInterfaces@2023-04-01' = { name: nicName location: location properties: { ipConfigurations: [ { name: 'ipconfig1' properties: { privateIPAllocationMethod: 'Dynamic' publicIPAddress: { id: publicIp.id } subnet: { id: vnet.properties.subnets[0].id } } } ] } } resource vm 'Microsoft.Compute/virtualMachines@2023-07-01' = { name: vmName location: location properties: { hardwareProfile: { vmSize: 'Standard_B2s' } osProfile: { computerName: vmName adminUsername: adminUsername linuxConfiguration: { disablePasswordAuthentication: true ssh: { publicKeys: [ { path: '/home/${adminUsername}/.ssh/authorized_keys' keyData: adminSshPublicKey } ] } } } storageProfile: { imageReference: { publisher: 'Canonical' offer: 'ubuntu-24_04-lts' sku: 'server' version: 'latest' } osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: 'Premium_LRS' } } } networkProfile: { networkInterfaces: [ { id: nic.id } ] } } } output publicIpAddress string = publicIp.properties.ipAddress

JSONと比べて行数が大幅に減り、リソース間の依存関係(dependsOn)も Bicep が自動解決するため明示的な記述が不要になる。

3. Bicepファイルからデプロイする

Bicepファイルは az deployment group create にそのまま渡せる(内部でARMテンプレートに変換される)。

# Bicepファイルを直接デプロイ(az bicep buildは不要) az deployment group create \ --resource-group myResourceGroup \ --template-file vm-deploy.bicep \ --parameters adminUsername=azureuser \ adminSshPublicKey="$(cat ~/.ssh/id_rsa.pub)" # ARMテンプレートJSONに変換したい場合(確認用) az bicep build --file vm-deploy.bicep # → vm-deploy.json が生成される

AzureのAzureエンジニア向け実践講座では、BicepとARMテンプレートを使った環境構築を実機ハンズオンで扱っている。テンプレート設計のパターンをしっかり身につけたい場合は参考にしてほしい。

パラメータファイルで開発・本番の環境を切り替える

ひとつのBicepファイルで開発・ステージング・本番の3環境を管理するには、パラメータファイルを環境ごとに用意するのが基本パターンだ。

1. パラメータファイルの構造

パラメータファイルはJSONで記述する(Bicep 0.19以降はbicepparamファイルも使えるが、JSON形式が最も普及している)。

# params-dev.json(開発環境用) { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "vmName": { "value": "vm-dev" }, "adminUsername": { "value": "devadmin" }, "adminSshPublicKey": { "value": "ssh-rsa AAAA..." } } }

# params-prod.json(本番環境用) { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", "contentVersion": "1.0.0.0", "parameters": { "vmName": { "value": "vm-prod" }, "adminUsername": { "value": "prodadmin" }, "adminSshPublicKey": { "value": "ssh-rsa AAAA..." } } }

2. パラメータファイルを指定してデプロイする

--parameters オプションにファイルパスを指定するだけで環境を切り替えられる。

# 開発環境にデプロイ az deployment group create \ --resource-group rg-dev \ --template-file vm-deploy.bicep \ --parameters @params-dev.json # 本番環境にデプロイ az deployment group create \ --resource-group rg-prod \ --template-file vm-deploy.bicep \ --parameters @params-prod.json

--parameters の @ 記法はファイルからの読み込みを指定する。同じテンプレートで異なるリソースグループに対して異なる設定でデプロイできる。

本番環境のSSH公開鍵など機密性の高い値は Key Vault と連携して参照する設計も検討するとよい。パラメータファイルにシークレットをそのまま書くと、ファイルをgitにコミットした際にシークレットが漏洩するリスクがある。

デプロイ失敗時のトラブルシュート

エラー1: InvalidTemplateDeployment

テンプレートの構文エラーやリソース名の重複が原因で発生することが多い。まずエラーの詳細を確認する。

# デプロイ操作の一覧を取得する az deployment group list \ --resource-group myResourceGroup \ --query "[].{name:name, state:properties.provisioningState}" \ --output table # エラー詳細を確認する(デプロイ名は上の出力から取得) az deployment group show \ --resource-group myResourceGroup \ --name <デプロイ名> \ --query "properties.error" # 出力例(環境により異なります) { "code": "InvalidTemplateDeployment", "message": "The template deployment ... failed ...", "details": [ { "code": "QuotaExceeded", "message": "Operation results in exceeding quota limits..." } ] }

details フィールドのコードを確認して対処する。QuotaExceeded はサブスクリプションのリソースクォータ不足を示す。Azureポータルのクォータ管理から増量申請が必要だ。

エラー2: ResourceNotFound(NICがVNetを参照できない)

ARMテンプレートでリソース間の依存関係が正しく設定されていないと、VNetの作成が完了する前にNICの作成が始まり失敗する。

# ARMテンプレートの場合: dependsOnを明示的に設定する "dependsOn": [ "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]" ] # Bicepの場合: リソースの参照を使えば依存関係は自動解決される # subnet の id を vnet.properties.subnets[0].id のように参照する

Bicepでリソースの直接参照(resource.property)を使っていれば、dependsOnを手動で書く必要はない。

エラー3: Bicepのバリデーションエラー

デプロイ前にBicepのテンプレートを検証したい場合は、az deployment group validate を使う。

# デプロイ前のバリデーション az deployment group validate \ --resource-group myResourceGroup \ --template-file vm-deploy.bicep \ --parameters @params-dev.json # What-If確認(実際に何が変更されるか事前確認) az deployment group what-if \ --resource-group myResourceGroup \ --template-file vm-deploy.bicep \ --parameters @params-dev.json

what-if は実際にリソースを作成せず、テンプレートを適用した場合の変更内容を一覧表示する。本番環境へのデプロイ前には必ず実行する習慣をつけると安全だ。

本記事のまとめ

Azure仮想マシン作成をARMテンプレートとBicepで自動化するポイントをまとめる。
やりたいこと コマンド・ファイル
ARMテンプレートでVMをデプロイする az deployment group create --template-file vm-deploy.json
Bicepファイルでデプロイする az deployment group create --template-file vm-deploy.bicep
BicepをARMテンプレートJSONに変換する az bicep build --file vm-deploy.bicep
パラメータファイルを指定してデプロイする az deployment group create --parameters @params-prod.json
デプロイ前に変更内容を確認する az deployment group what-if --template-file vm-deploy.bicep
デプロイエラーの詳細を確認する az deployment group show --query "properties.error"
ARMテンプレートは既存のAzureリソースをエクスポートして参考にする方法もある。Azureポータルでリソースグループを開き、「テンプレートのエクスポート」から既存構成をJSON化できる。これを出発点にしてBicepに移行していくのが現実的な学習ルートだ。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、AzureでのLinuxサーバー構築を実機で学ぶ実践講座を開催しています。ARMテンプレート・Bicep・VNet設計から始まり、セキュリティ設定・監視・バックアップまでを2日間のハンズオンで習得できます。詳細はこちらからご確認ください。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。