VMのデプロイにキーコンテナを使う

Azure
前の記事

以前BicepでデプロイしたAzure VMですが、

デプロイ時のPowerShellのコマンドに管理者ユーザーとパスワードを設定する必要があります。

# パラメータをハッシュテーブルまたはインラインで指定する例
Connect-AzAccount

New-AzResourceGroup -Name rg-vmdeploy-bicep -Location "Japan East"

# $HOME\.azure\bin を現在のセッションのPATHに一時的に追加
$env:Path += ";$HOME\.azure\bin"

$parameters = @{
    vmName        = 'vm-deploy-bicep'
    adminUsername = 'XXXXX'
    adminPassword = 'YYYYY'
    location      = 'japaneast'
}

New-AzResourceGroupDeployment -ResourceGroupName rg-vmdeploy-bicep `
    -TemplateFile main.bicep `
    -TemplateParameterObject $parameters

コマンドもGitHubで保管しているのと、資格情報をハードコーディングしているので、

このままではVMの管理者ユーザーが駄々洩れです。

改善策はいろいろある気がしますが、今回はAzureのキーコンテナーを使ってみます。

Azure キーコンテナー(Key Vault)

資格情報や証明書を安全に管理するためのコンテナです。

基本はRBACで権限を持った人にのみ機密情報を渡せるようになります。

キーコンテナーへのアクセスはサブスクリプションの所有者権限だけではNGで、キーコンテナー専用のRBACがあるので、適切に付与していきましょう。

<参考>キーコンテナーのRBAC一部抜粋

Azure RBAC を使用して Azure キー コンテナーへのアクセス許可をアプリケーションに付与する
Azure ロールベースのアクセス制御を使用して、キー、シークレット、および証明書へのアクセス権を付与する方法について説明します。

キーコンテナーのデプロイ

  1. キーコンテナーを作成
  2. シークレットの登録
  3. VMデプロイのコマンドを修正

という流れにしないといけないため、先にキーコンテナーのリソースを作成します。

keyvault.bicep
// keyvault/keyvault.bicep

@description('デプロイする場所 (リージョン)')
param location string = resourceGroup().location

@description('Key Vault名。グローバルで一意である必要があります。')
param keyVaultName string

@description('Key Vaultへのアクセスを許可するユーザー/SPのオブジェクトID')
param principalId string

// Key Vault のリソース定義
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: keyVaultName
  location: location
  properties: {
    tenantId: subscription().tenantId 
    sku: {
      family: 'A'
      name: 'standard'
    }
    enableSoftDelete: true
    softDeleteRetentionInDays: 7
    // アクセス設定
    accessPolicies: [
      {
        objectId: principalId
        tenantId: subscription().tenantId
        permissions: {
          secrets: [
            'get' // VMパスワードの取得権限
          ]
        }
      }
    ]
  }
}

// Key VaultのリソースIDを外部に出力
output keyVaultId string = keyVault.id

permissionでgetのみ指定していますが、これだとシークレットの読み込みだけで後工程のシークレットの登録ができないため、権限を振り直す必要があります。

また、シークレット情報の登録はコードでやると結局意味ないので、登録のみポータルから行います。

IAMの設定

まずは権限の振り直しをします。

ポータルからキーコンテナのIAMに移動してロール割り当ての追加。

いろいろ出てくるので必要なものを選んで割り当ててください。

Key Vault表記だったりキー コンテナー表記だったりブレてるので、求めてるものがなければ英⇔カナを変えてみたりしてください。

シークレットの登録

続いて、シークレットを登録します。リソースのオブジェクトから「シークレット」を選択して生成します。

シークレットはキーバリューの形式で保存されるため、

管理者IDと管理者パスワードを紐づけて保管するということはできません。

ここで指定する「名前」はあくまでこのシークレットにつける名前なので、ユーザーIDとしての保管ではないです。

今回はとりあえずWindowsServerのパスワードだけ「vm-admin-password」という名前で作成しました。

PowerShellコマンドでシークレットを呼び出す

