[検証]Azure専用プログラミング言語Bicepを試してみた~Infrastructure as Code~#Azure リレー
2021-05-25
azblob://2022/11/11/eyecatch/2021-05-25-bicep-infrastructure-as-code-000.jpg

FIXER藤井です。今回は2021年5月25日(火)のAzure リレーとして、2021年5月25日現在、プレビューとして提供されているAzure の新機能 Bicepについて、検証した結果を報告します。BicepとはAzure において"Infrastructure as Code"を実現するための「専用のプログラミング言語」とでも言うべき存在です。

Azure におけるInfrastructure as Codeについて

具体的なBicepの検証内容の入る前に、そもそも「Infrastructure as Code」とは何か、そしてAzure における「Infrastructure as Code」について説明します。

Infrastructure as Codeとは

「Infrastructure as Code」とは各リソースの構成および依存関係に関する情報を、「プログラミング」のような形式で記述し、また実際の環境に適用させることで、ITインフラをあたかもアプリケーションのような感覚で構築および管理する手法のことです。「Infrastructure as Code」により以下のようなメリットが期待されます。

  • ITインフラの構築や運用を自動化することによるコストの削減
  • 人的作業の排除によるヒューマンエラーの防止
  • 作業内容が「ソースコード」として残ることによる作業履歴の管理および作業の再現性の確保

Azure におけるInfrastructure as Codeの歴史

Bicepは初めて独自形式が採用された点で、歴史の転換点と言えます。今までAzureにおいて、Microsoftの「純正品」として提供されている「Infrastructure as Code」の機能は、いずれXMLやJsonといった非常に一般的なデータ形式が利用されてきました。

  • ASMテンプレート:XML形式です。Azureの初期バージョンであるASM(クラシックモデル)という区分のサービスを対象にしています。
  • ARMテンプレート:Json形式です。現在のAzureの中心であるARM(Azure Resource Manager)という区分のサービスを対象にしています。ARMテンプレートに関しては拙記事「Tech Summit 2018: Linuxユーザが扱うAzure Resource Manager Templateの活用方法」でも取り上げています。

Bicep vs ARMテンプレート

すでにARM テンプレートが「純正品」として提供されているにも関わらず、新たにBicepが提供される背景は、Microsoft 公式ドキュメントの以下の記載に集約されています。

テンプレートを作成するための JSON 構文は冗長になることがあり、複雑な式を必要とします。 Bicep であれば、JSON テンプレートの各種機能を失うことなくエクスペリエンスを向上させることができます。 ARM テンプレートのために JSON をわかりやすく抽出したものです。 各 Bicep ファイルは、標準の ARM テンプレートにコンパイルされます。 

https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/templates…

自分自身も過去のプロジェクトで何回かARMテンプレートの作成をがっつりとやったことがあるのですが、とても職人的に忍耐を要求される作業です。数百行~数千行のネスト構造が複雑に入り組んだJsonファイルを、編集する必要が有ります。一か所でもタイポが有ればエラーになるのですが、ブレークポイントのようなものも貼れず、エラーログとJsonの一行一行を「目グレップ」で突き合わせることになります。

Azure 界隈でもARM テンプレートをやる人間はかなり少数派で、大部分のエンジニアはサードパーティ製品のTerraformを使うのが主流です。以前にFIXER内でベテランエンジニアと議論したときの結論とは、「ARMテンプレートとは、C言語などの高級言語が発明される前の時代におけるアセンブラのようなもので、いずれは高級言語に相当するものが提供されるだろう」というものでした。今まさにその「高級言語」に相当するのが「Bicep」です。

検証について

検証の流れ

今回の検証ではBIcepファイルを新規で作成するのではなく、すでに作成済みのARMテンプレレートを使用して、以下の2つを試してみます。

  • 検証(1)ARMテンプレートからBicepへのファイルの変換
  • 検証(2)Bicepを利用したAzure リソースのデプロイ

検証用ARMテンプレートについて

検証用ARMテンプレート作成の観点

ARMテンプレートはサンプルがGitHub上に多数公開されていますが、以下の観点により検証用のARMテンプレートを作成しています。

  • 観点(1)命名規則のコントロール:作成される各Azureリソースについて、共通のキーワードを接頭辞として付けること
  • 観点(2)依存関係のコントロール:リソース間に親子関係が存在するケースと、独立したリソース間に依存関係が存在するケースで構築ができること

検証で使用するARMテンプレートは以下の3種類のAzure リソースを作成します。

  • VIrtual Network(1個)
  • Subnet(1個)※上記VIrtual Networkに所属する
  • Network Security Group※上記Subnetに所属する

ARMテンプレートの詳細は下記です。l.7-l.8までの部分が「観点(1)命名規則のコントロール」に相当します。「観点(2)依存関係のコントロール」については、VIrtual Network&Subnetが「親子関係」に、Subnet&Network Security Groupが「独立したリソース間の依存関係」に相当します。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
  },
  "variables": {
    "PrefixKey1": "FIXER",
    "PrefixKey2": "blog",
    "PrefixName": "[concat(variables('PrefixKey1'), variables('PrefixKey2'))]",
    "Nsg": {
      "name": "[concat(variables('PrefixName'), '-Nsg')]"
    },
    "Vnet": {
      "name": "[concat(variables('PrefixName'), '-Vnet')]",
      "addressPrefixes": "192.168.0.0/16"
    },
    "Subnet": {
      "name": "[concat(variables('Vnet').name, '/', variables('PrefixName'), '-Subnet')]",
      "addressPrefix": "192.168.1.0/24"
    },
    "NsgId": "[resourceId('Microsoft.Network/networkSecurityGroups',variables('Nsg').name)]"
  },
  "resources": [
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/networkSecurityGroups",
      "name": "[variables('Nsg').name]",
      "location": "[resourceGroup().location]",
      "properties": {
        "securityRules": []
      }
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/virtualNetworks",
      "name": "[variables('VNet').name]",
      "location": "[resourceGroup().location]",
      "dependsOn": [
      ],
      "properties": {
        "addressSpace": {
          "addressPrefixes": [
            "[variables('VNet').addressPrefixes]"
          ]
        }
      }
    },
    {
      "apiVersion": "2015-06-15",
      "type": "Microsoft.Network/virtualNetworks/subnets",
      "name": "[variables('Subnet').name]",
      "dependsOn": [
        "[variables('Nsg').name]"
      ],
      "properties": {
        "addressPrefix": "[variables('Subnet').addressPrefix]",
        "networkSecurityGroup": {
          "id": "[variables('NsgId')]"
        }
      }
    }
  ],
  "outputs": {
  }
}

