Azure Resource Manager(ARM)の小ネタ:仮想マシンのカスタムイメージをJSONテンプレートからデプロイ

#はじめに
前回の記事で「[Azure上でRed Hat Enterprise Linux 7.3を動かすためのVMイメージを作成する](http://qiita.com/yotan/items/31a8199a3c8a9f096fcc)」というのを公開しましたが、その作成したカスタムイメージのをJSONテンプレート(と一部Azure Powershell)を使ってデプロイする手順についてまとめました。

Azure CLIを使ってデプロイするよ？って人は「[Linux オペレーティング システムを格納した仮想ハード ディスクの作成とアップロード](https://docs.microsoft.com/ja-jp/azure/virtual-machines/virtual-machines-linux-classic-create-upload-vhd)」を参照。
全部Powershellを使うし？という人は「[Resource Manager デプロイメント向けに Windows VM イメージを Azure にアップロードする](https://docs.microsoft.com/ja-jp/azure/virtual-machines/virtual-machines-windows-upload-image?toc=%2fazure%2fvirtual-machines%2fwindows%2ftoc.json)」あたりを参照ください。

#ながれ
OSのカスタムイメージを使って仮想マシンをデプロイする際には「仮想マシンのOSイメージを格納するストレージアカウント上にカスタムイメージが保存されている」ことが必須となります。
コンテナ名は異なっていても良いのですが、事前にストレージアカウントを作成して、そこにカスタムイメージとなるVHDファイルを転送しておく必要があります。
なので流れとしては

1. (カスタムイメージを作成)
- リソースグループを作成
- ストレージアカウントを作成
- ストレージアカウント上にカスタムイメージを転送
- 仮想マシン(及びその他必要なリソース)を作成

といった感じになります。

今回は1.の手順は省略しますが、2～5の手順をAzure PowershellとJSONテンプレートを使って構築してみます。

#デプローイ！
先のながれで説明した手順のPowershellスクリプトを以下に示します。

```ps1
New-AzureRmResourceGroup -Location japaneast -Name TEST-rg
New-AzureRmResourceGroupDeployment -ResourceGroupName TEST-rg -TemplateParameterFile .\vm-parameters-rhel-1.json -TemplateFile .\vm-template-rhel-1.json
Add-AzureRmVhd -ResourceGroupName TEST-rg -Destination https://<storageaccountname>.blob.core.windows.net/images/rhel-azure-image.vhd -LocalFilePath "C:\temp\rhel-azure-image.vhd"
New-AzureRmResourceGroupDeployment -ResourceGroupName TEST-rg -TemplateParameterFile .\vm-parameters-rhel-2.json -TemplateFile .\vm-template-rhel-2.json
```

JSONテンプレートはストレージアカウントを作成するvm-template-rhel-1.jsonと、仮想マシン(及びその他必要なリソース)を作成するvm-template-rhel-2.jsonを準備しました。それぞれのテンプレートおよびサンプルパラメータは後述します。

こちらのスクリプトを実行すると、カスタムイメージがローカルPCから転送され、仮想マシンの動作に必要なリソースがAzure上に作成されます。
![image](https://qiita-image-store.s3.amazonaws.com/0/137436/50c215be-74d9-c887-e30f-4417103a3b58.png)

今回の例ではRed Hat Enterprise Linux 7.3のカスタムイメージを使って仮想マシンを構築しました。
![image](https://qiita-image-store.s3.amazonaws.com/0/137436/496de1f4-9fc0-2983-cadf-ae239afc209a.png)

ssh経由のログインも無事できました。
![image](https://qiita-image-store.s3.amazonaws.com/0/137436/21512000-69f2-e876-af40-8106e0b453d0.png)

#サンプルファイル
以下にJSONテンプレート及びパラメータのサンプルを示します。
パラメータやテンプレートの中身については適宜修正して再利用頂ければと思います。

##JSONテンプレート(ストレージアカウント)

```json:vm-template-rhel-1.json
{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "storageAccountName": {
            "type": "string"
        },
        "storageAccountType": {
            "type": "string"
        }
    },
    "variables": {},
    "resources": [
        {
            "name": "[parameters('storageAccountName')]",
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2016-05-01",
            "location": "[resourceGroup().location]",
            "kind": "Storage",
            "sku": {
                "name": "[parameters('storageAccountType')]"
            }
        }
    ],
    "outputs": {}
}
```

```json:vm-parameters-rhel-1.json
{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "storageAccountName": {
      "value": "<storageaccountname>"
    },
    "storageAccountType": {
      "value": "Standard_LRS"
    }
  }
}
```

##JSONテンプレート(仮想マシン他)

```json:vm-template-rhel-2.json
{
    "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "addressPrefix": {
            "type": "string"
        },
        "adminPassword": {
            "type": "securestring"
        },
        "adminUsername": {
            "type": "string"
        },
        "networkInterfaceName": {
            "type": "string"
        },
        "networkSecurityGroupName1": {
            "type": "string"
        },
        "PublicIpAddressName": {
            "type": "string"
        },
        "storageAccountName": {
            "type": "string"
        },
        "imageName": {
            "type": "string"
        },
        "subnetName1": {
            "type": "string"
        },
        "subnetPrefix1": {
            "type": "string"
        },
        "virtualMachineName": {
            "type": "string"
        },
        "virtualMachineSize": {
            "type": "string"
        },
        "virtualNetworkName": {
            "type": "string"
        }
    },
    "variables": {
        "vnetId": "[resourceId('Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
        "SubnetRef": "[concat(variables('vnetID'),'/subnets/',parameters('subnetName1'))]",
        "osDiskName": "[concat(parameters('virtualMachineName'), '-osdisk.vhd')]"
    },
    "resources": [
        {
            "name": "[parameters('virtualNetworkName')]",
            "type": "Microsoft.Network/virtualNetworks",
            "apiVersion": "2016-03-30",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[concat('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName1'))]"
            ],
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "[parameters('addressPrefix')]"
                    ]
                },
                "subnets": [
                    {
                        "name": "[parameters('subnetName1')]",
                        "properties": {
                            "addressPrefix": "[parameters('subnetPrefix1')]",
                            "networkSecurityGroup": {
                                "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName1'))]"
                            }
                        }
                    }
                ]
            }
        },
        {
            "name": "[parameters('networkSecurityGroupName1')]",
            "type": "Microsoft.Network/networkSecurityGroups",
            "apiVersion": "2016-10-01",
            "location": "[resourceGroup().location]",
            "properties": {
                "securityRules": [
                    {
                        "name": "allow-ssh",
                        "properties": {
                            "protocol": "Tcp",
                            "sourcePortRange": "*",
                            "destinationPortRange": "22",
                            "sourceAddressPrefix": "*",
                            "destinationAddressPrefix": "*",
                            "access": "Allow",
                            "priority": 1000,
                            "direction": "Inbound"
                        }
                    }
                ]
            }
        },
        {
            "type": "Microsoft.Network/publicIPAddresses",
            "apiVersion": "2016-10-01",
            "name": "[parameters('PublicIpAddressName')]",
            "location": "[resourceGroup().location]",
            "properties": {
                "publicIPAllocationMethod": "Dynamic"
            }
        },
        {
            "name": "[parameters('virtualMachineName')]",
            "type": "Microsoft.Compute/virtualMachines",
            "apiVersion": "2016-03-30",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[concat('Microsoft.Network/networkInterfaces/', parameters('networkInterfaceName'))]"
            ],
            "properties": {
                "osProfile": {
                    "computerName": "[parameters('virtualMachineName')]",
                    "adminUsername": "[parameters('adminUsername')]",
                    "adminPassword": "[parameters('adminPassword')]"
                },
                "hardwareProfile": {
                    "vmSize": "[parameters('virtualMachineSize')]"
                },
                "storageProfile": {
                    "osDisk": {
                        "osType": "Linux",
                        "name": "[parameters('virtualMachineName')]",
                        "vhd": {
                            "uri": "[concat(concat(reference(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), '2015-06-15').primaryEndpoints['blob'], 'vhds/'), variables('osDiskName'))]"
                        },
                        "image": {
                            "uri": "[concat('https://', parameters('storageAccountName'), '.blob.core.windows.net/images/',parameters('imageName'))]"
                        },
                        "createOption": "FromImage"
                    },
                    "dataDisks": []
                },
                "networkProfile": {
                    "networkInterfaces": [
                        {
                            "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName'))]"
                        }
                    ]
                }
            }
        },
        {
            "name": "[parameters('networkInterfaceName')]",
            "type": "Microsoft.Network/networkInterfaces",
            "apiVersion": "2016-10-01",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[concat('Microsoft.Network/publicIpAddresses/', parameters('publicIpAddressName'))]",
                "[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]"
            ],
            "properties": {
                "ipConfigurations": [
                    {
                        "name": "ipconfig1",
                        "properties": {
                            "subnet": {
                                "id": "[variables('subnetRef')]"
                            },
                            "privateIPAllocationMethod": "Dynamic",
                            "publicIpAddress": {
                                "id": "[resourceId('Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName'))]"
                            }
                        }
                    }
                ]
            }
        }
    ],
    "outputs": {}
}
```

```json:vm-parameters-rhel-2.json
{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "addressPrefix": {
      "value": "192.168.0.0/16"
    },
    "adminPassword": {
      "value": "<password>"
    },
    "adminUsername": {
      "value": "<username>"
    },
    "networkInterfaceName": {
      "value": "TESTVM-nic"
    },
    "networkSecurityGroupName1": {
      "value": "TEST-subnet1-nsg"
    },
    "PublicIpAddressName": {
      "value": "TESTVM01-pip"
    },
    "storageAccountName": {
      "value": "<storageaccountname>"
    },
    "imageName": {
      "value": "rhel-azure-image.vhd"
    },
    "subnetName1": {
      "value": "Subnet-1"
    },
    "subnetPrefix1": {
      "value": "192.168.0.0/24"
    },
    "virtualMachineName": {
      "value": "TESTVM01"
    },
    "virtualMachineSize": {
      "value": "Standard_F1s"
    },
    "virtualNetworkName": {
      "value": "TEST-vnet"
    }
  }
}
```

#おわりに
カスタムイメージを使った場合、同一のストレージアカウントに先にVHDファイルを転送しておかないといけない(別のストレージアカウントを参照できない)のが、少しイマイチですが、将来の仕様改善に期待です！