Azure Portal を使って ARMテンプレートのデプロイをする際に KeyVaultのシークレットを参照する

# はじめに
Azure Portal から、AVDをデプロイする際に、数多くの入力項目があるのですが、その際に ドメイン管理者のパスワードやVM管理者のパスワードを手入力する必要があります。
そこで入力ミスがあると、時間を掛けてデプロイを待ったのに、パスワード入力ミスで失敗した・・・という事がありえます。

パラメータファイルに 固定値のパスワードを記載すれば簡単なのですが、デプロイを運用者の人にやってもらうようなシチュエーションを考えると、セキュリティ的に問題があります。
そんなこともあって、KeyVault から パスワードを読み込ませる事を考えました。

私は、AVDで試していますが、VMを ARMテンプレートでデプロイする際に パスワードを読み込ませたいというシチュエーションの人にも応用していただけると思います。

---

# 本記事を参照いただく前に・・・
本記事を参照頂く前に、以下の記事を参照ください。

編集する元となる オリジナルの ARM テンプレートの入手方法や、本記事の内容を元に カスタマイズした結果の ARM テンプレートの内容まで記載されています。

https://qiita.com/carol0226/items/c7af5206ce78492ba879

https://qiita.com/carol0226/items/fee6f2f325c3c342b494

---

# ハマった点
公開情報を調べてみると、以下の記事が見つかります。
この通りにやれば良さそうに思うのですが、落とし穴がありました。

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/template-tutorial-use-key-vault

ここに書かれている内容は、Parameters.json に Reference関数を記載して KeyVaultのシークレットを参照する記載をする方法が説明されています。
しかし、これは Azure PowerShell や Azure CLI からコマンドを使ってデプロイする時にしか機能しないことが判りました。
Microsoftのサポートにも問合せをして確認済みです。
> サポート回答より
Portal ではパスワードの入力が必須という条件で実装されており、Azure ポータルでデプロイを試みる場合、シークレットからパスワードを取得しているもののそれをそのまま受け入れる実装がございません。
そのため、シークレットからパスワードを取得するパラメーターをアップロード後、”保存” した後にパラメーターが戻され、パスワードが Null に設定される動作となります。

私の場合は、Azure Portal で「カスタムテンプレートデプロイ」の画面にパラメータを表示させて、手動で可変の値を変更したのちに 「確認と作成」ボタンを押してデプロイさせたい訳です。
なので、この方法は使えません。

---

# ARMテンプレートの概要（参考）
## テンプレートファイル（Template.json）
Template.json は、以下で紹介されているセクションがあります。
ARMテンプレートをカスタマイズするには、まず これを知っておく必要があります。
私の記事を理解いただくには、以下に記載した 「パラメータ」と「リソース」だけ覚えておいてください。
- パラメータ（parameters）
　項目名を用意して、初期値や選択肢の一覧を指定します。
- 変数（variables）
- ユーザー定義関数（functions）
- リソース（resources）
　パラメータや変数を参照して、記述された Azureリソースをデプロイします。
- 出力（outputs）

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/syntax#template-format

## パラメータファイル（Parameters.json）
テンプレートファイルには、複数のセクションがありますが、パラメータファイルには Parameters セクションしかありません。
考え方としては、Template.json は固定で使い、可変となる値だけ Parameters.json に記載して デプロイの際に組み合わせて使用する設計となっているようです。

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/parameter-files

---

# KeyVaultのシークレットを 参照させるために考えたこと
結論としては、パラメータファイルや、テンプレートファイルの Parameters セクションに [Reference関数](https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/template-functions-resource#reference "Reference関数") で KeyVaultから値を参照しても 引き渡すことができませんでした。
これは、MSサポートから案内があった通り、セキュリティ情報を Portal画面にテキストで表示させないための実装があるからです。

解決策としては、[Reference関数](https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/template-functions-resource#reference "Reference関数") を Resources セクションに書いてしまうということです。

Parameters セクションに記載された値は Portal の「カスタムテンプレートのデプロイ」で表示させる際に Null 値になってしまいますが、Resources セクションは「確認と作成」ボタンを押したあとのデプロイ時に処理されるセクションであり、画面表示を介さないため シークレット値を読み込ませるができるのです。

---

## 補足説明
### パラメータファイルを使うとシークレット値を失う
以下の図のイメージで、パラメータファイルが シークレットを読み込みますが、Azure Portal 画面でテンプレートに引き渡される際に、Null 値になってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/6597ca75-83cf-8cab-32dc-9c3865669380.png)

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#reference-secrets-with-static-id

---

### ヒラメキ💡：インラインパラメータで渡す
Resources セクションに記載して解決する方法は、以下の公開情報を読んでいた際に思いつきました。
公開情報の説明は テンプレートを入れ子で処理するやり方で 本件とは直接関係が無いのですが、赤枠の部分をよく見ると、パラメータファイルではなく インラインパラメータで KeyVaultからシークレットを渡しています。コレならイケるのかもとヒラメキが起こりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/5188f19e-e2d3-25e4-d6ca-2513777d0d40.png)

