【簡単】ARM ToolsとARM Template Viewerで簡単にAzureでInfrastructureAsCodeをはじめる

## はじめに

ARM Templateを実装するにあたってVisualStudioCodeの拡張機能を使えば驚くほど簡単だったため、実際にやってみて分かった注意点を含めて備忘録として残します。誰かの助けになれば幸いです。

## 準備

* [VisualStudioCode](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)をインストールします
* [拡張機能: Azure Resource Manager (ARM) Tools](https://marketplace.visualstudio.com/items?itemName=msazurermtools.azurerm-vscode-tools)をインストールします
* [拡張機能: ARM Template Viewer](https://marketplace.visualstudio.com/items?itemName=bencoleman.armview)をインストールします

## Azure Resource Manager (ARM) Toolsを使う

さっそくARM Templateをファイル template.json(任意の名前でもOK)として作成します。
ARM Toolsはインストールしてあれば特別な設定必要なく使用可能です。
そのままコードにキーワードを打てばARM Templateの記載に必要なあらゆることが簡単に実装できるようになっています。
本記事では以下を実施します。

* ARM Templateのお決まりのテンプレートを生成する
* ストレージアカウントを構築する定義を実装する
* AppServicePlanを構築する定義を実装する
* functionAppを構築する定義を実装する

### ARM Templateのお決まりのテンプレートを生成する

非常にシンプルで`arm`と打ち込むだけです。以下のようにARM Templateでまず記載すべきスケルトンが生成できます。

<img width="876" alt="スクリーンショット 2020-01-26 14.59.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/65cc99ee-0308-73b4-fd31-2df7837c5984.png">

<img width="1002" alt="スクリーンショット 2020-01-26 14.59.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/fed78577-b5e9-40e6-8118-3d8420d4feff.png">

ここではfunctionsは使わないため生成したスケルトンから定義は削除します。

### ストレージアカウントを構築する定義を実装する

構築するリソースはresources定義の中に定義します。
`armstorage`を打ち込むだけでスケルトンと同様ストレージアカウントの定義が生成できます。

<img width="1002" alt="スクリーンショット 2020-01-26 15.10.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/61749b79-b44e-c6f7-7194-9cdb189f090f.png">

瞬時に以下まで実装してくれます。

<img width="1002" alt="スクリーンショット 2020-01-26 15.11.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/20f20f14-d22a-ad6a-cf9a-3a5bb3abc932.png">

基本的にはARM Toolsが生成する定義で良いですが、以下に注意して[リファレンス](https://docs.microsoft.com/en-us/azure/templates/)を見ながらコードを修正します。

* apiVersionの修正
    * 上記では`2015-06-15`ですが[StorageAccountのリファレンス](https://docs.microsoft.com/en-us/azure/templates/microsoft.storage/2019-04-01/storageaccounts)では`2019-04-01`のため修正します

更新したapiVersionに従い、定義を修正します。ARM ToolsでWARNINGが出るのでそれを修正すればOKです。
WARNINGによるとskuとkindの定義を追加します。

<img width="1002" alt="スクリーンショット 2020-01-26 15.24.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/797662e5-1b28-eaf9-ad00-d56f3b9ac65f.png">

### AppServicePlanを構築する定義を実装する

* 上記同様に`armplan` と打ち込みAppServicePlanの定義を生成します。
* 要領はStorageAccountと同じため詳細は省略します。

### functionAppを構築する定義を実装する

* 同じく `armfunction` と打ち込みfunctionAppの定義を生成します
* 生成後、AppServicePlanとStorageAccountの紐付けである`dependsOn`定義を修正します
* `properties.siteConfig.appSettings.WEBSITE_RUN_FROM_PACKAGE`を定義し値を1とします
    * functionをzipでデプロイする時に必須となります

## 完成したARM Template

上記で説明した修正点に加え、parameters等リファクタリングした後のARM Templateは以下になります。

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "suffix": {
            "type": "string",
            "defaultValue": "2020012601"
        },
        "storageAccountSKU": {
            "type": "string",
            "defaultValue": "Standard_LRS",
            "allowedValues": [
                "Standard_LRS",
                "Standard_GRS"
            ]
        },
        "storageAccountKind": {
            "type": "string",
            "defaultValue": "StorageV2"
        },
        "appServicePlanSku": {
            "type": "string",
            "defaultValue": "D1",
            "allowedValues": [
                "D1",
                "B1"
            ]
        }
    },
    "variables": {
        "storageAccountName": "[concat('stracc', parameters('suffix'))]",
        "appServicePlanName": "[concat('appsvr', parameters('suffix'))]",
        "functionAppName": "[concat('funcapp', parameters('suffix'))]"
    },
    "resources": [
        {
            "name": "[variables('storageAccountName')]",
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-04-01",
            "location": "[resourceGroup().location]",
            "tags": {
                "displayName": "[variables('storageAccountName')]"
            },
            "sku": {
                "name": "[parameters('storageAccountSKU')]"
            },
            "kind": "[parameters('storageAccountKind')]"
        },
        {
            "name": "[variables('appServicePlanName')]",
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2018-02-01",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "[parameters('appServicePlanSku')]",
                "capacity": 1
            },
            "properties": {
                "name": "[variables('appServicePlanName')]"
            }
        },
        {
            "apiVersion": "2019-08-01",
            "type": "Microsoft.Web/sites",
            "name": "[variables('functionAppName')]",
            "location": "[resourceGroup().location]",
            "kind": "functionapp",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
            ],
            "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
                "siteConfig": {
                    "appSettings": [
                        {
                            "name": "AzureWebJobsDashboard",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')),'2015-05-01-preview').key1)]"
                        },
                        {
                            "name": "AzureWebJobsStorage",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')),'2015-05-01-preview').key1)]"
                        },
                        {
                            "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')),'2015-05-01-preview').key1)]"
                        },
                        {
                            "name": "WEBSITE_CONTENTSHARE",
                            "value": "[toLower(variables('functionAppName'))]"
                        },
                        {
                            "name": "FUNCTIONS_EXTENSION_VERSION",
                            "value": "~2"
                        },
                        {
                            "name": "FUNCTIONS_WORKER_RUNTIME",
                            "value": "dotnet"
                        },
                        {
                            "name": "WEBSITE_RUN_FROM_PACKAGE",
                            "value": "1"
                        }
                    ]
                }
            }
        }
    ],
    "outputs": {
        "functionName": {
        "type": "string",
         "value": "[variables('functionAppName')]"
        }
    }
}
```

## ARM Viewerによる確認

VisualStudioCodeのウィンドウ右上の目のマークをクリックすると描画できます。

![スクリーンショット 2020-01-26 13.43.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/4cb843fd-6a54-2a9b-b75f-72efb46acfa5.png)

以下が表示できます。

![スクリーンショット 2020-01-26 13.20.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/21e617a8-141b-f133-2f7f-fca4aa787c3f.png)

以下のリソースが生成される理解となります。

* sites:functionApp
* serverfarms: AppServicePlan
* storageaccounts: そのまま

## ARM Templateをデプロイする

AZ CLIでは以下のようにデプロイできます。[Azure Portalでもデプロイ](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/templates/quickstart-create-templates-use-the-portal)できます。

```bash
$ az group deployment create -g <ResourceGroupName> --template-file template.json
```

これでARM Viewerで確認した構成がAzure Portalで確認できます。

<img width="768" alt="スクリーンショット 2020-01-26 13.31.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/e29c700f-de9e-247f-384b-79b0b2db4498.png">

## サンプルfunctionをデプロイ

### azure function coreをインストール

インストール方法は[こちらの記事](https://qiita.com/kita-atsushi/items/765937071cb3eccb78f5)を参照してください。

### dotnet sdkをインストール

dotnet functionを生成するにあたり、.net core sdkが必要となります。
今回、なぜか生成するHttp triggerテンプレートはdotnet2.1しかサポートされていないみたいなので、.net core SDK 2.1をあらかじめインストールします。
[こちらを参考にすると](https://kledgeb.blogspot.com/2018/06/net-core-23-ubuntunet-core-210deb.html)以下のコマンドとなります。

```bash
$ wget -nv https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb

