【Azure】ARMテンプレートを使おう！

# Azureリソースの定義をコードで管理したい

　Azureを利用していて、「Azure Portalで毎回ポチポチ操作するのが面倒！」とか「リソースの設定もコードで管理して変更履歴を追いたい！」と思うことがあるのではないでしょうか？AzureにはIaC(Infrastructure as Code）を定義してAzureリソースを管理するサービスとして、Azure Resource Manager Template（ARMテンプレート）があります。このサービスを利用することで、

- 再現可能なインフラストラクチャを作成
- リソースの定義をコードで管理

などを実現することができます。しかし、以下のような理由でARMテンプレートがとっつきにくいと考えている方も多いのではないでしょうか？

- 細かいパラメータを定義できる反面、どのパラメータの指定が必要なのかわからない（全部指定するの？）
- 初回だけとはいえ、長々とテンプレートを書くのが面倒

また、ARMテンプレートを作成する上で、キーワードの補完を行ってくれるようなツールも欲しいですよね？
　この記事では、「ARMテンプレートってどうやって書けばいいの？」や「ARMテンプレートを書くのにお勧めのツールは？」といった疑問に答えたいと思います。

# ARMテンプレートってどうやって書けばいいの？

　まずは、皆さんのARMテンプレート利用へのハードルを下げたいと思います。ハードルを下げるために、ARMテンプレートの構造を理解してから効率的にテンプレートを完成させる方法を紹介します。

## ARMテンプレートの構造を理解する

　ARMテンプレートは以下のような構造で定義されます。（ **※あまり使わない項目や発展的な項目は省略しています** ）

```json:sample.json
{ 
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 
    "contentVersion": "1.0.0.0", 
    "parameters": {}, 
    "variables": {}, 
    "resources": []
} 
```

|項目|説明|
|:---:|:---:|
|$schema|テンプレート言語のバージョンが記述されているJSONスキーマファイルの場所|
|contentVersion|テンプレートのバージョン|
|parameters|デプロイ時にユーザーが指定したい項目を定義|
|variables|毎回変更の必要がない固定値を変数として定義|
|resources|リソースグループまたはサブスクリプション内でデプロイまたは更新するリソースの定義|

各項目で具体的に何を指定していくのかを順番に説明します。

### parameters

　parametersでは、リソース名やSKUなどのデプロイ時にユーザーが指定したい項目を定義できます。

```
"parameters": {
    “<parameter-name>”:{
        “type”: “string”,
        “defaultValue”: “test”,
        “allowedValues”: [ “test”, “prod” ],
        “metadata”: {
            “description”: “value of test”
        }            
    }
},
```

|項目|説明|
|:---:|:---:|
|type|パラメーター値の型。使用できる型は、string, securestring, int, bool, object, secureObject, array|
|defaultValue|パラメーターに値が指定されない場合のパラメーターの既定値|
|allowedValues|適切な値が確実に指定されるように、パラメーターに使用できる値の配列|
|description|ポータルを通じてユーザーに表示されるパラメーターの説明|

### variables

　variablesでは、毎回変更の必要がない固定値を変数として定義できます。

```
“variables": {
    “<variable-name>”: “<variable-value>”
    “copy”: [
        {
            "name": “stringArray",
            “count”: 3,
            “input”: “[concat('item', copyIndex('stringArray', 1))]”
        }
    ]
},
```

|項目|説明|
|:---:|:---:|
|name|ループを識別する任意の値|
|count|変数に必要な反復回数|
|input|繰り返すプロパティ|

このテンプレートは以下の配列を返します。

```
[ “item1”, “item2”,　"item3", "item4", "item5" ]
```

配列以外にもオブジェクトを作成することも可能です（詳細は[公式ドキュメント（変数の反復処理）](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/templates/copy-variables)参照）。

### resources

　resourcesでは、リソースグループまたはサブスクリプション内でデプロイまたは更新するリソースの定義を行うことが可能で、リソースは複数定義できます。ここでは例として、仮想マシンの定義を紹介します。resources内は各リソースによって設定項目が異なるため [公式リファレンス](https://docs.microsoft.com/ja-jp/azure/templates)を参照してください。

```
"resources": [
        {
            "apiVersion": "2019-12-01",
            "type": "Microsoft.Compute/virtualMachines",
            "name": "[variables('vmName')]",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "updateIp"
            ],
            "properties": {
                "hardwareProfile": {
                    "vmSize": “Standard_A1_v2”
                },
                "osProfile": {
                    "computerName": "[variables('vmName')]",
                    "adminUsername": "[parameters('adminUsername')]",
                    "adminPassword": "[parameters('adminPasswordOrKey')]",
                    "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), json('null'), variables('linuxConfiguration'))]"
                },
                "storageProfile": {
                    "imageReference": {
                        "publisher": "Canonical",
                        "offer": "UbuntuServer",
                        "sku": "16.04.0-LTS",
                        "version": "latest"
                    }
                },
                "networkProfile": {
                    "networkInterfaces": [
                        {
                            "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
                        }
                    ]
                }
            }
        }
]
```

各リソースによって設定項目が異なるため、ARMテンプレート内で使用可能な便利関数の一部を紹介します。

|関数|説明|
|:---:|:---:|
|variables|“variables”で指定した変数を呼び出す関数|
|parameters|“parameters”で指定した値を呼び出す関数|
|resourceGroup|デプロイ時に指定するデプロイ先リソースグループに関する情報を返す関数で、location以外のプロパティも存在する|
|resourceId|リソースIDを返す関数。（使用方法：**resourceId(‘<リソースの種類>’, ’<リソース名>’)**）|

また、リソース間の依存関係も定義可能です。上記の例では以下の記述がそれにあたります。

```
"dependsOn": [
    "updateIp"
],
```

　"dependsOn"では、先に作成されている必要のあるリソースを複数指定できます。リソース名かリソースIDのどちらかで指定可能です。上記の例ではリソース名で指定していますが、別種類で同名のリソースが存在する可能性があるため、リソースIDで指定したほうが安全です。

## 効率よくARMテンプレートを作成する

　ここまでで、ARMテンプレートの構造を理解しました。しかし、「１から自力で作成するのは面倒。効率よく作成出来ないの？」や「公式リファレンスのパラメータって全部必要？」といった疑問が出てくると思います。ここでは、最低限の労力で効率よくARMテンプレートを作成するためのポイントを紹介します。

### 必要なパラメータのみ指定する

　[公式リファレンス](https://docs.microsoft.com/ja-jp/azure/templates)には各リソース作成時に指定できる全パラメータが記載されています。しかし、ここの全パラメータを指定しなければいけないわけではありません。パラメータには必須項目と非必須項目があり、最低限指定しなければいけないのは必須項目のみです（公式リファレンス内の表に記載されています）。指定されなかった非必須項目はデフォルト値もしくは、未指定の状態でリソースが作成されます（パラメータによる）。効率よくARMテンプレートを作成するためには、

1. 必須項目の値を決定する
2. 要件に応じて必要な非必須項目の値を決定する。

という手順をふむのが良いでしょう。闇雲に全パラメータの値を検討していては余計な時間が掛かってしまいます。 

### クイックスタートテンプレートを活用する

　クイックスタートテンプレートとは[公式リファレンス](https://docs.microsoft.com/ja-jp/azure/templates)の各リソースページ最下部にリンクされているテンプレート例です。よく指定されるであろう項目を厳選しているため、クイックスタートテンプレートを基にして作成を始めると、短時間でARMテンプレートを作成できると思います。

### 既存リソースからテンプレートを作成する

　Azureでは既存のリソースからARMテンプレートをエクスポートする機能が用意されています。既存リソースを複製したい場合や、既存リソースを参考にしたい場合などに有用です。ARMテンプレートをエクスポートする方法は３種類あります。

1: Azure Portalから個別のリソースごとにエクスポート

Portalでエクスポートしたいリソースを開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/598a85ec-ae45-d269-e6de-417768c4a224.png)

"テンプレートのエクスポート"を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/37360df5-4261-b78e-790a-e2c2a89826c7.png)

"ダウンロード"を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/2ec3c6fa-97f6-5fa2-c39e-7d982000a53a.png)


2: Azure Portalからリソースグループ単位でエクスポート

Portalでエクスポートしたいリソースグループを開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/fb4f57f1-ca9b-eee0-d1e1-a81acd677c3c.png)

