クラウドで、アジリティを手に入れよう。
column

コラム

ARMテンプレート(基礎編)

はじめに

こんにちは。クラウドCoEの長塚です。

今回はAzure Resource Manager(ARM)テンプレートについて解説します。

私が携わっていた案件でAzure基盤構築に利用することになり、ARMテンプレートについて勉強した内容をまとめたものになります。

後半は簡単なチュートリアルもありますので、是非お試しください!

IaCとは

ARMテンプレートの解説に入る前に、Infrastructure as Code(IaC)について簡単にご説明します。

IaCとは、インフラのアーキテクチャや設定パラメータの詳細をコード上に定義することです。(インフラのコード化)

IaC導入の意義としては「IaCを活用することでエンジニアの労働生産性を向上し、より高次の作業に時間を割けるようにすること」が挙げられます。

IaCの具体的なメリットは以下の通りです。

  • 同一のインフラを複数の環境に繰り返し構築することができる。(冪等性の保持)
  • 人為的な設定ミスを事前に防ぐことができる。
  • インフラコードの共有やバージョン管理ができる。

一方で、IaCのデメリットは以下が挙げられます。

  • 初めて構築するインフラのコード作成には時間が掛かる。
  • 簡単な設定変更に対しては適用にむしろ時間が掛かる。
  • 学習コストが大きい。

本ブログの別の記事でIaCについて詳しく記載していますので、ぜひご一読ください。

Infrastructure as Codeを理解する(第1回)

ARMテンプレートとは

ARMテンプレートは内部的にAzure Resource Managerを利用してデプロイを行っています。

まずAzure Resource Managerの概要を説明したうえでARMテンプレートの解説を行っていきます。

 

Azure Resource Manager (ARM)とは

Azure Resource Managerとは、Azure リソースをデプロイし管理するためのAzure独自のサービスです。 

Azure アカウント内のリソースを作成、更新、および削除できる管理レイヤーを提供します。

アクセス制御、ロック、タグなどの管理機能を使用して、デプロイ後にリソースを保護および整理することができます。

 

ARMテンプレートとは

ARMテンプレートとは、AzureソリューションでIaCを実装するために利用されるJSON形式のテンプレートファイルです。

ARMテンプレートにはシステムインフラストラクチャの構成や設定が定義されており、ARMテンプレートをデプロイすることでそれらのリソースを指定の環境に構築することができます。

ARMテンプレートは宣言型の構文を使用しているため、プログラミングコマンドの記述が不要です(一部プログラミングコードのような記述方法もあります)。

 

JSONとは別にAzure独自のファイル形式であるBicepファイルも提供されています。

BicepファイルはJSONよりも記載内容の視認性が高く、コーデンングもしやすいように設計されています。

Bicep ファイルは、デプロイ中に自動で ARM テンプレートに変換されます。

注意点として、Azureリソースやリソースの設定の中にはBicepでは対応していないものがありますので事前にドキュメントのご確認をお勧めします。

 

ARMテンプレートのデプロイの際は、リソースの依存関係を考慮してデプロイが調整され、正しい順序で処理が実行されます。 また、並列デプロイに対応しているため比較的短時間でデプロイを完了することができます。

デプロイ実行後は、Azure portal上でテンプレートのデプロイ履歴やデプロイの詳細情報を確認できます。デプロイされたテンプレートの内容やパラメータ値、実行時のログ、およびデプロイ処理に関する出力値を確認することができます。

 

テンプレートの構成 (例)

ここではARMテンプレートの基本的な構成をご説明します。

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

ARMテンプレートは、上記のようにいくつかのセクションに分かれていますが、今回は利用頻度の高いparameters, variables, resources, outputについて解説いたします。

$schemaはテンプレートの形式を指定しますが、お作法としてとりあえず記載しておくようにしてください。$schemaの詳細はこちらをご参照下さい。

 

parameters

テンプレートへの入力を定義します。デプロイ時に値を指定することで、同じテンプレートを異なる環境で使用できるようになります。