検証内容詳細

検証の準備:環境構築

検証に入る前に検証で必要なツールであるBicep CLIをインストールします。今回はWindows 端末でAzure PorwerShellの環境が必要になるのですが、Azure PorwerShellのインストール手順は省略します。

  1. Microsoft 公式サイトよりインストラーをダウンロードします。参照「Windows インストーラー
  2. インストーラーをダブルクリックで起動したら、利用規約に同意して「Next」をクリックします。
  3. インストール場所を選択し、「Next」をクリックします。
  4. 「Install」をクリックしてインストールを開始します。
  5. インストール完了が表示されたら「Finish」をクリックします。
  6. PowerShellにて「$ENV:Path.Split(;)」を実行し、Bicep CLIが追加されていることを確認できたら完了です。

検証(1)ARMテンプレートからBicepへのファイルの変換

  1. PoweShellにてカレントディレクトリを対象ARMテンプレートの保存パスに変更します
    cd %保存ディレクトリ%
  2. 以下のコマンドによりBicepファイルへの変換を実行します。
    bicep decompile 【ファイル名】
  3. 以下のようにPowerShell上で表示されればOKです。

実際に作成されたBicepファイルの内容は以下のようなものです。元のARMテンプレートでは65行だったのが、Bicepファイルでは46行まで圧縮されています。

また特にポイントとなるのはl.38-l.45のSubnetの作成に関わる部分です。元のARMテンプレートではl.48-l.61が対応する部分で、l.52-l.54の"dependsOn"にてネットワークセキュリティグループに対して依存関係にあることが明示的に記述する必要が有ったのが、Bicepでは自動的に処理されるようになっています。"dependsOn"とは「依存する対象のリソース(この場合はNetwork Security Grouop)の作成が完了するまで、依存する側のリソース(kの場合はSubnet)の作成開始を待機する」という意味です。

var PrefixKey1 = 'FIXER'
var PrefixKey2 = 'blog'
var PrefixName = concat(PrefixKey1, PrefixKey2)
var Nsg = {
  name: '${PrefixName}-Nsg'
}
var Vnet = {
  name: '${PrefixName}-Vnet'
  addressPrefixes: '192.168.0.0/16'
}
var Subnet = {
  name: '${Vnet.name}/${PrefixName}-Subnet'
  addressPrefix: '192.168.1.0/24'
}
var NsgId = Nsg_name.id

resource Nsg_name 'Microsoft.Network/networkSecurityGroups@2015-06-15' = {
  name: Nsg.name
  location: resourceGroup().location
  properties: {
    securityRules: []
  }
}

resource VNet_name 'Microsoft.Network/virtualNetworks@2015-06-15' = {
  name: Vnet.name
  location: resourceGroup().location
  properties: {
    addressSpace: {
      addressPrefixes: [
        Vnet.addressPrefixes
      ]
    }
  }
  dependsOn: []
}

resource Subnet_name 'Microsoft.Network/virtualNetworks/subnets@2015-06-15' = {
  name: Subnet.name
  properties: {
    addressPrefix: Subnet.addressPrefix
    networkSecurityGroup: {
      id: NsgId
    }
  }
}

検証(2)Bicepを利用したAzure リソースのデプロイ

Azure PowerShellを使用してBicepファイルをデプロイっします。Azure PowerShellを触ったことが無い方は手前味噌ですが、過去の拙記事「Azure PowerShell に初めて挑戦する人のための手引き①【入門編】」を併せてご参照ください。

  1. 以下のコマンドAzureにログインします。
    Login-AzAccount
  2. 以下のコマンドによりサブスクリプションを選択します。
    Select-AzSubscription -SubscriptionId 【サブスクリプションID】
  3. 以下のコマンドにより作成対象のリソースグループ名を指定します。
    $rg = 'bicep'
  4. 以下のコマンドにより東日本リージョンにリソースグループを作成します。
    New-AzResourceGroup -Name $rg -Location "japaneast"
  5. 以下の対象のBicepファイルのローカルのフルパス(ドライブ文字からファイル名まで全て)を指定します。
    $filepath = '【フルパス】'
  6. 以下のコマンドによりAzureのデプロイを実行します。
    New-AzResourceGroupDeployment -ResourceGroupName $rg -TemplateFile $filepath
  7. Azure Portalよりデプロイの成功を確認します。

最後に

Azure のInfrastructure as Codeを学ぶことは、Azureの構造の最も本質的な部分を理解することに通じます。藤井自身はAzure のMCPを5つ(Azure Solutions Architect Expert / DevOps Engineer Expert / Azure Administrator Associate / Azure Developer Asociate / Azure Security Engineer Associate)を持っていますが、実は試験を受けるときに、直接は正解を知らなかったとしても、ARM テンプレートの構造を思い浮かべると回答を類推できるケースが結構な割合で存在します。難解でとっつきにくいテーマではありますが、読者の皆さんもトライしてみてください。