"テンプレートのエクスポート"を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/a4ef8108-81fc-96b7-db6e-c332e647cf4f.png)

"ダウンロード"を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/d4f10620-885f-4fcc-2675-1e7a0b09240b.png)

3: Azure CLIでエクスポート（Power Shellでも可）

Azureへログイン

```shell
$ az login
# ブラウザを開くことができる場合、開いたブラウザに Azure サインイン ページが読み込まれます
# それ以外の場合、別デバイスで https://aka.ms/devicelogin を開き、ターミナルに表示されている認証コードを入力します
```

テンプレートをエクスポート

```shell
# リソースグループ単位でエクスポート
$ az group export --name <resourceGroupName>
```

**※CLIでは個別のリソースごとにはエクスポートできないようです（2020/5現在）**

# ARMテンプレートを書くのにお勧めのツールは？

　ここまでで、ARMテンプレートを効率よく書く方法を紹介しました。これでARMテンプレートを書く準備は整った訳ですが、実際に書いていくとタイプミスなどのケアレスミスがどうしても発生してしまいます。ここではそれを防ぐためのツールを紹介します。
　私が紹介するのはVisual Studio Codeの拡張機能である「Azure Resource Manager (ARM) Tools for Visual Studio Code」です。この拡張機能はARMテンプレートに対するvalidation機能や補完機能を備えています。この拡張機能のvalidation機能でタイプミスなどのケアレスミスを防ぐことができますし、補完機能を活用することで作成時間を短縮することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/507052/4383fe40-e369-bd9e-6f70-10339f34192f.png)

# まとめ

　この記事では、「ARMテンプレートってどうやって書けばいいの？」や「ARMテンプレートを書くのにお勧めのツールは？」といった疑問に答えるために、

- ARMテンプレートの構造
- ARMテンプレートを効率よく書くためのポイント
- ARMテンプレートの作成を補助してくれるツール

を紹介しました。皆さんがこの記事を読むことでARMテンプレートを使ってみたいと思ってくれると嬉しいです。