VMを作成するPowerShellコマンドは以下のようになります。

# Key Vault IDの取得
$existingKeyVaultId = (Get-AzKeyVault -VaultName $keyVaultName).ResourceId

# $HOME\.azure\bin を現在のセッションのPATHに一時的に追加
$env:Path += ";$HOME\.azure\bin"

$vmParameters = @{
    vmName                  = "vm-deploy-bicep"
    adminUsername           = "adminUser"
    existingKeyVaultId      = $existingKeyVaultId
    adminPasswordSecretName = "vm-admin-password"
    location                = $location
}

キーコンテナーのリソースIDを取得して、そのリソースIDの中にあるvm-admin-passwordの情報をシークレットとして利用するという流れです。

また、呼び出されるmain.bicepにも変更が必要です。

キーコンテナーを参照し、VMの管理者パスワードを設定しています。

main.bicep

// main.bicep

@description('デプロイする場所 (リージョン)')
param location string = resourceGroup().location

// --- 必須パラメータ ---
@description('VM名')
param vmName string
@description('管理者ユーザー名')
param adminUsername string

// --- Key Vault 関連パラメータ (外部からIDを受け取る) ---
@description('既に作成されているKey Vaultの完全なリソースID')
param existingKeyVaultId string

@description('Key Vaultに保存されているVM管理者パスワードのシークレット名')
param adminPasswordSecretName string = 'vm-admin-password'

// --- ネットワーク/VM設定パラメータ ---
@description('仮想ネットワーク名')
param vnetName string = 'vnet-deploy-bicep'
@description('サブネット名')
param subnetName string = 'subnet-deploy-bicep'
@description('NIC名')
param nicName string = 'nic-deploy-bicep'
@description('VMのサイズ (例: Standard_B1s)')
param vmSize string = 'Standard_B1s'

// --- Key Vault 参照の準備 ---
// Key Vault IDから Key Vault 名を抽出
var keyVaultName = last(split(existingKeyVaultId, '/'))

// Key Vault のプロバイダー情報を取得するための変数 (Bicepが参照を解決するために必要)
// Bicepでは外部リソースへの参照に `resourceId()` が使われますが、Key Vault参照には特殊な構文が必要です。
var keyVaultReference = resourceId(
  subscription().subscriptionId,
  resourceGroup().name,
  'Microsoft.KeyVault/vaults',
  keyVaultName
)

// 1. 仮想ネットワークモジュールのデプロイ
module vnetModule 'modules/vnet.bicep' = {
  name: 'vnet-deployment'
  params: {
    location: location
    vnetName: vnetName
    subnetName: subnetName
  }
}

// 2. ネットワークインターフェースモジュールのデプロイ
module nicModule 'modules/nic.bicep' = {
  name: 'nic-deployment'
  params: {
    location: location
    nicName: nicName
    // VNetモジュールの出力からサブネットIDを取得
    subnetId: vnetModule.outputs.subnetId 
  }
}

// 3. 仮想マシンモジュールのデプロイ
module vmModule 'modules/vm.bicep' = {
  name: 'vm-deployment'
  params: {
    location: location
    vmName: vmName
    adminUsername: adminUsername
    vmSize: vmSize
    nicId: nicModule.outputs.nicId
    
    // Key Vault 参照をシークレットURIとしてVMモジュールに渡す
    // この構文により、デプロイ時にAzure Resource ManagerがKey Vaultからシークレットを取得します。
    adminPassword: '[reference(\'${keyVaultReference}\', \'2023-07-01\').secretUriWithVersion(\'${adminPasswordSecretName}\')]'
  }
}

// 最終的なVM IDを出力
output virtualMachineId string = vmModule.outputs.vmId

これで、PowerShellコマンドを実行したところデプロイは正常に完了しました。

確認作業をしようとすると、デプロイしたWindowsServerにパブリックIPが割り当てられていないことが判明したため、NICの設定を少し変更しようと思います。

ということで、次回は設定変更とBicep冪等性について確認します。

コメント