Bicep でカスタマイズした AKS をデプロイする

Bicep の勉強も兼ねて、ユーザー割り当て Managed ID および、いくつかの機能を有効にした AKS をデプロイするテンプレートを作ってみました。

# 環境

* Windows 10
* Azure CLI (2.28.0)
* Bicep (0.4.1008)

## 準備

下記の手順を参考に、Azure CLI と Bicep をインストールしておきます。

https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/install

また、Bicep の文法などは下記の Microsoft Learn などを参考にしていただければと思います。

https://docs.microsoft.com/ja-jp/learn/paths/bicep-deploy/

# Bicep の中身

早速ですが、テンプレートを貼っておきます。

```bicep
// Usage : az deployment group create --resource-group <resource-group-name> --template-file <path-to-bicep>

// パラメータの定義

@description('The Azure region into which the resources should be deployed.')
param location string = resourceGroup().location

@description('for AKS Cluster Name & VNET Name Prefix')
param clusterName string = 'akslab-example-001'

@description('for AKS Cluster Managed Identity Name')
param managedIdName string = guid(clusterName)

// 1. VNet & Subnet の作成

@description('for AKS Cluster Name & VNET Name Prefix')
param VNetAddressPrefix string = '10.10.0.0/16'

@description('for AKS Cluster Name & VNET Name Prefix')
param SubnetAddressPrefix string = '10.10.1.0/24'

resource AKSVNet 'Microsoft.Network/virtualNetworks@2021-03-01' = {
  name: '${clusterName}-vnet'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        VNetAddressPrefix
      ]
    }
    subnets: [
      {
        name: '${clusterName}-vnet-subnet1'
        properties: {
          addressPrefix: SubnetAddressPrefix
        }
      }
    ]
  }
}

// 2. ユーザー割り当て Managed ID の作成

resource managedId 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: managedIdName
  location: location
}

// 3. ロールの作成と割り当て

@description('A new GUID used to identify the role assignment')
param roleNameGuid string = guid(managedIdName)

var role = {
  Owner: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
  Contributor: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
  Reader: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
}

resource AKSSubnet 'Microsoft.Network/virtualNetworks/subnets@2021-03-01' existing = {
  parent: AKSVNet // https://githubmemory.com/repo/Azure/bicep/issues/1972
  name: '${clusterName}-vnet-subnet1'
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
  name: roleNameGuid
  scope: AKSSubnet
  properties: {
    roleDefinitionId: role['Contributor']
    principalId: managedId.properties.principalId
    principalType: 'ServicePrincipal'
    // https://githubmemory.com/repo/Azure/bicep/issues/3695
  }
  dependsOn: [
    managedId
  ]
}

// 4. AKS Cluster の作成

resource aks 'Microsoft.ContainerService/managedClusters@2021-08-01' = {
  name: clusterName
  location: location
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: managedId
  }
  properties: {
    dnsPrefix: clusterName
    enableRBAC: true
    agentPoolProfiles: [
      {
        name: 'agentpool1'
        count: 3
        vmSize: 'standard_d2s_v3'
        mode: 'System'
        vnetSubnetID: AKSSubnet.id
      }
    ]
  }
}
```

# 説明

## 1. VNet & Subnet の作成

AKS のノードに振る IP を自前の Subnet にするため、新規作成します。こうすることで、よりプライベートクラスターにしやすくなります。

https://docs.microsoft.com/en-us/azure/templates/microsoft.network/virtualnetworks?tabs=bicep

## 2. ユーザー割り当て Managed ID の作成

ユーザー割り当て Managed ID を作成します。指定できるオプションは多くないですね。

https://docs.microsoft.com/ja-jp/azure/templates/microsoft.managedidentity/userassignedidentities?tabs=bicep

既定で何もオプションをつけない場合、AKS クラスターには個別のシステム割り当て Managed ID が作成されます。自前のユーザー割り当て Managed ID を利用すると、複数の AKS クラスターで同一の Managed ID を使えるようになり、権限周りの統一がしやすくなります。

