JavaScriptを有効にしてください

Arc Jumpstart を Azure Verified Module で書き換えてみる その1

 ·   6 分で読めます  ·   Kento

Arc Jumpstart は Azure Arc 環境のテスト環境を簡単に作成することができます
勉強がてら Bicep の Azure Verified Module に書き換えつつ、中身をのぞいてみます

今回は Azure VM を Azure Arc 対応サーバーにするシナリオです
Azure Arc Jumpstart

Windows 編

勉強対象の ARM Template は azure_arc/azure_arc_servers_jumpstart/azure/windows/arm_template at main · microsoft/azure_arc です

基にするファイル構成

このような構成になっています

ファイル名 説明
azuredeploy.json テンプレート本体
azuredeploy.parameters.json パラメータファイル
scripts/install_arc_agent.ps1 Connected Machine Agent (Azure Arc のエージェント) のインストールスクリプト

azuredeploy.json を Azure Verified Module に書き換えることにします
Connected Machine Agent のインストールスクリプトはサービスプリンシパルを利用しています
個人的に使いずらいのでデバイスコードを使ったインストール方法に変更します

Azure Verified Module に書き換える

azuredeploy.json を ARM Tepmlate から Bicep へ逆コンパイルをします
やり方は ARM テンプレート JSON を Bicep に逆コンパイルする - Azure Resource Manager | Microsoft Learn に記載があります
今回は Visual Studio Code を使用して Bicep ファイルを作成する - Azure Resource Manager | Microsoft Learn に書かれている Visual Studio Code の拡張機能を使った方法を取りました

コンパイル結果は割愛しますが、以下のリソースが含まれています

  • NIC (Microsoft.Network/networkInterfaces)
  • NSG (Microsoft.Network/networkSecurityGroups)
  • NSG rule (Microsoft.Network/networkSecurityGroups/securityRules)
  • Vnet (Microsoft.Network/virtualNetworks)
  • Public IP (Microsoft.Network/publicIPAddresses)
  • VM (Microsoft.Compute/virtualMachines)
  • VM Extension (Microsoft.Compute/virtualMachines/extensions)
  • Bastion Host (Microsoft.Network/bastionHosts)

コンパイル結果を参考にして、Azure Verified Module で書き換えました
モジュールを使っているので非常にスッキリしています

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
@description('すべてのリソースに共通する接頭語です')
param prefix string

@description('リソースの場所')
param location string = resourceGroup().location

@description('Azure Arc 対応サーバーのユーザー名')
param vmUserName string = 'AzureAdmin'

@description('Azure Arc 対応サーバーのパスワード')
@secure()
param vmPassword string

@description('VM のサイズ')
param vmSize string = 'Standard_D4s_v3'

@allowed([
  'no'
  'yes'
])
@description('Bastion をデプロイするかどうか')
param deployBastion string = 'no'

@description('Azure Arc の接続にデバイスコードを利用するかどうか。false の場合、サービスプリンシパルを利用')
param useDeviceCode bool = true

@description('サービス プリンシパルの ID。デバイスコードを利用する場合はスルー')
param appId string

@description('サービス プリンシパルの シークレット。デバイスコードを利用する場合は適当に埋める')
@secure()
param appSecret string

@description('Vnet のアドレス空間')
param vnetAdressPrefix string = '10.0.0.0/16'

var tenantId = subscription().tenantId
var subscriptionId = subscription().subscriptionId
var resourceGroupName = resourceGroup().name
var nicIpConfigurations = {
  // Bastion を使わない場合、VM に Public IP を割り当てる
  no: {
    name: '${prefix}-ipconfig'
    subnetResourceId: vnet.outputs.subnetResourceIds[0]
    pipConfiguration: {
      name: '${prefix}-pip'
    }
  }
  yes: {
    name: '${prefix}-ipconfig'
    subnetResourceId: vnet.outputs.subnetResourceIds[0]
  }
}

// NSG
module nsg 'br/public:avm/res/network/network-security-group:0.3.1' = {
  name: '${prefix}-nsg'
  params: {
    name: '${prefix}-nsg'
    // Non-required parameters
    location: location
    securityRules: [
      {
        name: 'Allow-RDP'
        properties: {
          access: 'Allow'
          destinationAddressPrefix: 'VirtualNetwork'
          destinationPortRanges: [
            '3389'
          ]
          direction: 'Inbound'
          priority: 200
          protocol: 'Tcp'
          sourceAddressPrefix: '*'
          sourcePortRange: '*'
        }
      }
    ]
  }
}

