はじめに
Azure マシン構成(旧称: ゲスト構成)は、Azure VM や Arc 対応サーバーの OS 内部設定を監査・修復できる機能です。
今回は以下の 3 点を実際に動作確認しました。
- 組み込みポリシーによる監査(パスワード複雑性)
- カスタム GuestConfiguration パッケージの作成と登録
- 修復タスクによる設定の自動修復(ApplyAndAutoCorrect モード)
組み込みポリシーによる監査
まず、組み込みポリシー「Audit Windows machines that do not have the password complexity setting enabled」(ID: bf16e0bb-31e1-4646-8202-60a235cc7e74)を RG スコープに割り当てました。
$builtinDef = Get-AzPolicyDefinition -BuiltIn | Where-Object { $_.Name -eq "bf16e0bb-31e1-4646-8202-60a235cc7e74" }
$scope = "/subscriptions/<subscriptionId>/resourceGroups/rg-guestconfig-test"
New-AzPolicyAssignment `
-Name gc-builtin-test `
-PolicyDefinition $builtinDef `
-Scope $scope `
-IdentityType SystemAssigned `
-Location japaneast `
-PolicyParameterObject @{ IncludeArcMachines = "false" }コンプライアンス評価スキャンを実行後に確認すると NonCompliant となりました。検証用 VM はデフォルト設定でパスワード複雑性が無効のため、正しく検出されています。
$job = Start-AzPolicyComplianceScan -ResourceGroupName rg-guestconfig-test -AsJobGet-AzPolicyState -ResourceGroupName "rg-guestconfig-test" |
Where-Object { $_.PolicyAssignmentName -eq "gc-builtin-test" } |
Select-Object PolicyAssignmentName, ComplianceState
PolicyAssignmentName ComplianceState
-------------------- ---------------
gc-builtin-test NonCompliantカスタム GuestConfiguration パッケージの作成
DSC 設定の記述
カスタムパッケージは PowerShell DSC で設定を記述し、MOF にコンパイルしてからパッケージ化します。今回は 3 つの設定を作成しました。
1つ目はタイムゾーンの設定です。DSCでは ComputerManagementDsc モジュールの TimeZone リソースを使用します。
注意点として、タイムゾーンの設定には PSDscResources ではなく ComputerManagementDsc モジュールが必要です。PSDscResources には TimeZone リソースが含まれていないため、誤ったモジュールを指定するとコンパイルが失敗します。
Configuration CheckTimeZone {
Import-DscResource -ModuleName ComputerManagementDsc
Node localhost {
TimeZone SetTimeZone {
IsSingleInstance = "Yes"
TimeZoneName = "Tokyo Standard Time"
}
}
}
CheckTimeZone -OutputPath "output/temp/mof/CheckTimeZone"2つ目は最小パスワード長(MinPasswordLength)の設定です。DSC では SecurityPolicyDsc モジュールの AccountPolicy リソースを使用します。
Configuration MinPasswordLength {
Import-DscResource -ModuleName SecurityPolicyDsc
Node localhost {
AccountPolicy SetMinPasswordLength {
Name = 'MinPasswordLength'
Minimum_Password_Length = 14
}
}
}
MinPasswordLength -OutputPath "mof/MinPasswordLength"3つ目はレジストリ設定(DontDisplayLastUsername)です。実装には PSDscResources モジュールの Registry リソースを使用します。
Configuration DontDisplayLastUsername {
Import-DscResource -ModuleName PSDscResources
Node localhost {
Registry DontDisplayLastUsername {
Key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
ValueName = 'DontDisplayLastUserName'
ValueData = '1'
ValueType = 'Dword'
Ensure = 'Present'
}
}
}
DontDisplayLastUsername -OutputPath "mof/DontDisplayLastUsername"MOF のコンパイルは PowerShell 5.1 で実行します。DSC 設定ファイルを .ps1 として保存し、PowerShell 5.1 で実行することで localhost.mof が生成されます。
# PowerShell 5.1 で実行
powershell.exe -Command {
. .\CheckTimeZone.ps1
CheckTimeZone -OutputPath "output/temp/mof/CheckTimeZone"
}実行すると指定した出力先に localhost.mof が生成されます。
Directory: output\temp\mof\CheckTimeZone
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2026/03/07 xx:xx xxxx localhost.mofパッケージの作成とアップロード
MOF をコンパイルした後、PowerShell 7 の GuestConfiguration モジュールでパッケージを作成します。-Type AuditAndSet を指定することで監査だけでなく設定の修復(Set)も行えます。
Import-Module GuestConfiguration
New-GuestConfigurationPackage -Name CheckTimeZone `
-Configuration "output/temp/mof/CheckTimeZone/localhost.mof" `
-Type AuditAndSet -Path "output/temp/packages" -Force
New-GuestConfigurationPackage -Name MinPasswordLength `
-Configuration "mof/MinPasswordLength/localhost.mof" `
-Type AuditAndSet -Path "output/temp/packages" -Force
New-GuestConfigurationPackage -Name DontDisplayLastUsername `
-Configuration "mof/DontDisplayLastUsername/localhost.mof" `
-Type AuditAndSet -Path "output/temp/packages" -Force今回は、パッケージをプライベートエンドポイント経由でのみアクセス可能なストレージアカウントに保存しました。そのうえで、VM のシステム割り当てマネージド ID に Storage Blob Data Reader ロールを付与することで、GuestConfig エージェントがプライベートエンドポイント経由でパッケージを取得できるようにします
New-AzStorageAccount -ResourceGroupName rg-guestconfig-test -Name "<storage-account-name>" -Location japaneast -SkuName Standard_LRS -Kind StorageV2 -PublicNetworkAccess Disabledただし、仮想マシンのマネージド ID を直接 RBAC で使用すると上限の4000 に抵触しやすくなってしまうため。システム割り当てマネージド ID をグループにまとめて、ストレージアカウントに対して Storage Blob Data Reader を付与しました。
$gcGroupId = "72fa1ccc-f653-4222-a10e-a316595a22ba"
$storageId = (Get-AzStorageAccount -ResourceGroupName rg-guestconfig-test -Name <storage-account-name>).Id
New-AzRoleAssignment -ObjectId $gcGroupId -RoleDefinitionName "Storage Blob Data Reader" -Scope $storageIdカスタムポリシー定義と割り当て
ポリシー定義 JSON の生成
New-GuestConfigurationPolicy コマンドレットを使用してポリシー定義 JSON を生成します。-Mode ApplyAndAutoCorrect を指定することで、修復タスクが実行された際に設定が自動適用されます。
Import-Module GuestConfiguration
New-GuestConfigurationPolicy `
-LocalContentPath 'output\temp\packages\CheckTimeZone.zip' `
-ContentUri "https://<storage-account-name>.blob.core.windows.net/guestconfig-packages/CheckTimeZone.zip" `
-DisplayName 'Audit Tokyo Standard Time' `
-Description 'Checks whether the time zone is set to Tokyo Standard Time' `
-PolicyId "6ee089f1-f126-4cec-9c2d-9ab26c928b40" `
-Path 'output\temp\policies\CheckTimeZone' `
-Platform Windows `
-PolicyVersion '1.0.0' `
-Mode ApplyAndAutoCorrect `
-UseSystemAssignedIdentity
New-GuestConfigurationPolicy `
-LocalContentPath 'output\temp\packages\MinPasswordLength.zip' `
-ContentUri "https://<storage-account-name>.blob.core.windows.net/guestconfig-packages/MinPasswordLength.zip" `
-DisplayName 'Audit Minimum Password Length (14)' `
-Description 'Checks whether minimum password length is set to 14 or more' `
-PolicyId "<policy-id>" `
-Path 'output\temp\policies\MinPasswordLength' `
-Platform Windows `
-PolicyVersion '1.0.0' `
-Mode ApplyAndAutoCorrect `
-UseSystemAssignedIdentity
New-GuestConfigurationPolicy `
-LocalContentPath 'output\temp\packages\DontDisplayLastUsername.zip' `
-ContentUri "https://<storage-account-name>.blob.core.windows.net/guestconfig-packages/DontDisplayLastUsername.zip" `
-DisplayName 'Audit DontDisplayLastUserName Registry Setting' `
-Description 'Checks whether DontDisplayLastUserName registry value is set to 1' `
-PolicyId "<policy-id>" `
-Path 'output\temp\policies\DontDisplayLastUsername' `
-Platform Windows `
-PolicyVersion '1.0.0' `
-Mode ApplyAndAutoCorrect `
-UseSystemAssignedIdentityポリシー定義の登録
生成した JSON を使ってポリシー定義を登録します。
$subId = "<subscriptionId>"
New-AzPolicyDefinition -Name gc-custom-timezone `
-Policy "output/temp/policies/CheckTimeZone/CheckTimeZone_DeployIfNotExists.json" `
-SubscriptionId $subId -Mode All
New-AzPolicyDefinition -Name gc-custom-minpassword `
-Policy "output/temp/policies/MinPasswordLength/MinPasswordLength_DeployIfNotExists.json" `
-SubscriptionId $subId -Mode All
New-AzPolicyDefinition -Name gc-custom-dontdisplaylast `
-Policy "output/temp/policies/DontDisplayLastUsername/DontDisplayLastUsername_DeployIfNotExists.json" `
-SubscriptionId $subId -Mode Allポリシーの割り当てと Contributor ロールの付与
ポリシーを割り当てる際、-IdentityType SystemAssigned でシステム割り当てマネージド ID を指定します。このマネージド ID が deployIfNotExists 効果の実行(= GuestConfigurationAssignment リソースのデプロイ)を行うために RBAC が必要になります。今回は共同作成者ロールを使いましたが、実際にはカスタムロールを利用して権限を最小化しましょう。
$subId = "<subscriptionId>"
$scope = "/subscriptions/<subscriptionId>/resourceGroups/rg-guestconfig-test"
$policyDef = Get-AzPolicyDefinition -Name gc-custom-timezone -SubscriptionId $subId
$minPwdDef = Get-AzPolicyDefinition -Name gc-custom-minpassword -SubscriptionId $subId
$dontDisplayDef = Get-AzPolicyDefinition -Name gc-custom-dontdisplaylast -SubscriptionId $subId
New-AzPolicyAssignment `
-Name gc-custom-timezone-test `
-PolicyDefinition $policyDef `
-Scope $scope `
-IdentityType SystemAssigned `
-Location japaneast
New-AzPolicyAssignment `
-Name gc-minpassword-test `
-PolicyDefinition $minPwdDef `
-Scope $scope `
-IdentityType SystemAssigned `
-Location japaneast
New-AzPolicyAssignment `
-Name gc-dontdisplaylast-test `
-PolicyDefinition $dontDisplayDef `
-Scope $scope `
-IdentityType SystemAssigned `
-Location japaneast
$tzA = Get-AzPolicyAssignment -Name gc-custom-timezone-test -Scope $scope
$mpA = Get-AzPolicyAssignment -Name gc-minpassword-test -Scope $scope
$ddA = Get-AzPolicyAssignment -Name gc-dontdisplaylast-test -Scope $scope
New-AzRoleAssignment `
-RoleDefinitionName Contributor `
-ObjectId $tzA.IdentityPrincipalId `
-Scope $scope
New-AzRoleAssignment `
-RoleDefinitionName Contributor `
-ObjectId $mpA.IdentityPrincipalId `
-Scope $scope
New-AzRoleAssignment `
-RoleDefinitionName Contributor `
-ObjectId $ddA.IdentityPrincipalId `
-Scope $scope修復前の状態確認(Before)
修復タスクを実行する前に WinRM HTTPS (Port 5986) 経由で PSSession を確立し、VM 内の設定値を確認しました。
$so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$sess = New-PSSession -ComputerName "10.1.0.18" -Credential $cred -UseSSL -SessionOption $so -Port 5986
Invoke-Command -Session $sess -ScriptBlock {
Write-Host "TimeZone: $((Get-TimeZone).Id)"
Write-Host (net accounts | Where-Object { $_ -like "*Minimum password length*" })
Write-Host "DontDisplayLastUserName: $((Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -Name DontDisplayLastUserName -ErrorAction SilentlyContinue).DontDisplayLastUserName)"
}
TimeZone: UTC
Minimum password length: 0
DontDisplayLastUserName: 0タイムゾーンが UTC、最小パスワード長が 0、ログイン画面の前回ユーザー名表示が有効(0)という状態です。
修復タスクの実行
コンプライアンス評価スキャン
修復タスクを実行する前にコンプライアンス評価スキャンを完了させる必要があります。スキャンが完了していない状態で修復タスクを作成すると TotalDeployments = 0 となり修復が実行されません。
$job = Start-AzPolicyComplianceScan -ResourceGroupName "rg-guestconfig-test" -AsJobスキャン完了後(約 10 分)に確認すると、カスタムポリシー 3 つが NonCompliant と評価されました。
PolicyAssignmentName ComplianceState
-------------------- ---------------
gc-minpassword-test NonCompliant
gc-dontdisplaylast-test NonCompliant
gc-custom-timezone-test NonCompliant
gc-builtin-test NonCompliant修復タスクの作成
Start-AzPolicyRemediation で修復タスクを作成します。-PolicyAssignmentId には Get-AzPolicyAssignment で取得した割り当てオブジェクトの .Id プロパティを使います。
$scope = "/subscriptions/<subscriptionId>/resourceGroups/rg-guestconfig-test"
$allAssigns = Get-AzPolicyAssignment -Scope $scope
$minPwdAssign = $allAssigns | Where-Object { $_.Name -eq "gc-minpassword-test" }
$dontDisplayAssign = $allAssigns | Where-Object { $_.Name -eq "gc-dontdisplaylast-test" }
$tzAssign = $allAssigns | Where-Object { $_.Name -eq "gc-custom-timezone-test" }
Start-AzPolicyRemediation -Name "gc-remediation-minpwd" `
-PolicyAssignmentId $minPwdAssign.Id `
-ResourceDiscoveryMode ExistingNonCompliant `
-ResourceGroupName "rg-guestconfig-test"
Start-AzPolicyRemediation -Name "gc-remediation-dontdisplay" `
-PolicyAssignmentId $dontDisplayAssign.Id `
-ResourceDiscoveryMode ExistingNonCompliant `
-ResourceGroupName "rg-guestconfig-test"
Start-AzPolicyRemediation -Name "gc-remediation-timezone" `
-PolicyAssignmentId $tzAssign.Id `
-ResourceDiscoveryMode ExistingNonCompliant `
-ResourceGroupName "rg-guestconfig-test"約 2 分後に確認すると、3 つの修復タスクが Succeeded、TotalDeployments = 1、SuccessfulDeployments = 1 になりました。
Get-AzPolicyRemediation -ResourceGroupName "rg-guestconfig-test" |
Select-Object Name, ProvisioningState, `
@{N='Total';E={$_.DeploymentSummary.TotalDeployments}}, `
@{N='Succeeded';E={$_.DeploymentSummary.SuccessfulDeployments}}
Name ProvisioningState Total Succeeded
---- ----------------- ----- ---------
gc-remediation-timezone Succeeded 1 1
gc-remediation-dontdisplay Succeeded 1 1
gc-remediation-minpwd Succeeded 1 1修復の仕組み
修復タスクが deployIfNotExists ポリシーを実行すると、VM 上に GuestConfigurationAssignment リソースが作成されます。この Assignment の assignmentType は ApplyAndAutoCorrect となっており、VM 上の Guest Configuration Service(GCService)がポーリング間隔(デフォルト 5 分)で設定を自動適用します。
GuestConfigurationAssignment の状態は REST API で確認できます。名前に $ 文字が含まれる場合は URL エンコードが必要です($ → %24)。
az rest --method get `
--url "https://management.azure.com/subscriptions/<subscriptionId>/resourceGroups/rg-guestconfig-test/providers/Microsoft.Compute/virtualMachines/vm-guestconfig-azure/providers/Microsoft.GuestConfiguration/guestConfigurationAssignments/MinPasswordLength%24pidptko63snurv7y?api-version=2022-01-25" `
--query "{compliance:properties.complianceStatus, assignmentType:properties.guestConfiguration.assignmentType}" -o json{
"assignmentType": "ApplyAndAutoCorrect",
"compliance": "Compliant"
}修復後の状態確認(After)
修復タスク完了後に再度 PSSession で VM 内の設定値を確認しました。
Invoke-Command -Session $sess -ScriptBlock {
Write-Host "=== After Values ==="
Write-Host "TimeZone: $((Get-TimeZone).Id)"
Write-Host (net accounts | Where-Object { $_ -like "*Minimum password length*" })
Write-Host "DontDisplayLastUserName: $((Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' -Name DontDisplayLastUserName -ErrorAction SilentlyContinue).DontDisplayLastUserName)"
}=== After Values ===
TimeZone: Tokyo Standard Time
Minimum password length: 14
DontDisplayLastUserName: 13 項目すべてが期待通りに変更されました。
| 設定項目 | Before | After |
|---|---|---|
| タイムゾーン | UTC | Tokyo Standard Time |
| 最小パスワード長 | 0 | 14 |
| ログイン画面の前回ユーザー名表示 | 0(表示する) | 1(表示しない) |
最終コンプライアンス状態
コンプライアンス再スキャン後(約 10 分)に確認すると、カスタムポリシー 3 つが Compliant になりました。
$job3 = Start-AzPolicyComplianceScan -ResourceGroupName "rg-guestconfig-test" -AsJobGet-AzPolicyState -ResourceGroupName "rg-guestconfig-test" |
Where-Object { $_.PolicyAssignmentName -like "gc-*" } |
Select-Object PolicyAssignmentName, ComplianceState |
Format-Table -AutoSize
PolicyAssignmentName ComplianceState
-------------------- ---------------
gc-dontdisplaylast-test Compliant
gc-minpassword-test Compliant
gc-custom-timezone-test Compliant
gc-builtin-test NonCompliantgc-builtin-test は Audit のみのポリシーで修復対象外のため NonCompliant のままです。
まとめ
Azure マシン構成を使うと、Azure Policy の deployIfNotExists 効果と GuestConfiguration パッケージを組み合わせることで仮想マシン内の OS 設定を自動的に監査・修復できます。
ただし、ポータルぽちぽちで設定できないので利用のハードルは少し高めです。ご利用は計画的に
参考リンク
- Azure マシン構成の概要 - Microsoft Learn
- カスタム マシン構成パッケージの作成 - Microsoft Learn
- マシン構成の修復オプション - Microsoft Learn
- 当サイトは個人のブログです。このブログに示されている見解や意見は個人的なものであり、所属組織の見解や意見を表明するものではありません。
- 公開情報を踏まえて正確な情報を掲載するよう努めますが、その内容の完全性や正確性、有用性、安全性、最新性について一切保証しません。
- 添付文章やリンク先などを含む本サイトの内容は作成時点でのものであり、予告なく変更される場合があります。