はじめに
Azure 環境を使わせるにあたり、PaaS サービスは必ずプライベート エンドポイントで使わせたい、ユーザーに NW 周りを操作する権限は与えなくない、といった制限をかけたい場合があると思います。
完全にクローズドな環境とするのは難しいため、可能な範囲で通信や権限を許可して使いやすくしたいところですが、ポリシー上どうしても許容できない場合もあるでしょう。
ポリシーを活用してプライベート エンドポイント必須にすることはできますが、サービスによっては、ポータルでのデプロイと同時にプライベート エンドポイントを有効化できなかったり、 NW 関連の権限をすべて奪ってしまうとプライベート エンドポイントができなくなったりと、細かい調整が難しかったりします。
それを解決できそうな方法として、テンプレート スペックと Automation を使う方法を試してみました。
こちらの別バージョンとして、Microsoft Forms + Azure Logic Apps + Template Spec で構成したものを技術書典で販売した同人本でまとめています。 ご興味があればこちらもご覧ください。
目次
構成
構成を図示するとこんな感じです。
やりたいことはこれ
- ユーザーは、サブスクリプションの閲覧者ロールと Automation オペレーター ロールを持つ
- Azure Automation のマネージド ID は、サブスクリプションの共同作成者ロールを持つ
- PaaS サービス (Storage Account) + プライベート エンドポイントをデプロイする bicep テンプレートを作成し、テンプレート スペックとして登録しておく
- ユーザー自身にリソース作成権限はないが、 Automation Runbook を実行するとテンプレート スペックがデプロイされリソースが作成される
Automation Runbook に権限を与えてテンプレートスペックをデプロイさせることで、 ユーザーに直接リソースを操作する権限を与えないけどデプロイができる状況を作ろう、という感じです。
※よくよく考えたら、この構成だとユーザーは Template Spec の読み取り権限すらなくても OK でした。
テンプレート スペック作成
ARM テンプレートもしくは Bicep を登録しておくことができ、Azure ポータルからデプロイに利用することができます。
簡単なバージョン管理もできます。
テンプレート スペックをデプロイするには、テンプレート スペック自体の読み取り権限と、テンプレートでデプロイするリソースの操作権限が必要です。
今回のテンプレートではストレージ アカウントや、プライベート エンドポイントの権限が必要になりますが、ユーザーには権限を付与しません。
今回の構成で使用したテンプレートはこちら
既存の VNet 、サブネットに対してプライベート エンドポイントと、それと関連付けたストレージ アカウントをデプロイします。
ストレージ アカウントはパブリック アクセスを完全に無効にしています。
本来であれば Azure DNS プライベート ゾーンにレコードをデプロイまで含めると思いますが、今回想定していたシナリオ的にそこまでは入れていません。
テンプレート スペックの作成はポータルからもできますが、現状、bicep の場合は次のようなコマンドから作成する必要があります。
az ts create --name StorageWithPE --version "1.0" --resource-group pe-bicep-spoke --location japaneast --template-file ".\Storage\StorageWithPrivateEndpoint.bicep"
Azure Automation
Runbook 作成
Azure Automation の PowerShell Runbook を作成します。
今回作成したのはこれ (コメントはデバッグ用)
param( [string] $location, [string] $ResourceGroup_Name, [string] $VNet_Name, [string] $Subnet_Name, [string] $Storage_Name, [string] $Storage_SKU ) $TemplateSpec_Name = "StorageWithPE" $TemplateSpec_ResourceGroup = "pe-bicep-spoke" Connect-AzAccount -identity # Write-Output "Input" # Write-Output "Location:"$location # Write-Output "RG Name:"$ResourceGroup_Name # Write-Output "VNet Name:"$VNet_Name # Write-Output "Subnet Name:"$Subnet_Name # Write-Output "Storage Name:"$Storage_Name # Write-Output "Storage SKU:"$Storage_SKU $TemplateSpec = Get-AzTemplateSpec -ResourceGroupName $TemplateSpec_ResourceGroup -Name $TemplateSpec_Name $LatestVersionId = "init" if ($TemplateSpec.Versions.ID.GetType().FullName -eq "System.String"){ $LatestVersionId = $TemplateSpec.Versions.ID }elseif($TemplateSpec.Versions.ID.GetType().FullName -eq "System.Object[]"){ $LatestVersionId = $TepmlateSpec.Versions.ID[$TepmlateSpec.Versions.length - 1] } # Write-Output "Get Template Spec" # Write-Output "Spec ID:"$LatestVersionId New-AzResourceGroupDeployment ` -ResourceGroupName $ResourceGroup_Name ` -TemplateSpecId $LatestVersionId ` -location $location ` -VNetName $VNet_Name ` -SubnetName $Subnet_Name ` -Storage_Name $Storage_Name ` -Storage_SKU $Storage_SKU
スクリプトでは、以下のことを行います。
- bicep テンプレートに必要なパラメータを受ける
- 実行するテンプレート スペックのリソース ID を取得する (RG 名と名前はハードコーディング)
- 入力されたパラメータを使ってテンプレート スペックをデプロイする
テンプレート スペックのリソース ID を取得するとき、バージョンが 1 つか複数かで取得結果が異なるため、if 文で処理しています。
マネージド ID 有効化
システム マネージド ID を有効化し、サブスクリプションの共同作成者権限を与えておきます。
ストレージ アカウント、プライベート エンドポイントのデプロイだけであれば強めではありますが、ストレージ アカウント以外のサービスも同様に実装していくことを考えると、最終的には共同作成者でもよいかと思います。
ユーザー作成
最後に、テスト用のユーザーを作成してロールを付与します。
ユーザーはサブスクリプションをスコープに、閲覧者と Automation オペレーターのロールを付与します。
Automation のマネージド ID が共同作成者を持つため何でもできてしまいますが、ユーザーには Automation オペレーターロールを用いて Runbook の実行権限しか与えないようにします。
テスト
最後にテスト ユーザーでログインして、Runbook の実行と期待したリソースがデプロイできることを確認します。
デプロイ前のリソース グループ
Runbook 実行時のパラメータ指定
Runbook の実行成功
リソース グループのデプロイ履歴でも成功確認
リソース グループに、指定したストレージ アカウントとプライベート エンドポイントができている
※デプロイはできているけど、本来必要な Azure DNS プライベート ゾーンを設定してないのでこのままだと繋がらない
終わりに
ユーザーに権限を与えないけどリソースのデプロイができる、という状況を作ってみました。
これなら最低限の権限で実現できるのではないでしょうか。
ただ、ユーザーが多い場合、 Runbook の同時実行数とかは制限になるかと思います。
本当は Logic Apps で手動実行するときにパラメータ指定させるつもりでしたが、Power Automate と違ってプルダウンのパラメータ指定を作れるトリガーがなかったので Automation にしました。
ストレージの SKU とか手打するものではないと思うのでいい方法がないか検討中…