// Vnet
module vnet 'br/public:avm/res/network/virtual-network:0.1.8' = {
  name: '${prefix}-vnet'
  params: {
    addressPrefixes: [
      vnetAdressPrefix
    ]
    name: '${prefix}-vnet'
    location: location
    subnets: [
      {
        name: 'default'
        addressPrefix: cidrSubnet(vnetAdressPrefix, 24, 0)
        networkSecurityGroupResourceId: nsg.outputs.resourceId
      }
      {
        name: 'AzureBastionSubnet'
        addressPrefix: cidrSubnet(vnetAdressPrefix, 24, 1)
      }
    ]
  }
}

// VM
module vm 'br/public:avm/res/compute/virtual-machine:0.5.3' = {
  name: '${prefix}-arcvm'
  params: {
    adminUsername: vmUserName
    adminPassword: vmPassword
    imageReference: {
      publisher: 'MicrosoftWindowsServer'
      offer: 'WindowsServer'
      sku: '2022-datacenter-g2'
      version: 'latest'
    }
    name: '${prefix}-arcvm'
    nicConfigurations: [
      {
        nicSuffix: '-nic'
        ipConfigurations: [
          nicIpConfigurations[deployBastion]
        ]
      }
    ]
    osDisk: {
      diskSizeGB: 128
      managedDisk: {
        storageAccountType: 'Standard_LRS'
      }
    }
    osType: 'Windows'
    vmSize: vmSize
    zone: 0
    // Arc 用の拡張機能
    extensionCustomScriptConfig: {
      enabled: true
      fileData: [
        {
          uri: ((useDeviceCode)
            ? 'https://raw.githubusercontent.com/NakayamaKento/Azure_Bicep/main/products/ArcEnabledWindowsServer/scripts/install_arc_agent_deviceCode.ps1'
            : 'https://raw.githubusercontent.com/NakayamaKento/Azure_Bicep/main/products/ArcEnabledWindowsServer/scripts/install_arc_agent.ps1')
        }
      ]
    }
    extensionCustomScriptProtectedSetting: {
      commandToExecute: ((useDeviceCode)
        ? 'powershell.exe -ExecutionPolicy Bypass -File install_arc_agent_deviceCode.ps1 -tenantId ${tenantId} -resourceGroup ${resourceGroupName} -subscriptionId ${subscriptionId} -location ${location} -adminUsername ${vmUserName}'
        : 'powershell.exe -ExecutionPolicy Bypass -File install_arc_agent.ps1 -appId ${appId} -password ${appSecret} -tenantId ${tenantId} -resourceGroup ${resourceGroupName} -subscriptionId ${subscriptionId} -location ${location} -adminUsername ${vmUserName}')
    }
  }
}


// Bastion
module bastion 'br/public:avm/res/network/bastion-host:0.2.2' = if (deployBastion == 'yes'){
  name: '${prefix}-bastion'
  params: {
    name: '${prefix}-bastion'
    virtualNetworkResourceId: vnet.outputs.resourceId
  }
}

デバイスコードを使ったインストールスクリプト

参考にした Jumpstart ではサービスプリンシパルを使っていました
サービスプリンシパルは作成後のライフサイクルを考える必要があります
そのため、検証のためサービスプリンシパルを作成 → 検証後に削除 という操作が必要になります

一方で、Azure Arc の登録にはデバイスコードを使ったインストール方法もあります
この方法は都度、ブラウザを開く必要がありますが、サービスプリンシパルのライフサイクルを考えなくてよいため個人的には使いやすいです

デバイスコードを使ったインストール方法に変更するため、scripts/install_arc_agent.ps1 の中身を見てみます
おおよそ以下の構成です

  1. パラメータの定義、環境変数の設定
  2. tmp フォルダの作成
  3. C:\tmp に LogonScript.ps1 を作成
  4. ユーザーのログオン時に LogonScript.ps1 が実行されるように設定