扱える型としては、規定値(defaultValue)と選択肢(allowedValues)があり、前者はAzure portal画面上の初期値として、後者はAzure portal画面上のプルダウンとして表示されます。

また、parameters内のmetadata.descriptionにパラメータの説明を記述することで、GUIでの設定画面で説明を表示させるとができます。利用可能な型としては、string, int, bool, array, secureString, secureObjectがあります。詳細はこちらをご覧ください

 

variables

テンプレートで再利用可能な変数を定義します。ユーザからの入力で定義するのではなく、主にparametersから値を受け取って定義します。

また、関数を利用してvariablesの値の生成が可能です。parametersと同様の型が利用可能です。

 

resources

Azure上に構築するリソースの定義をします。

リソース間の依存関係の定義をすることでデプロイの前後関係を考慮した上でResource Managerが自動でデプロイを実行してくれます。

繰り返し(copy)や、条件(condition)の処理を定義することで、柔軟かつ可読性の高いインフラコードを記述することができます。

リソース毎に設定できる項目はこちらのリファレンスをご覧ください。

 

resourcesの記載例

以下のテンプレートではAzure ストレージアカウントをデプロイする設定を記載しています。

"resources": [
  {
    "type": "Microsoft.Storage/storageAccounts",
    "apiVersion": "2019-06-01",
    "name": "[parameters('storageAccountName')]",
    "location": "[parameters('location')]",
    ...
  }
]

 

output

デプロイ処理後の出力を定義し、リソースのURLや接続文字列などのデプロイ結果の情報を取得することができます。

parametersとほぼ同じ型を利用できます

 

今回は詳細の記述は割愛させていただきますが、その他のARMテンプレートの特徴として以下のようなものが挙げられます。

  • テンプレートをネストさせることで、リソース間の依存関係の視認性を向上させることができる。
  • テンプレート内で別のテンプレートをリンクさせることができる。GithubやAzureストレージ上に配置した設定内容を記載したテンプレートをリンクさせることで、インフラコードの見通しが良くなり、パラメータ値の管理をセキュアかつ効率的に行うことができる。
  • モジュール分割することができる。システムの規模が大きくなった際に機能や領域別にテンプレートを分割することで保守性を向上させることができる。

実務レベルでのARMテンプレートの利用の場合はこれらの機能を活用するシーンが多いのではないでしょうか。

ARMテンプレートの使い方・チュートリアル

ここからはAzure公式ドキュメントに掲載されているチュートリアル沿ってARMテンプレートのデプロイ方法について解説します。

このチュートリアルはサーバレスWebアプリケーションをARMテンプレートを利用してデプロイするものになります。

 

チュートリアル環境の構成について

チュートリアルで構築するWebアプリの構成は以下の図のようになります。

図はAzure portalのリソースビジュアライザーで自動生成しました。

 

Webアプリを構成するリソースは以下の通りです。

  • Application Insights
    • アプリケーションのパフォーマンスを監視・管理できるAzureのサービスです。
  •  ストレージアカウント
    • Azureの4つのストレージサービスの管理単位です。Blob, Files, Queue, Tableがあります。
  • App Service
    • Azureで提供している4つのアプリケーションサービスの総称です。Web Apps, Mobile Apps, Functions, API Appsがあります。
  • App Service プラン
    • App Serviceを動かすためのVMリソースの総称です。

 

環境構築

Azureサブスクリプション

まず初めに、Azureにリソースをデプロイするための環境構築を行います。

Azureでは無料アカウント(サブスクリプション)を利用することができますので、アカウントがない方はこちらから作成してください。

もし無料アカウントと別に新しくアカウントを作る必要がある場合はこちらをご参照ください。

従量課金制のサービスのみを利用するため、無料アカウントを利用できなくても試しに触る程度でしたら基本的に料金は発生しないと思います。(保証はできません)

 

コマンドラインツール

デプロイの実行はローカルのターミナルかAzure portal上から行うことができます。

Azure portal上から実行する場合は、GUIを利用する場合とCloud Shellを利用する場合に分けられます。

 

前者は画面にテンプレートファイルをアップロードもしくは内容をペーストしてボタンを押すだけで、コマンドなしでデプロイができます。

