ARMテンプレートの新機能とノウハウ

この記事はAzure AdventCalendar2019 の記事です。

去年も同様のARMテンプレートノウハウ記事を書いてますので合わせてご参考ください。
https://qiita.com/tenn25/items/7921a9f1a36fcb6218d9

## １．最近の新機能

先月のMSIgniteでARMテンプレートに関する新機能がいくつか発表されました。

セッション動画がYoutubeで公開されています（ありがたや）
https://www.youtube.com/watch?v=3D-JIKShrws&feature=youtu.be



#### ①コメントアウトできるようになった

ARMテンプレートは現状JSONファイルですが、`//`や`/**/`でコメントが入れられるようになり、
CLIやPowershellからのデプロイに対応しました。
以下で紹介する公式の拡張機能を入れる前提ですが、VSCode上でもエラーになりません。

#### ②コードスニペット機能

こちらも後述のVSCodeの拡張機能ですが、
今までは、公式のリファレンスを見るか、ポータルから既存リソースをコードに出力してプロパティを確認するしかありませんでした。
`arm`と入力することでコードスニペットの候補が出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/adfdd648-baf5-b89d-31df-64e2ab257b8a.png)

選択すると、自動でひな形が作られます。
まだ大まかなリソースしか出てこず、第2,3階層の細かいリソースは出てきませんが、今後に期待したいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/3e0b08b9-9dce-b714-ee70-53c0751395e6.png)

`"type": ""`と打てば細かいリソースも候補に出てきそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/72875498-6743-d460-c3a6-c133753a3f31.png)

`apiVersion`もそのリソースタイプが定義されているAPIバージョンの候補が出てきます。これは便利。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/83511a92-2677-eefb-edfc-abd31b67e4b5.png)

""と入れれば他の項目も候補に出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/858e9bd2-f99d-5827-240a-1685d877caca.png)

MSIgniteのデモだと、以下のように中のプロパティも候補が確認できてるのですが、
自分の手元では出し方が分かりませんでした…（できた方教えてください）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/271fd766-56fb-7b9a-a84d-bdeaf5294ef4.png)



#### ③デプロイ前に差分が確認できるようになった(What If機能)

待望の機能がついに出ました…！！！
AWSのCloudformationやTerraformなどはデプロイ前に差分が確認できますが、
ARMテンプレートは長らくこれができず、IaCツールとしては遅れをとっていました。

こちらもMSIgniteのデモから抜粋します。
デモではaddressPrefixを変更し、subnetを１つコメントアウトして、Powerhsellの`New-AzDeploymentWhatIf`コマンドを実行しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/dc81df16-8ba2-548b-8218-9fb7c5c12172.png)


右側のターミナルに差分が表示されています。
素晴らしいです…涙で前が見えません。
まだプレビュー段階ですが、これで実運用でも安心してデプロイできそうです。

利用申請はこちら
http://aka.ms/ARMTemplatePreviews

公式ドキュメントはこちら
https://docs.microsoft.com/en-us/azure/azure-resource-manager/template-deploy-what-if


## ２．VSCodeのおすすめプラグイン

#### ①ARMテンプレート公式拡張機能

この拡張が優秀なので、宗教上の理由がない限りはVSCodeを使いましょう。

https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/resource-manager-tools-vs-code

機能は上記に書いたような機能やインテリセンス、文字の色付けです。

#### ②JSONの括弧を見やすくする拡張機能

ついでにJSONの括弧に色を付けてくれる拡張も入れましょう。
https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer-2


①②の拡張インストール前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/c05383ff-8d86-c508-48d6-faa49e39080b.png)

インストール後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/7f3e71b6-15db-e2d9-64f6-0760039df6aa.png)

圧倒的見やすさ…！！
生のJSONは人間が読むものじゃないと思っていますが、だいぶマシになりました。

#### ③ARMテンプレート関数の出力値チェックに使える拡張機能

Azure ARM Template Helper
https://marketplace.visualstudio.com/items?itemName=ed-elliott.azure-arm-template-helper

使うためには準備が必要で、ARMテンプレートのリポジトリのルートディレクトリに
.vscode/armd.jsonを作成しましょう。
以下のように、**テンプレートファイルからの相対パス** でParamaters.jsonへのパスを指定します。

```
{
    "parameterFile": "***/***/parameters.json"
}
```

その後、VS Code再起動すると、
コマンドパレット(Ctrl+Shif+P)から以下の操作が可能になります。
![Items (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/51c3f79a-dce3-644f-bb9b-e745b9d16050.png)

ARM:TestExtentionを選択して、関数を入力すると、その出力を確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/a21cfd33-2c92-2028-0c5e-f70aea66d1e9.png)

```
例）[concat('hoge-', parameters('environment'), '-webapps')] evaluated to: hoge-staging-webapps
```

関数周りは、ARMテンプレートで最もエラー原因になりやすいです。
parameters,variabels,concat,reference,list**あたりの関数を多用すると複雑になるので、結構重宝します。