デバイスコードを使う場合、以下の修正が必要です

  • パラメータからサービスプリンシパル関連のものを削除
  • LogonScript.ps1 の中身をデバイスコードを使ったもの [–use-device-code] に変更

変更後のものはこちらになります

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
param (
    [string]$subscriptionId,
    [string]$tenantId,
    [string]$resourceGroup,
    [string]$location,
    [string]$adminUsername
)

[System.Environment]::SetEnvironmentVariable('subscriptionId', $subscriptionId,[System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('tenantId', $tenantId,[System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('resourceGroup', $resourceGroup,[System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('location', $location,[System.EnvironmentVariableTarget]::Machine)
[System.Environment]::SetEnvironmentVariable('adminUsername', $adminUsername,[System.EnvironmentVariableTarget]::Machine)

New-Item -Path "C:\" -Name "tmp" -ItemType "directory" -Force

# Creating PowerShell Logon Script
$LogonScript = @'
Start-Transcript -Path C:\tmp\LogonScript.log

## Configure the OS to allow Azure Arc Agent to be deploy on an Azure VM

Write-Host "Configure the OS to allow Azure Arc Agent to be deploy on an Azure VM"
Set-Service WindowsAzureGuestAgent -StartupType Disabled -Verbose
Stop-Service WindowsAzureGuestAgent -Force -Verbose
New-NetFirewallRule -Name BlockAzureIMDS -DisplayName "Block access to Azure IMDS" -Enabled True -Profile Any -Direction Outbound -Action Block -RemoteAddress 169.254.169.254 

## Azure Arc agent Installation

Write-Host "Onboarding to Azure Arc"
# Download the package
function download() {$ProgressPreference="SilentlyContinue"; Invoke-WebRequest -Uri https://aka.ms/AzureConnectedMachineAgent -OutFile AzureConnectedMachineAgent.msi}
download

# Install the package
msiexec /i AzureConnectedMachineAgent.msi /l*v installationlog.txt /qn | Out-String

# Run connect command
 & "$env:ProgramFiles\AzureConnectedMachineAgent\azcmagent.exe" connect `
 --use-device-code `
 --resource-group $env:resourceGroup `
 --tenant-id $env:tenantId `
 --location $env:location `
 --subscription-id $env:subscriptionId `
 --tags "Project=jumpstart_azure_arc_servers" `
 --correlation-id "d009f5dd-dba8-4ac7-bac9-b54ef3a6671a"

Unregister-ScheduledTask -TaskName "LogonScript" -Confirm:$False
Stop-Process -Name powershell -Force
'@ > C:\tmp\LogonScript.ps1

# Creating LogonScript Windows Scheduled Task
$Trigger = New-ScheduledTaskTrigger -AtLogOn
$Action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument 'C:\tmp\LogonScript.ps1'
Register-ScheduledTask -TaskName "LogonScript" -Trigger $Trigger -User "${adminUsername}" -Action $Action -RunLevel "Highest" -Force

# Disabling Windows Server Manager Scheduled Task
Get-ScheduledTask -TaskName ServerManager | Disable-ScheduledTask

Linux 編

Linux の場合も同様に Azure Verified Module に書き換えます
詳細は省きますが、変更後のものは こちらになります
Azure_Bicep/products/ArcEnabledLinuxServer at main · NakayamaKento/Azure_Bicep

試してみる

Bicep ファイルをそのままデプロイに使ってもいいですが、Template Spec に登録しておきます
詳しくは ARM テンプレートとBicep 03 | Template Spec を試し隊 – クラウドを勉強し隊 を参考にしてください

portal01
Template Spec:

Template Spec からデプロイします
今回はデバイスコードを使ってみます
そのため、サービスプリンシパルのためのパラメータは使わないので、適当に値を入れておきます

portal02
Template Spec:

portal03
Template Spec:

デプロイ完了後、ログインしてみると無事にスクリプトが実行されました
途中、デバイスコードでの認証が求められ、認証を行うと Azure Arc に登録されました

Linux の方も問題なくデプロイできました

まとめ

Jumpstart の ARM Template を Azure Verified Module に書き換えてみました
ついでにデバイスコードを使ったインストール方法に変更し、使い勝手をよくできたので満足

参考

共有

Kento
著者
Kento
2020年に新卒で IT 企業に入社. インフラエンジニア(主にクラウド)として活動中