後者はセキュアかつほぼ無料でリモートのShellからデプロイコマンドの実行ができます。

 

ローカル環境の構築が不要な点がAzure portalから実行する際の主なメリットになりますが、テンプレートを編集するたびにアップロードが必要になります。

ローカルの環境構築は大して手間もかからないですし、テンプレートの改修とデプロイを短時間で行えるため、今回はローカル環境で進めていきたいと思います。

 

ローカル環境で利用するデプロイツールとしては、Azure CLIAzure Powershellがあります。

どちらもWindows, Mac OS, Linuxに対応しており機能面で大きな優劣はありません。

そのため、普段からPowershellに使い慣れている方はAzure Powershell、そうでない方はAzure CLIを使うのが良いと思います。(ただし、Azure Powershellを利用する場合は自分の環境にPowershellが導入されている必要があります。)

 

インストール手順はAzure CLIインストール手順もしくはAzure Powershellインストール手順ご参照ください。

 

テキストエディタ

テキストエディタにはVisual Studio Codeを使うことをお勧めします。

動作が軽く、さまざまな拡張機能を簡単に導入することができます。

また、Microsoftが提供しているエディタのためAzure関連の拡張機能が充実しています。

VS Codeインストール手順

 

拡張機能は「Azure Resource Manager (ARM) Tools」をインストールしてください。

こちらの拡張機能は、補完候補の表示や静的解析によるエラー表示をしてくれます。

拡張機能のインストール手順

 

テンプレートファイルの作成

ここからはARMテンプレートを作成し、実際にAzure上にリソースをデプロイしていきます。

テンプレートの作成方法は0から自分で作成する方法と既存のリソースを利用する方法に大きく分けられます。

ただし、特に大きな規模のインフラになる場合、テンプレートを一から作成するのは非常に骨の折れる作業のためあまりおすすめしません。できる限り既存のリソースを活用するのが良いでしょう。

 

既存のリソースを利用する方法としては、Microsoftが運営するコミュニティから提供されているクイックリファレンステンプレートを利用する場合と、既存のインフラからエクスポートする方法があります。

既に他プロジェクトで類似のシステムがあるのであればそこからエクスポートして編集するのが良いかもしれません。

今回はチュートリアルでも使用されているクイックリファレンステンプレート上のテンプレートをコピーして使用します。

 

まず、今回のチュートリアル用のプロジェクトフォルダを作成し、ディレクトリを移動します。

mkdir function-app-create-dynamic && cd "$_"

 

次にテンプレートファイルを作成します。

touch azuredeploy.json

 

クイックリファレンステンプレートからソースコードを azuredeploy.json にコピー&ペーストします。