## ３．Variablesのうまい使い方

インフラコードのツールにはよくあるものですが、
ARMテンプレートにも`"variables":{}`という変数を定義・加工できるスコープがあります。

Azureポータルから既存のリソースをARMテンプレートとして出力した場合は、
variablesが使われることはなく冗長な書き方で出力されます。

分かりやすいテンプレートを書くためには、変数を上手くまとめる必要がありますが、
関連する変数（同じリソースのもの）を階層でグルーピングしてあげると分かりやすいです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/e4c67c46-472d-e5c6-ef7b-200bd635078b.png)

階層化された変数は、以下のように上の階層から"."で繋げることで参照できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/620afb22-80a5-889e-807d-da87d7eaff0c.png)

こうすることで変数名も短くなりますし、
Resource側の設定を見た時も直感的で分かり易いと思います。

## ４．リファレンスやポータルを参考にしてlistKeys関数を使う

参照系の関数は、ARMテンプレートを使っていて一番つまづくところだと思います。
ややこしくて嫌になりますが、避けて通れないところでもあります。

listKeys関数は、すでに存在するリソースのプロパティなどを取得する関数です。
よくあるユースケースとしては、リソースの接続文字列を取得してKeyVaultやAppServiceの環境変数に埋め込むときに使います。

ここではNoritficationHub/Namespaceの接続文字列を取得して、
AzureFunctionのアプリケーション設定(環境変数)に設定するまでの流れを説明します。

#### ①ポータルで取得したい値を確認

まずは今回取得したい値をポータル上で確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/aa881301-1b1b-f817-e8a9-c4b7dbe3593e.png)

このConnectionStringのPrimaryキーをAppFunctionで使いたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/bf6360b2-c067-bf45-898f-1c07b80dee03.png)

#### ②ポータル上でARMテンプレートに出力して、対象のリソース名を確認

ポータル画面から既存のリソースをテンプレートとして出力します。
超分かりづらいですが、NotificationHubを構成するリソースは以下の通りだということが確認できます。

- Microsoft.NotificationHubs/namespaces
  - Microsoft.NotificationHubs/namespaces/AuthorizationRules (RootManageSharedAccessKey)
  - Microsoft.NotificationHubs/namespaces/notificationHubs
     - Microsoft.NotificationHubs/namespaces/notificationHubs/authorizationRules (★DefaultFullSharedAccessSignature)
     - Microsoft.NotificationHubs/namespaces/notificationHubs/authorizationRules (DefaultListenSharedAccessSignature)

今回はこの★マークのリソースが発行する接続文字列をlistKeys関数で取得します。


#### ③listKeysに関するリファレンスを確認

https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/resource-group-template-functions-resource#list

listKeysに対応しているリソースの一覧にNotificationHubがあるので、問題なく使えそうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/e73922be-5ef2-f49b-1646-e5367d80babe.png)

`primaryKeyConnection`という値で取れそうなことが分かります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/36e37dea-c976-1e87-2983-adcef33a360a.png)


#### ④テンプレートの実装

listKeysは以下のような引数を指定します。

```
listKeys(resourceName or resourceIdentifier, apiVersion, functionValues)
```

Parametersを直接参照して、文字連結などしても良いですが、
私の場合は以下のようにResourceIdやApiVersionをVariablesに定義してしまいます。
後々他のリソースで同様の値を使うこともあり得ますし、何よりlistKeys内のネストが深くなることを避けています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/8f72b495-2efb-6392-1fe4-47e6a81aabab.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/79236/beb66024-8c00-a78c-956a-365eaaa41135.png)

これでもかなり長ったらしいですが、パッと見て理解しやすいと思います。


## ５．デバッグ方法

ARMテンプレートを実行する方法は大きく分けて3つほどあります。

- ローカルのCLIまたはCLoudShellからコマンド実行
- Azureポータル上のデプロイ画面にテンプレートとパラメータを貼り付けて実行
- AzureDevOpsのReleaseパイプラインによる実行


ARMテンプレートで運用していてキツいのが、エラー発生時です。
エラーが発生している行数などが分からないこともあり、原因が特定ができないときがあります。

上記3つはエラーの出方や詳細度が結構違うので、
ローカルから実行したけどエラーが全然分からん！という時は、ポータルから実行してみたりするとヒントが得られることもあるので試してみると良いと思います。

最近はエラーに関する公式ドキュメントも揃ってきたので、こちらも参考すると良いでしょう。
https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/resource-manager-common-deployment-errors


## まとめ

なるべく公式ドキュメントに載っていないことを書いたつもりですが、参考になれば嬉しいです。
ARMテンプレートは2年ほど使ってますが、正直アップデートが少ない印象があったので今回の新機能発表はかなりテンション上がりました。

あとはYAML対応するか、AWS CDKのようなラッパーツールが登場してくれれば、他のIaCツールに負けない強力なものになると思います。