arm-templates について
ARM テンプレートと Bicep で Azure インフラをデプロイするガイドです。Azure ネイティブの Infrastructure as Code を使うチーム向けに、モジュール、デプロイ範囲、検証、変更プレビューを扱います。
主な機能
- 01Bicep と ARM テンプレートの作成
- 02再利用可能な Bicep モジュール
- 03複数のデプロイ範囲
- 04テンプレートの検証
- 05What-If による変更プレビュー
活用シーン
- 01コードによる Azure リソースの構築
- 02デプロイ前のインフラ変更の確認
- 03ARM JSON テンプレートから Bicep への移行
- 04リソースグループまたはサブスクリプション範囲へのデプロイ
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ bagelhole/ devops-security-agent-skills --skill arm-templatesインストール後、Claude Code で/arm-templatesを入力して読み込まれたか確認します。
同梱ファイルreferences/arm-syntax.md
詳細
ARM テンプレートと Bicep
ARM テンプレートと Bicep を使用して Azure インフラストラクチャをデプロイします。Bicep は、ARM JSON にコンパイルされる推奨のドメイン固有言語で、より簡潔な構文、モジュール、充実したツールサポートを備えています。
使用する場面
- サードパーティ製ツールを使わずに、Azure ネイティブの Infrastructure as Code が必要な場合。
- 組織が Azure を標準としており、ポータルとの緊密な統合を求めている場合。
- 変更をデプロイする前に What-If 分析が必要な場合。
- 保守性を高めるため、既存の ARM JSON テンプレートを Bicep に移行する場合。
- リソースグループ、サブスクリプション、管理グループ、またはテナントレベルのデプロイ スコープが必要な場合。
前提条件
# Install Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Install Bicep CLI (bundled with Azure CLI 2.20+)
az bicep install
az bicep upgrade
# Verify installation
az bicep version
# Login and set subscription
az login
az account set --subscription "my-subscription-id"
Bicep の基本
仮想ネットワークを含むリソースグループのデプロイ
// main.bicep
@description('Azure region for all resources')
param location string = resourceGroup().location
@description('Environment name used for resource naming')
@allowed(['dev', 'staging', 'prod'])
param environment string = 'dev'
@description('Base name for all resources')
param baseName string
var vnetName = '${baseName}-${environment}-vnet'
var nsgName = '${baseName}-${environment}-nsg'
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: nsgName
location: location
properties: {
securityRules: [
{
name: 'AllowHTTPS'
properties: {
priority: 100
direction: 'Inbound'
access: 'Allow'
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: '*'
destinationAddressPrefix: '*'
}
}
{
name: 'DenyAllInbound'
properties: {
priority: 4096
direction: 'Inbound'
access: 'Deny'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: '*'
}
}
]
}
}
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: [
'10.0.0.0/16'
]
}
subnets: [
{
name: 'web-subnet'
properties: {
addressPrefix: '10.0.1.0/24'
networkSecurityGroup: {
id: nsg.id
}
}
}
{
name: 'app-subnet'
properties: {
addressPrefix: '10.0.2.0/24'
}
}
{
name: 'data-subnet'
properties: {
addressPrefix: '10.0.3.0/24'
privateEndpointNetworkPolicies: 'Enabled'
}
}
]
}
}
output vnetId string = vnet.id
output webSubnetId string = vnet.properties.subnets[0].id
output appSubnetId string = vnet.properties.subnets[1].id
マネージド ID を使用した VM のデプロイ
// vm.bicep
param location string = resourceGroup().location
param vmName string
param subnetId string
param adminUsername string = 'azureuser'
@secure()
param adminPublicKey string
resource nic 'Microsoft.Network/networkInterfaces@2023-05-01' = {
name: '${vmName}-nic'
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: subnetId
}
}
}
]
}
}
resource vm 'Microsoft.Compute/virtualMachines@2023-07-01' = {
name: vmName
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
hardwareProfile: {
vmSize: 'Standard_B2s'
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
linuxConfiguration: {
disablePasswordAuthentication: true
ssh: {
publicKeys: [
{
path: '/home/${adminUsername}/.ssh/authorized_keys'
keyData: adminPublicKey
}
]
}
}
}
storageProfile: {
imageReference: {
publisher: 'Canonical'
offer: '0001-com-ubuntu-server-jammy'
sku: '22_04-lts-gen2'
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: 'Premium_LRS'
}
}
}
networkProfile: {
networkInterfaces: [
{
id: nic.id
}
]
}
diagnosticsProfile: {
bootDiagnostics: {
enabled: true
}
}
}
}
output vmPrincipalId string = vm.identity.principalId
output vmId string = vm.id
Bicep モジュール
モジュールの定義
// modules/storage.bicep
@description('Storage account name (3-24 chars, lowercase alphanumeric)')
param storageAccountName string
param location string = resourceGroup().location
param sku string = 'Standard_LRS'
@allowed(['Hot', 'Cool', 'Archive'])
param accessTier string = 'Hot'
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
name: sku
}
kind: 'StorageV2'
properties: {
accessTier: accessTier
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
networkAcls: {
defaultAction: 'Deny'
bypass: 'AzureServices'
}
}
}
output storageAccountId string = storageAccount.id
output primaryBlobEndpoint string = storageAccount.properties.primaryEndpoints.blob
モジュールの使用
// main.bicep
param location string = resourceGroup().location
param environment string = 'prod'
module storage 'modules/storage.bicep' = {
name: 'storage-deployment'
params: {
storageAccountName: 'myapp${environment}sa'
location: location
sku: environment == 'prod' ? 'Standard_GRS' : 'Standard_LRS'
}
}
module vnet 'modules/network.bicep' = {
name: 'vnet-deployment'
params: {
location: location
environment: environment
}
}
// Reference module outputs
output storageBlobEndpoint string = storage.outputs.primaryBlobEndpoint
ARM JSON テンプレートの構造
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"storageAccountName": {
"type": "string",
"metadata": {
"description": "Name of the storage account"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
}
},
"variables": {
"storageSku": "Standard_LRS"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"name": "[parameters('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[variables('storageSku')]"
},
"kind": "StorageV2",
"properties": {
"supportsHttpsTrafficOnly": true,
"minimumTlsVersion": "TLS1_2"
}
}
],
"outputs": {
"storageId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
}
}
}
デプロイ コマンド
# Validate a Bicep template before deployment
az deployment group validate \
--resource-group mygroup \
--template-file main.bicep \
--parameters environment='prod' baseName='myapp'
# Preview changes with What-If
az deployment group what-if \
--resource-group mygroup \
--template-file main.bicep \
--parameters environment='prod' baseName='myapp'
# Deploy Bicep to resource group
az deployment group create \
--resource-group mygroup \
--template-file main.bicep \
--parameters environment='prod' baseName='myapp' \
--name "deploy-$(date +%Y%m%d-%H%M%S)"
# Deploy ARM JSON with parameter file
az deployment group create \
--resource-group mygroup \
--template-file template.json \
--parameters @parameters.prod.json
# Subscription-level deployment (e.g., resource groups, policies)
az deployment sub create \
--location eastus \
--template-file subscription-level.bicep \
--parameters @params.json
# Management group deployment
az deployment mg create \
--management-group-id my-mg \
--location eastus \
--template-file mg-policy.bicep
# Export resource group to ARM JSON
az group export --name mygroup --output json > exported-template.json
# Decompile ARM JSON to Bicep
az bicep decompile --file exported-template.json
# Build Bicep to ARM JSON (for inspection)
az bicep build --file main.bicep --outfile main.json
# List deployments and their status
az deployment group list \
--resource-group mygroup \
--output table
# Delete a failed deployment
az deployment group delete \
--resource-group mygroup \
--name my-failed-deployment
パラメータ ファイル
// parameters.prod.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environment": { "value": "prod" },
"baseName": { "value": "myapp" },
"adminPublicKey": {
"reference": {
"keyVault": {
"id": "/subscriptions/{sub-id}/resourceGroups/{rg}/providers/Microsoft.KeyVault/vaults/{vault}"
},
"secretName": "ssh-public-key"
}
}
}
}
リンク済みテンプレートと入れ子になったテンプレート
// Deploy to a different resource group
module networkInSharedRg 'modules/network.bicep' = {
name: 'shared-network'
scope: resourceGroup('shared-networking-rg')
params: {
location: location
}
}
// Conditional deployment
param deployMonitoring bool = true
module monitoring 'modules/monitoring.bicep' = if (deployMonitoring) {
name: 'monitoring-deployment'
params: {
location: location
}
}
// Loop deployment
param storageAccounts array = [
{ name: 'logs', sku: 'Standard_LRS' }
{ name: 'data', sku: 'Standard_GRS' }
]
module storageLoop 'modules/storage.bicep' = [for account in storageAccounts: {
name: 'storage-${account.name}'
params: {
storageAccountName: '${baseName}${account.name}sa'
sku: account.sku
location: location
}
}]
トラブルシューティング
| 症状 | 原因 | 対処方法 |
|---|---|---|
InvalidTemplate エラー | ARM JSON または Bicep の構文エラー | az bicep build を実行して、コンパイルエラーを確認します |
デプロイ中の ResourceNotFound | リソースの依存関係が宣言されていない | dependsOn を追加するか、Bicep の暗黙的な参照を使用します |
クォータエラーを伴う DeploymentFailed | サブスクリプションのクォータ超過 | クォータの引き上げを申請するか、別のリージョンを使用します |
AuthorizationFailed | RBAC 権限が不足している | 対象のリソースグループに共同作成者ロールを割り当てます |
| パラメータ ファイル内のシークレットがソース管理に含まれている | シークレットがプレーンテキストで保存されている | パラメータ ファイルで Key Vault 参照を使用します |
| デプロイに非常に時間がかかる | 多数のリソースが直列にデプロイされている | 並列デプロイができるように、dependsOn を慎重に使用します |
What-If で予期しない削除が表示される | Incremental ではなく Complete モードになっている | 管理対象外のリソースが削除されないよう、--mode Incremental(既定値)を使用します |
| Bicep モジュールが見つからない | 相対パスが正しくない | パスがモジュールを使用するファイルからの相対パスになっていることを確認します |
関連スキル
terraform-azure-- より幅広いプロバイダーをサポートする、マルチクラウド対応の IaC 代替手段です。azure-networking-- テンプレートで参照される VNet、NSG、ファイアウォールの構成です。azure-vms-- 仮想マシンのサイズと構成の詳細です。azure-aks-- Bicep/ARM 用の Kubernetes クラスター定義です。
コメント
Markdown 対応