コピー&ペーストするソースコードは以下になります。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.5.6.12127",
      "templateHash": "10848576439099634716"
    }
  },
  "parameters": {
    "appName": {
      "type": "string",
      "defaultValue": "[format('fnapp{0}', uniqueString(resourceGroup().id))]",
      "metadata": {
        "description": "The name of the function app that you wish to create."
      }
    },
    "storageAccountType": {
      "type": "string",
      "defaultValue": "Standard_LRS",
      "allowedValues": [
        "Standard_LRS",
        "Standard_GRS",
        "Standard_RAGRS"
      ],
      "metadata": {
        "description": "Storage Account type"
      }
    },
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]",
      "metadata": {
        "description": "Location for all resources."
      }
    },
    "appInsightsLocation": {
      "type": "string",
      "metadata": {
        "description": "Location for Application Insights"
      }
    },
    "runtime": {
      "type": "string",
      "defaultValue": "node",
      "allowedValues": [
        "node",
        "dotnet",
        "java"
      ],
      "metadata": {
        "description": "The language worker runtime to load in the function app."
      }
    }
  },
  "variables": {
    "functionAppName": "[parameters('appName')]",
    "hostingPlanName": "[parameters('appName')]",
    "applicationInsightsName": "[parameters('appName')]",
    "storageAccountName": "[format('{0}azfunctions', uniqueString(resourceGroup().id))]",
    "functionWorkerRuntime": "[parameters('runtime')]"
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2021-08-01",
      "name": "[variables('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[parameters('storageAccountType')]"
      },
      "kind": "Storage"
    },
    {
      "type": "Microsoft.Web/serverfarms",
      "apiVersion": "2021-03-01",
      "name": "[variables('hostingPlanName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "Y1",
        "tier": "Dynamic"
      },
      "properties": {}
    },
    {
      "type": "Microsoft.Web/sites",
      "apiVersion": "2021-03-01",
      "name": "[variables('functionAppName')]",
      "location": "[parameters('location')]",
      "kind": "functionapp",
      "identity": {
        "type": "SystemAssigned"
      },
      "properties": {
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
        "siteConfig": {
          "appSettings": [
            {
              "name": "AzureWebJobsStorage",
              "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2021-08-01').keys[0].value)]"
            },
            {
              "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
              "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};AccountKey={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2021-08-01').keys[0].value)]"
            },
            {
              "name": "WEBSITE_CONTENTSHARE",
              "value": "[toLower(variables('functionAppName'))]"
            },
            {
              "name": "FUNCTIONS_EXTENSION_VERSION",
              "value": "~2"
            },
            {
              "name": "WEBSITE_NODE_DEFAULT_VERSION",
              "value": "~10"
            },
            {
              "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
              "value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))).InstrumentationKey]"
            },
            {
              "name": "FUNCTIONS_WORKER_RUNTIME",
              "value": "[variables('functionWorkerRuntime')]"
            }
          ],
          "ftpsState": "FtpsOnly",
          "minTlsVersion": "1.2"
        },
        "httpsOnly": true
      },
      "dependsOn": [
        "[resourceId('Microsoft.Insights/components', variables('applicationInsightsName'))]",
        "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]",
        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
      ]
    },
    {
      "type": "Microsoft.Insights/components",
      "apiVersion": "2020-02-02",
      "name": "[variables('applicationInsightsName')]",
      "location": "[parameters('appInsightsLocation')]",
      "kind": "web",
      "properties": {
        "Application_Type": "web",
        "Request_Source": "rest"
      }
    }
  ]
}

 

VS Codeの拡張機能を利用してパラメータファイルを作成します。

テンプレートファイル上で右クリックし、[Select/Create Parameter File…]をクリックします。

 

[New]をクリックします。

[Only required parameters]をクリックします。テンプレートの記載内容を基に、設定が必要なパラメータのみパラメータファイルに自動で記載してくれます。

 

パラメータファイル名に「azuredeploy.parameters.json」と入力し[保存]をクリックします。

 

パラメータファイルが作成されました。赤になっている箇所はパラメータの値を設定する必要がある箇所です。こちらも拡張機能によるエラー表示になります。

 

パラメータに値を設定します。

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "appInsightsLocation": {
            "value": "japaneast"
        }
    }
}

 

デプロイ

ここからは作成したテンプレートをAzureにデプロイしていきます。

まずAzureにサインインします。

az login

 

サインイン画面が表示されたらチュートリアルで利用するアカウントをクリックします。

 

このような画面が表示されたらサインイン成功です。こちらの画面は閉じて大丈夫です。

 

サインインに成功すると以下のようなログがターミナル上に表示されます。

サインインしたアカウント(ユーザ)に紐づくサブスクリプションが一覧で表示されます。

% az login
A web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. Please continue the login in the web browser. If no web browser is available or if the web browser fails to open, use device code flow with `az login --use-device-code`.
[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "xxxxxxxxxxx",
    "id": "xxxxxxxxxxx",
    "isDefault": true,
    "managedByTenants": [],
    "name": "Azure サブスクリプション 1",
    "state": "Enabled",
    "tenantId": "xxxxxxxxx",
    "user": {
      "name": "xxx@xxx.com",
      "type": "user"
    }
  }
]

 

サインインしたアカウントに紐づく有効なアカウントの一覧を取得します。