https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/templates/key-vault-parameter?tabs=azure-cli#reference-secrets-with-dynamic-id

---

# インラインパラメータで引き渡すための記述方法
## ポイント１（Parameters セクションに追加）
Parameters セクションでは、以下のように "mykeyvault" という自分用の変数項目を新設して、その項目の DefaultValue に、KeyVault の リソース ID を示す文字列を記載しておきます。
**"[resourceId('AVD-Environment','Microsoft.KeyVault/vaults','avd-deploy2')]"**
````
        "myKeyvault": {
            "type": "string",
            "metadata": {
                "description": "管理者パスワード（キーボルトのシークレット）を登録した キーボルトのリソースID"
            },
            "defaultValue": "[resourceId('AVD-Environment','Microsoft.KeyVault/vaults','avd-deploy2')]"
        },
````

上記のように Parameters セクション に記載されていると、テンプレートのデプロイ 画面では、以下のように表示されるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/d96e9ec4-1083-4591-fab3-a659ff6334a3.png)

---

## ポイント２（Resources セクションを編集）
**Before**
編集前のオリジナルのテンプレートの内容です。この箇所を見つけてください。
シンプルに、パラメーター で入力された値を そのまま 引き渡しています。
````
        "parameters": {
            "vmAdministratorAccountUsername": {
                "value": "[parameters('vmAdministratorAccountUsername')]"
            },
            "vmAdministratorAccountPassword": {
                "value": "[parameters('vmAdministratorAccountPassword')]"
                }
            },
            "administratorAccountUsername": {
                "value": "[parameters('administratorAccountUsername')]"
            },
            "administratorAccountPassword": {
                "value": "[parameters('administratorAccountPassword')]"
            },
        }
````

**After**
以下の内容に修正してください。
ここでは、**reference** 句 を使って キーボルトを参照させています。その際に キーボルトのリソース ID を **parameters('mykeyvault')** を使って取得しています。
このとき、ポイント１で **mykeyvault** の変数に保存しておいた文字列が使用されます。
````
        "parameters": {
            "vmAdministratorAccountUsername": {
                "value": "[parameters('vmAdministratorAccountUsername')]"
            },
            "vmAdministratorAccountPassword": {
                "reference": {
                    "keyVault": {
                        "id": "[parameters('myKeyvault')]"
                    },
                    "secretName": "vmAdminPassword"
                }
            },
            "administratorAccountUsername": {
                "value": "[parameters('administratorAccountUsername')]"
            },
            "administratorAccountPassword": {
                "reference": {
                    "keyVault": {
                        "id": "[parameters('myKeyvault')]"
                    },
                    "secretName": "DomainPassword"
                }
            },
        }
````

---

# KeyVault 作成のポイント
今回の用途向けには、以下のポイントを意識して キーコンテナを作成頂けば良いと思います。

## アクセス構成
アクセス許可モデルは、「コンテナのアクセスポリシー」を選択するのと、
リソースアクセスで「テンプレートの展開用」にチェックを入れるのがポイントです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/9a30e046-17cc-d9e7-1ae8-1c5b9591a86f.png)

:::note warn
ここで、アクセスポリシーへ変更したあと、次章の設定変更ができるようになるまで、5分くらい待つ必要がありました。
:::

## アクセスポリシー
画面で示したアクセス許可があれば問題ありません。
なお、必要最低限 どの権限が必要かの検証までやれていません。
テンプレートの展開だけなら、展開作業を行うアカウントに「シークレットのアクセス許可」で「取得」の権限だけ与えておくと良さそうです（ぜひ検証してみてください）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/1a03852a-4801-2c00-84eb-fc75e4512b67.png)

---

## シークレット
以下のように、テンプレートで参照させたいシークレット値を設定しておきます。
シークレットは、下図のように、DomainPassword と vmAdminPassword の２つ作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/bb80dede-89b7-2d10-8def-425477685e60.png)

シークレットは、以下のように作成します（シークレット値に パスワード を入力します）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/ba8904ae-adaf-32d2-80c0-6094a00e9024.png)

シークレットの作成後、「現在のバージョン」を押して 以下の画面を表示させたあと、「シークレット値を表示する」を押すと 値を見ることが出来ます（当然 参照権限があるユーザーのみ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/fa85e98e-4c0d-29d1-119b-1f90eee219c6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619637/91fcbdf3-6e17-9585-2a18-e3b94941eb96.png)

こうすることで、シークレット値を 設定・参照できるユーザーだけが管理者パスワードを把握していて、テンプレートをデプロイする人にはパスワードを知らせることなく作業を実施してもらうことが可能になります。