https://docs.microsoft.com/ja-jp/azure/aks/use-managed-identity

## 3. ロールの作成と割り当て

前項で作成した Managed ID に、サブネットの共同所有者ロールを割り当てます。

共同所有者のロールは決まったリソースタイプが用意されています。[こちら](https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/scope-extension-resources)が参考になると思います。

## 4. AKS Cluster の作成

AKS Cluster の作成ですが、かなり多くのオプションがあります。プライベートクラスターの有効化や Pod/Services のサブネット指定、クラスターオートスケーラーの設定などなど……  
下記の Bicep ドキュメントを参考に、自分好みのオプションを追加していきましょう！

https://docs.microsoft.com/ja-jp/azure/templates/microsoft.containerservice/managedclusters?tabs=bicep

# 残念ながら……うまく実行できません

いざ、実行！

```
az deployment group create --resource-group <resource-group-name> --template-file <path-to-bicep>
```

なのですが、残念ながら記事執筆時点の Bicep では、この書き方だと VS Code 上で警告等の指摘は無いものの、ビルド後にエラーが発生してしまいます。

> {"error":{"code":"InvalidTemplate","message":"Deployment template validation failed: 'The template resource 'akslab-example-001' at line '106' and column '5' is not valid: The template function 'reference' is not expected at this location. Please see https://aka.ms/arm-template-expressions for usage details.. Please see https://aka.ms/arm-template-expressions for usage details.'.","additionalInfo":[{"type":"TemplateViolation","info":{"lineNumber":106,"linePosition":5,"path":"properties.template.resources[3]"}}]}}

## エラーの原因を調べていく

このエラーの示す場所 (at line '106' and column '5') は、Bicep の内容ではなくビルドした ARM Template の中の場所を指しています。

Bicep 拡張をインストールした Visual Studio Code にて、対象の Bicep ファイルを右クリックすると「Build」というメニューがあるので実行します。すると、ARM Template である JSON ファイルが作成されるので、こちらを見てみます。

```
{
    "type": "Microsoft.ContainerService/managedClusters",
    "apiVersion": "2021-08-01",
    "name": "[parameters('clusterName')]",
    "location": "[parameters('location')]",
    "identity": {
    "type": "UserAssigned",
    "userAssignedIdentities": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('managedIdName')), '2018-11-30', 'full')]"
    },

    ... 以下略
```

エラーの場所 (at line '106' and column '5') は、このブロックを指しており、ブロック内の `reference()` はここだけです。ここで `reference()` を使うことはできない、と怒られていますね。

調べてみたところ、ARM Template 上では `userAssignedIdentities` の個所を下記のように修正すると実行できるとの[情報](https://stackoverflow.com/questions/64877861/the-template-function-reference-is-not-expected-at-this-location)が見つかりました。

```
"userAssignedIdentities": {
    "[resourceID('Microsoft.ManagedIdentity/userAssignedIdentities/', parameters('managedIdName'))]": {}
} 
```

＃Bicep 上でなんしか書き方を変えれば、ビルド時にこの出力ができるのか……残念ながら情報は見つけられませんでした。

修正のうえ、該当の ARM Template ファイルを利用してデプロイすれば、実行できるかと思います。

```
az deployment group create --resource-group <resource-group-name> --template-file <path-to-json>
```

Bicep ファイルだけでデプロイできなくて残念ですが……

# 最後に

Bicep、VS Code の Bicep 拡張の Intellisense も優秀で、慣れるとテンプレートを書く生産性がだいぶ上がる感があります。

マルチプラットフォーム的には Terraform 等に軍配があがりますが、さしあたり Azure だけをターゲットにする場合はかなり便利なツール (言語) だと思います。

検証等の際は、コストなどの関係でも同じような構成を作ったり壊したりすることは結構あると思います。こういう仕組みに慣れておくのは結構大事ですね！