% az account list -o table
Name                    CloudName    SubscriptionId                        TenantId                              State    IsDefault
----------------------  -----------  ------------------------------------  ------------------------------------  -------  -----------
Azure サブスクリプション 1       AzureCloud   xxxxxxxxxxxxxxxxx  xxxxxxxxxxxxxxxxx  Enabled  True

 

一覧からサブスクリプションIDを取得し、操作を行う先(デプロイ先)として指定します。

az account set <subscription-id>

 

Azureリソースをデプロイするにはリソースグループという管理単位が必要になります。

ここで先ほど指定したサブスクリプションにリソースグループを作成します。

az group create -n <resource-group-name> -l <resource-group-location>

 

% az group create -n demo-rg -l japaneast
{
  "id": "/subscriptions/xxxxxxxxxxxx/resourceGroups/demo-rg",
  "location": "japaneast",
  "managedBy": null,
  "name": "demo-rg",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}

 

作成したリソースグループにリソースをデプロイします。

az group deployment create -g <resource-group-name> --template-file ./azuredeploy.json --parameters azuredeploy.parameters.json

 

ターミナル上で以下の表示がされていることが確認できるとデプロイ成功になります。

...
"provisioningState": "Succeeded",
...

 

リソースが作成されたか以下コマンドを実行して確認します。

az resource list -g <resource-group-name> -o table

 

リソースが作成できていることが確認できました。

% az resource list -g demo-rg -o table
Name                                    ResourceGroup    Location    Type                                                Status
--------------------------------------  ---------------  ----------  --------------------------------------------------  --------
Failure Anomalies - xxxxxxxxxxxxxxxxxx  demo-rg          global      microsoft.alertsmanagement/smartDetectorAlertRules
Application Insights Smart Detection    demo-rg          global      microsoft.insights/actiongroups
xxxxxxxxxxxxxxxxxx                      demo-rg          japaneast   Microsoft.Insights/components
xxxxxxxxxxxxxazfunctions                demo-rg          japaneast   Microsoft.Storage/storageAccounts
xxxxxxxxxxxxxxxxxx                      demo-rg          japaneast   Microsoft.Web/serverFarms
xxxxxxxxxxxxxxxxxx                      demo-rg          japaneast   Microsoft.Web/sites

 

Azure portal上でもリソースが作成されていることが確認できました。

 

なお、クイックリファレンステンプレートのGithubにあるソースコードから直接デプロイする場合は以下のコマンドになります。

az group deployment create --resource-group <my-resource-group> --template-uri https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.web/function-app-create-dynamic/azuredeploy.json

 

Webアプリ画面へのアクセス

続いてデプロイしたWebアプリにアクセスしてみます。

[種類]が[microsoft.web/sites]となっているリソースをクリックし、Functionsの詳細画面を開きます。

URLをクリック(コピー)し、ブラウザの別タブにペーストし開きます。

 
https://xxxxxxxxxxxxxx.azurewebsites.net

 

以下画面が表示されたら成功になります。

 

チュートリアルが完了したら、追加コストの発生を避けるため次のコマンドを実行してリソース グループとそこに含まれるすべてのリソースを削除してください。

az group delete -g <resource-group-name>

さいごに

チュートリアルではサーバレスWebアプリケーションを数行のコマンドを叩くだけで簡単にデプロイすることができました。

また、クイックリファレンステンプレートやVS Codeの拡張機能を活用することで、テンプレートの作成も短時間で行うことができることが実感いただけたのではないかと思います。

IaCはメリットが大きい一方で学習コストや初期構築時の工数がネックになっていましたが、Azureではドキュメントや無料で使える学習環境の整備(特にMicrosoft Learn)、テンプレート作成支援サービスの提供によってIaCのデメリットを抑えつつメリットを享受できるような工夫がされていると感じました。

 

今回はARMテンプレートの基礎的な内容を解説しましたが、大規模なインフラのためのテンプレート記載方法やCI/CDへの応用といった実業務に役立つ便利な機能がまだまだありますので、そちらについてはまた別の回でご紹介したいと思います。

以上、少しでもお役に立てたようでしたら幸いです。

最後までお読みいただきありがとうございました!

RECOMMEND

おすすめ記事一覧