$ sudo apt-get install apt-transport-https
$ sudo apt-get update
$ sudo apt-get install dotnet-sdk-2.1

$ sudo rm -f packages-microsoft-prod.deb 
```

### dotnet functionを生成

```bash
$ func init MyFunctionProj --dotnet
```

### functionをデプロイ

`func azure functionapp publish <function app name>` でデプロイします。

```
root@vagrant:/vagrant/arm-templates/functions/MyFunctionProj# func azure functionapp publish funcapp2020012601
Microsoft (R) Build Engine version 16.4.0+e901037fe for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 35.66 ms for /vagrant/arm-templates/functions/MyFunctionProj/MyFunctionProj.csproj.
  MyFunctionProj -> /vagrant/arm-templates/functions/MyFunctionProj/bin/publish/bin/MyFunctionProj.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.69


Getting site publishing info...
Creating archive for current directory...
Uploading 3.44 MB [###############################################################################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in funcapp2020012601:
    HttpTrigger - [httpTrigger]
        Invoke url: https://funcapp2020012601.azurewebsites.net/api/httptrigger?code=xxxxxxxxxx
```

### デプロイしたfunctionを確認する

Azure ポータルからデプロイしたfunctionを確認します。
HttpTriggerを確認できたら正しくデプロイされています。

<img width="768" alt="スクリーンショット 2020-01-26 13.35.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/133866/28275603-bfb5-7cd2-5081-78d1cd6a669c.png">

以上