はじめに
Azure VM を利用しているときに、VM の設定や構成を管理したい場合があると思います。
この構成の管理の方法としては、Intune や Active Directory などのツールを利用する方法がありますが、これらの方法は、それぞれのサービスに VM を登録する必要があり、利用する環境によっては、他の管理状況の都合などで利用できないといったことがあると思います。
また、新しい環境をつくる場合でも、Intune のライセンスを持っていない、Active Directory の環境がない、などの理由で、これらを利用はできないけれども、VM の構成を管理したいということがあると思います。
こういった需要に対して、Azure では、マシンの構成という機能を提供しています。
本記事では、マシンの構成の概要と、どのように動いて、どう設定するかについて、調べて試した内容を解説していきます。
マシンの構成の概要
マシンの構成は、Azure VM や Azure Arc 対応のサーバーに対して、構成の管理を行うことができるリソースです。
ここで、「リソース」と書いた理由は、マシンの構成は、Azure 上の 拡張リソース として提供されているためです。
拡張リソースとは、他のリソースに機能を追加するリソースのことです。
マシンの構成は、Azure VM や Azure Arc 対応のサーバーに対する拡張リソースですが、例えば、Azure リソースのロック等も拡張リソースとして提供されていて、ARM テンプレートや Bicep 等を利用して設定することが可能です。
マシンの構成と他のサービスの使い分け
OS 上の構成管理をする方法は、いくつかあります。
代表的なのは、Intune や Active Directory Domain Services などのサービスを利用する方法です。
これに関しては、使いどころが異なってくると思いますので、マシンの構成と Intune、Active Directory Domain Services(Group Policy)の違いと使い分けについてまとめます。
マシンの構成とIntune、Active Directory Domain Services(Group Policy)の違い
以下に、Microsoft の代表的な構成管理サービスである、マシンの構成、Intune、Active Directory Domain Services(Group Policy)の違いをまとめます。
| 観点 | マシンの構成 | Intune | AD DS(Group Policy) |
|---|---|---|---|
| 主な対象 | Azure VM。 サーバー中心だが Windows 10/11 クライアントも対象 | Windows クライアント/macOS/iOS/Android/Linux デスクトップの エンドユーザーデバイス | ドメイン参加 Windows 機(サーバー・クライアント両方)。オンプレ / Azure を問わない |
| クラウド/オンプレ | Azure ネイティブ | クラウドネイティブ MDM。ネットワーク境界を問わない | ドメインコントローラへの到達性が前提。(DC は Azure にも配置できる) |
| 前提となる ID | Azure VM は system-assigned managed identity + 拡張機能 | Microsoft Entra ID に登録されたユーザー/デバイス | AD 参加したOS(ADドメインアカウント) |
| 適用の仕組み | DSC ベースの agent が周期的に準拠性と非準拠理由を報告・是正 | MDM プロトコル(CSP)でプロファイル/ポリシーを配信、コンプライアンスポリシーで判定 | GPO を用いてポリシー配信、適用 |
| ユーザー単位の設定 | 不可(マシン単位のみ) | 可(ユーザー/デバイス両方をターゲットにできる) | 可(ユーザー構成 / コンピューター構成) |
| 準拠状況の可視化 | Azure Portal や Resource Graph(他 Azure リソースと横断可能) | Intune 管理センターのレポート |
gpresult / RSOP。集約されたコンプライアンスダッシュボードは持たない |
| ネットワーク要件 | 対象マシンから Machine Configuration service / 設定ファイルの配置先へ outbound 443 | インターネット経由で Intune サービスへ到達 | ドメインコントローラへの内部ネットワーク到達性が必須 |
つまり、Azure VM において、サーバー OS を中心に、Linux/Windows ともに比較的幅広く構成管理を行いたい場合は、マシンの構成が適していると考えられます。
なお、特にエンタープライズレベルの環境での、これらの構成管理の考え方に関しては、各社で異なると思います。
実際の検討においては、各社の管理ポリシー等をご確認いただいて、各種機能の適応性をご検討いただければと思います。
マシンの構成の構成要素とどのように動くか
マシンの構成を利用するためには、いくつかの準備と、実際に適用するステップが必要になります。
まずはこの概要について、大きくマシン構成を作成するステップと適用するステップに分けて解説していきます。
マシンの構成を作成して割り当てるステップ
マシンの構成では、OS上の状態が、事前に定義した構成に準拠しているか、また、準拠していない場合は、どうやって準拠構成に修正するかを Desired State Configuration (DSC) を利用して定義します。
補足
もともと、PowerShell の Desired State Configuration (DSC) という拡張機能が Azure VM に対して構成管理をする方法として提供されていました。
このマシンの構成サービスは、DSC 拡張機能の後継として提供されているサービスとなります。
実装は、PowerShell スクリプトとして作成し、コンパイルして MOF ファイルを作成し、ZIP 形式でパッケージ化します。
この ZIP 形式のパッケージをデプロイして、Azure VM に割り当てることで、マシンの構成の適用が可能になります。
補足
つまり、マシンの構成においては、自由に構成を定義することができますが、定義に準拠しているかの判断、準拠していない場合の修正方法を、PowerShell を利用して自分で実装する必要があります。
作成した定義ファイルをどのように VM に適用可能な状態に設定していくかが次のステップになります。
作成したマシンの構成は、作成した構成ファイルのZIPパッケージをStorage Account 上など、VM からアクセス可能な場所に配置します。
VM に設定を反映させるためには、 Guest Assignment Service (GAS) というものを利用することになります。
具体的には、Guest Assignment Service に、パッケージの場所、構成違反の場合の対応などの情報を Assignment という形で登録します。
実際に VM に適用されるときの動き
Assignment が登録され、VM に拡張機能をインストールが完了すると、マシンの構成が動作するようになります。
具体的な、適用動作の流れを説明していきます。
マシンの構成では、VM 上にインストールされた Guest Configuration 拡張機能が、定期的に Guest Assignment Service に問い合わせを行い、自分にアサインされた構成情報を取得します。
この構成情報には、どこの定義を確認しないといけないか、パッケージ化されたモジュールのダウンロード先のURL、構成違反が検出された場合の対応 (違反を報告するだけか、修正も自動でするのか等) が記載されています。
この構成情報を元に、拡張機能はビルドされたパッケージファイルをダウンロード先から取得し、VM 上で実行します。
このパッケージファイルを実行することで、準拠状況が取得できるので、構成情報に記載されたとおりに、構成の修正や、準拠状況の報告を行います。
マシンの構成の設定方法
では、具体的に、マシンの構成に関して、どのように設定していくかを解説していきます。
一連の流れに関しては、サンプルコードをGitHubに公開していますので、そちらを参照しながら進めていくと理解がしやすいと思います。
前提条件
マシンの構成を Azure VM で動作させるには、以下のいくつかの前提条件を満たすことが必要です。
以降の手順は、これらがすべて満たされていることを前提としています。
| 前提条件 | 内容 |
|---|---|
| リソースプロバイダー | サブスクリプションで Microsoft.GuestConfiguration が登録されていること |
| マネージド ID | 対象 VM でシステム割り当てマネージド ID が有効化されていること。 マシンの構成サービスへの認証に使われます |
| 拡張機能 | 対象 VM にマシンの構成の拡張機能が導入されていること。 構成の適用(Set)まで行う場合は 1.26.24 以降が必要 |
| ネットワーク | 対象 VM から outbound 443/TCP で、マシンの構成サービスとパッケージの配置先へ到達できること |
PowerShell を利用して、マシンの構成を作成する
マシン構成は、DSC(Desired State Configuration)の後継となるサービスであり、DSC の構成ファイルを作成するのと同じように、PowerShell を利用して、具体的な構成などを作成することができます。
以下で、どういった構成で何を作成する必要があるかを記載いたします。
具体的に作成するフォルダ構成
configuration/
├── MyServiceConfig.ps1 // DSC の構成を定義する PowerShell スクリプト
└── modules/
└── MyServiceConfig/
├── MyServiceConfig.psd1 // モジュール定義ファイル。モジュールに関する情報を記載する
└── MyServiceConfig.psm1 // 具体的に構成を実装する PowerShell スクリプト。Set()、Test()、Get() の関数(詳細は後述)を実装する
つまり、マシンの構成を作成するためには、DSC の構成を定義する PowerShell スクリプト(MyServiceConfig.ps1)と、モジュール定義ファイル(MyServiceConfig.psd1)、具体的に構成を実装する PowerShell スクリプト(MyServiceConfig.psm1) の3つのファイルを実装することになります。
補足
DSC の処理の実行中に利用する設定ファイル等を追加することもできます。
今回の説明では、よりシンプルに最小構成で動かすための部分にフォーカスして記載しています。
それでは、具体的に何を実装していくか見ていきます。
モジュール定義ファイル(MyServiceConfig.psd1)の実装
まず、マシンの構成に関して、モジュール定義ファイル(MyServiceConfig.psd1)の実装について解説します。
このファイルは、実際にマシンの構成のモジュールのメタデータを記述したデータファイルとなります。
いわば、このモジュールがどういったバージョンで、誰が記載して、何をマシン構成のリソースとして提供しているかを宣言するためのファイルとなります。
以下に実装例を記載します。
@{
# module を import したときに最初に読み込む実装ファイル
RootModule = 'MyServiceConfig.psm1'
# package が依存する module の version。実装変更のたびに更新する
ModuleVersion = '1.0.3'
# module を一意に識別する固定 GUID。新規作成時に 1 回だけ生成し、以後 version を上げても変えない
# New-Guid で生成した値をそのまま貼り付ける(他の module と重複させない)
GUID = '<New-Guid で生成した GUID>'
Author = 'blog-lab'
CompanyName = 'N/A'
Copyright = '(c) blog-lab'
Description = 'sample guest configuration'
# この module から DSC resource として公開する class 名
DscResourcesToExport = @('MyServiceConfig')
# このモジュールが必要とする PowerShell 本体の最小 version
PowerShellVersion = '7.2'
}
具体的に構成を実装する PowerShell スクリプト(MyServiceConfig.psm1)の実装
次に、具体的に構成を実装する PowerShell スクリプト(MyServiceConfig.psm1)の実装について解説します。
このファイルでは、Set()、Test()、Get() という、三つの主要な関数を実装することとなります。
これらの三つの関数が何をするためのものかをまずまとめます。
| 関数名 | 役割 |
|---|---|
| Get() | 構成が準拠しているかどうかを判定するために、現在のマシンの構成情報を取得します |
| Test() | 構成が準拠しているかどうかを実際に判定する処理を実装します。多くのケースで、Get() で取得した情報を活用します |
| Set() | 構成が準拠していない場合に、準拠する状態に修正するための処理を実装します |
つまり、Get() で現在の構成情報を取得し、Test() で準拠しているかどうかを判定し、準拠していない場合は Set() で修正するという流れになります。
では、具体的な実装例を見ていきます。
GitHub 上に配置したサンプルでは、TargetPath (指定したファイルパス) にファイルが存在するかどうかという構成を確認し、ファイルが存在しない場合は、作成することを想定しています。
ここでは、具体的な実装にはフォーカスせず、構成の骨組みを示します。
enum Ensure {
Absent
Present
}
# 非準拠の理由を Machine Configuration の report へ返すための型
# class 名は module 固有名にする(他 module の Reason 型と衝突させないため)
class MyServiceConfigReason {
# program から識別・集計できる安定した理由コード
[DscProperty()]
[string] $Code
# 利用者が原因を理解するための説明文
[DscProperty()]
[string] $Phrase
}
[DscResource()]
class MyServiceConfig {
# resource instance を一意に識別する必須の Key property
[DscProperty(Key)]
[string] $Name
# Present: TargetPath のファイルを存在させる / Absent: 削除する
[DscProperty(Mandatory)]
[Ensure] $Ensure = [Ensure]::Present
# 管理対象ファイルの絶対 path。相対 path は実行時の CWD に依存するため受け付けない
[DscProperty(Mandatory)]
[string] $TargetPath
# Get() が返す非準拠理由。Configuration からは設定できない出力専用 property にする
[DscProperty(NotConfigurable)]
[MyServiceConfigReason[]] $Reasons
# Reason object の生成処理を共通化する内部 helper
hidden [MyServiceConfigReason] NewReason([string] $code, [string] $phrase) {
$reason = [MyServiceConfigReason]::new()
$reason.Code = $code
$reason.Phrase = $phrase
return $reason
}
# OS から actual state を取得し、resource と Reasons に格納して返す
[MyServiceConfig] Get() {
# パラメータの初期化
$current = [MyServiceConfig]::new()
$current.Name = $this.Name
$current.Ensure = $this.Ensure
$current.TargetPath = $this.TargetPath
$current.Reasons = @()
# 実際に、構成に準拠しているかどうか(具体的にTargetPathにファイルが存在するかどうか)を判断するためのロジックを記載
# もし、準拠していない場合は、上の例を参考に、理由を Reasons に追加する
return $current
}
# desired state なら true を返す。状態変更は行わない
[bool] Test() {
# Get() で取得した Reasons を使って準拠判定を行う
# 準拠していない場合は false を返す。準拠している場合は true を返す
$current = $this.Get()
return $current.Reasons.Count -eq 0
}
# actual state を desired state へ収束させる
[void] Set() {
# 実際に、構成に準拠していない場合は、準拠する状態に修正するためのロジックを記載
}
}
DSC の構成を定義する PowerShell スクリプト(MyServiceConfig.ps1)の実装
最後、DSC の構成を定義する PowerShell スクリプト(MyServiceConfig.ps1)の実装についてとなります。
これは、実装した「MyServiceConfig.psm1」のモジュールをどういう引数で、実行していくかを定義するファイルとなります。
つまり、マシンの構成を書いた実装を、どういうパラメータで実行するかを定義するファイルとなります。
以下が、具体的な実装例となります。
# ============================================================
# MyServiceConfig.ps1
# Guest Configuration 用 DSC 構成定義
# ============================================================
Configuration MyServiceConfig {
param(
[string]$NodeName = 'MyServiceConfig'
)
Import-DscResource -ModuleName MyServiceConfig
Node $NodeName {
# TargetPath は必ず絶対 path で指定する。agent は root で動くため、
# '~' や相対 path を書くと authoring 時の想定と実行時の対象がズレる。
MyServiceConfig Main {
Ensure = 'Present'
Name = 'my-service'
TargetPath = '/tmp/HelloWorld'
}
}
}
作成したスクリプトをコンパイルしてパッケージ化する
ここでは、作成したモジュールを実際にコンパイルして、パッケージ化する方法について解説します。
なお、Windows 向けのマシンの構成と、Linux 向けのマシンの構成では、コンパイル時に利用するモジュールが異なります。
具体的には、それぞれ以下の違いがあります。
| Windows | Linux | |
|---|---|---|
| コンパイラ version | PSDesiredStateConfiguration 2.0.7(stable, DSC v2) | PSDesiredStateConfiguration 3.0.0-beta1(prerelease, DSC v3) |
| Import-Module -RequiredVersion | 2.0.7 | 3.0.0 |
つまり、後段のコンパイル処理を行うためのコマンドは一緒ですが、その前段のモジュールのインストールと、Import-Module の version 指定が異なるということになります。
具体的には、以下の通りです。
Windows 向けのモジュールインポート方法
# 1. Windows 用 stable version をインストール
Install-Module -Name PSDesiredStateConfiguration -RequiredVersion 2.0.7 -Scope CurrentUser
# 2. 2.0.7 を明示的に import(既定でロードされる version と混在させない)
Import-Module -Name PSDesiredStateConfiguration -RequiredVersion 2.0.7 -Force
Linux 向けのモジュールインポート方法
# 1. Linux 用 prerelease version をインストール
Install-Module -Name PSDesiredStateConfiguration -RequiredVersion 3.0.0-beta1 -AllowPrerelease -Scope CurrentUser
# 2. import 時は "3.0.0"(beta1 抜き)を指定する — インストール名は beta1 でも登録される Version は 3.0.0
Import-Module -Name PSDesiredStateConfiguration -RequiredVersion 3.0.0 -Force
これまでで、Windows/Linux それぞれのモジュールのインストールと、Import-Module の version 指定が完了しました。
以降の処理では、具体的に、コンパイルして、パッケージ化していきます。
Windows/Linux 共通で実行可能で、以下の通りとなります。
# 3. GuestConfiguration モジュールをインストール
Install-Module -Name GuestConfiguration -Scope CurrentUser -Force
# 4. ローカルの DSC リソースモジュールを検索パスに追加
$configurationRoot = Join-Path (Get-Location) 'configuration'
$localModuleRoot = Join-Path $configurationRoot 'modules'
$env:PSModulePath = "$localModuleRoot$([IO.Path]::PathSeparator)$env:PSModulePath"
# 5. Configuration を dot-source して呼び出し、MOF を出力
. (Join-Path $configurationRoot 'MyServiceConfig.ps1')
$mofOutDir = Join-Path $configurationRoot 'MyServiceConfig'
MyServiceConfig -OutputPath $mofOutDir
# 6. ZIP package を作成(AuditAndSet)
$namedMof = Join-Path $mofOutDir 'MyServiceConfig.mof'
$packageOutDir = Join-Path (Get-Location) 'package'
New-Item -Path $packageOutDir -ItemType Directory -Force | Out-Null
New-GuestConfigurationPackage `
-Name MyServiceConfig `
-Configuration $namedMof `
-Type AuditAndSet `
-Path $packageOutDir `
-Force
# 7. SHA-256 を計算(assignment.bicep の contentHash に使う)
$zipPath = Join-Path $packageOutDir 'MyServiceConfig.zip'
(Get-FileHash -Path $zipPath -Algorithm SHA256).Hash
補足
New-GuestConfigurationPackage コマンドレットの Type に注意が必要です。
この Type 引数には、Audit と AuditAndSet の二種類があり、Audit は、構成の準拠状況を確認するだけが可能となります。
つまり、Set() を行えません。
なお、New-GuestConfigurationPackage コマンドレットの Type 引数の指定は、後述の Assignment 作成時に指定する assignmentType の指定とは異なる意味合いを持ちます。
この New-GuestConfigurationPackage コマンドレットの Type 引数は package が変更能力を持つかどうかを定義し、assignmentType では対象マシン上でその能力をどう使うかを定義します。
補足
このコンパイル処理を行うと、package フォルダに MyServiceConfig.zip が作成されます。
作成された ZIP ファイルを、VM からアクセス可能な Blob ストレージなどに配置することで、VM に対して Assignment を割り当て後に、VM からアクセスして、構成の適用が可能となります。
このブログでは、マシンの構成にフォーカスしているため、Blob ストレージへの配置方法については詳しく触れません。
コンパイル結果の動作テストについて
コンパイルを行うと、その後、作成したパッケージをテストしたいと思います。
このテストは、作成したパッケージをローカル環境で実行して、準拠状況の確認や、Set() の実行を行うことができます。
具体的には、以下のコマンドを用いて実行していきます。
注意
ローカル環境でテストを行う場合は、PowerShell の GuestConfiguration モジュールがインポートされている必要があります。
また、Linux 用のマシンの構成をテストする場合は、Linux 環境で、PowerShell をインストールしたうえで、実行する必要があります。
補足
Linux 環境でマシンの構成をテストする場合で、管理者権限が必要な場合は、sudo で PowerShell を起動して、実行する必要があります。
# 実行例
sudo pwsh -NoProfile -Command "Get-GuestConfigurationPackageComplianceStatus -Path '<パッケージファイルのパス>' -Verbose"
まずは、適用前の準拠状況、つまり Get() や Test() のテストは、以下のコマンドで確認します。
Get-GuestConfigurationPackageComplianceStatus -Path '<パッケージファイルのパス>' -Verbose
次に、実際に Set() を実行して、構成を修正する場合は、以下のコマンドで実行します。
Start-GuestConfigurationPackageRemediation -Path '<パッケージファイルのパス>' -Verbose
Set() の実行を行うと、準拠状態に修正されたことが期待されます。
再度、準拠状況を確認する場合は、以下のコマンドで確認します。
Get-GuestConfigurationPackageComplianceStatus -Path '<パッケージファイルのパス>' -Verbose
Assignment を作成して、VM に割り当てる
最後に、作成したパッケージを VM に割り当てるための Assignment の作成方法について解説します。
この Assignment の作成は、Bicep を利用して作成することができます。
具体的な Bicep のコード例は以下の通りです。
@description('デプロイ先リージョン')
param location string = resourceGroup().location
@description('対象 VM 名')
param vmName string
@description('Guest Configuration Assignment 名')
param guestConfigurationName string = 'MyServiceConfig'
@description('構成パッケージ ZIP の URI')
param packageContentUri string
@description('構成パッケージ ZIP の SHA-256')
param packageContentHash string
// Assignment を割り当てる VM リソースを既存リソースとして参照する
resource vm 'Microsoft.Compute/virtualMachines@2023-09-01' existing = {
name: vmName
}
// 実際の Assignment の作成
resource guestAssignment 'Microsoft.GuestConfiguration/guestConfigurationAssignments@2020-06-25' = {
name: guestConfigurationName
scope: vm // 割り当てる対象として、既存の VM リソースを指定する
location: location
properties: {
guestConfiguration: {
name: guestConfigurationName
contentUri: packageContentUri
contentHash: packageContentHash
version: '1.*'
// assignmentType は 3 種類(大文字小文字を区別する)
// Audit : 状態を報告するだけで、マシンには一切変更を加えない
// ApplyAndMonitor : 初回に一度だけ適用し、以降ドリフトしても自動修正せず報告のみ
// ApplyAndAutoCorrect : 適用したうえで、ドリフト時は次回評価で自動修正する
assignmentType: 'ApplyAndAutoCorrect'
}
}
}
上記のような Bicep コードを作成して、デプロイすることで、VM に対して Assignment が割り当てられます。
具体的にデプロイするためのコマンドは、例えば Azure CLI を利用する場合は、以下のようなコマンドでデプロイすることができます。
package_hash="$(sha256sum <package-file-path> | awk '{print $1}')"
az deployment group create \
--resource-group rg-gc-auditd \
--template-file assignment.bicep \
--parameters vmName=<VM名> \
packageContentUri=<パッケージファイルを配置したBlobストレージなどのURL> \
packageContentHash="$package_hash"
補足
パッケージファイルを配置したBlobストレージには、VM からアクセスが行われます。
そのため、作成する VM から、Blob ストレージへのアクセスが可能である必要があります。
なお、このダウンロードには、VM のシステム割り当てマネージド ID は使われません。
マネージド ID はマシンの構成サービスへの認証に使われるものなので、Blob 側は別途アクセス手段を用意する必要があります。
例えば、Blob ファイルへのアクセスに SAS トークンを利用する等が手段として利用可能です。
全体の動作のフロー
以下に、これまでに説明した、マシンの構成の設定や動作の流れについて、シーケンス図でまとめます。
まとめ
本記事では、Azure のマシンの構成について、概要から実際の設定方法までを解説しました。
マシンの構成は、Intune や Active Directory Domain Services(Group Policy)が利用できない、あるいは利用したくないという環境において、Azure VM の構成管理を実現できる選択肢です。
システム割り当てマネージド ID と拡張機能さえあれば動作するため、追加のライセンスやドメイン参加といった前提を必要としない点が大きな特徴だと言えます。
一方で、Intune や GPO のようにあらかじめ用意されたポリシーテンプレートを選ぶだけで完結する仕組みとは異なり、マシンの構成では Get()・Test()・Set() を PowerShell(DSC)で自前実装し、コンパイル・パッケージ化・Assignment のデプロイまで自分で行う必要があります。
そのぶん自由度は高いものの、構築・運用にはある程度の DSC の知識と実装コストがかかることも理解した上で採用を検討するのがよさそうです。
また、Guest Assignment Service を介した poll ベースの仕組みで動作するため、準拠状況の反映や修正の適用にはタイムラグが生じます。
即時性が求められる構成変更には不向きな場合がある点も、他サービスとの使い分けを検討する際のポイントになります。
Windows・Linux 双方の VM に対して、Azure ネイティブな仕組みだけで構成管理を実現したい場合には有力な選択肢となるはずなので、該当するユースケースがあればぜひ試してみてください。