非永続仮想デバイス インフラストラクチャ (NP-VDI)
NP-VDI により、IT チームは仮想デスクトップを物理デバイスとともに迅速に、すべて1つの統合プラットフォームからプロビジョニングできます。 組み込みの自動管理により、時間を節約し、手動によるオーバーヘッドを削減し、クラウド環境での包括的な エンドポイント制御を実現します。
主要ワークフロー
ベース イメージへのエージェント配布:
-
エージェントをクリーン VM にインストールします (登録はしない)。
-
次にエージェントを、提供されたコマンドライン ツールを使用して「ベース イメージ」として手動で登録します。
ベース イメージの保護:
-
ベース イメージ エージェントが登録され、構成が検証された後、VM をコマンド ライン経由で「保護」します。
インスタンスの生成:
-
新しい NP-VDI インスタンスはベース イメージからスピンアップでき、通常はセッションごとに新しい MAC アドレスが生成されます。
-
Ivanti Neurons は、これらのインスタンスを、ベース イメージ下で管理される一意の短期エンドポイントとして検出し、追跡します。
ポリシー管理:
-
ポリシー変更は、ベース イメージ レベルで行えます。 生成されたすべてのインスタンスは、最後に適用されたポリシーを、次回チェックイン時に継承します。
手順1: ベース イメージの準備
ベース イメージを準備するには、次の手順に従います。
- [エージェント] > [エージェント配布] > [手動インストール] に移動します。
- [手動インストール] タブで、エージェント ポリシーと登録キーを選択します。
- [Windows] を選択します。 次に、[インストール モード] を選択して [ダウンロード] をクリックします。
エージェント インストーラがダウンロードされます。このアクションにより、2つのファイルがダウンロードされます。 ファイル名「IvantiCloudAgent.exe.options」でインストーラを保存します。 このファイルはインストールに使用されます。
- ダウンロードした NP-VDI エージェントをインストールします。
エージェントをインストールするには:- インストールする前に、関連付けられた IvantiCloudAgent.exe.options ファイルの名前を変更します。
- インストーラを実行します。 アクティベーション キーまたは Cookie オプションが表示されたら、[キャンセル] をクリックします。
これにより、エージェントが、テナントに登録されずに、エンドポイントにインストールされます。
- エージェントをベース イメージとして登録します。 ベース イメージとして登録するには、次のコマンドを管理者としてエージェント インストール場所から実行します。
STAgentCtl.exe register --asBaseImage <Base Image Name> --instanceIdType MacAddress --enrollmentkey "%enrollmentkey%" --baseurl https://agentreg.ivanticloud.com
上記のコマンドの内容:
<Base Image Name> はベース イメージ デバイスの名前です。
%enrollmentkey% は tenantid_activationkey の組み合わせです。
instanceIdType は「MacAddress」、「HostName」、または「Custom」です。 これは、インスタンスを識別するための一意のフラグとして使用されます。
「カスタム」を使用する場合、カスタム タグのカンマ区切りリストを含む tagList オプションを指定して、そのエージェントを識別できます。 カスタム タグの詳細については、「カスタム タグ リスト」および 「更新タグ」セクションをご参照ください。
tenantid と activationkey はどちらも、次の例に示すように、エージェントとともにダウンロードされた、関連付けられた .options ファイル内にあります。
tenantid = 11111111-1111-1111-1111-4gc02d5b0f0d
activationkey = 9aI7SOQNybweiM2QKI7kcqIG8ufUFU21AvZwJwAEx6C-SAO5UGK62YkEgm89kvC2m-xwLesZA4t1_TBtEjIQMVaBU0KsVe-V60ZLBwWu83YREcUp3diAFvHjBvLtkxT3
cloudhost=https:agentreg.ivanticlouddev.com上記コードの登録キーは次のとおりです。
%enrollmentkey% = 11111111-1111-1111-1111-4gc02d5b0f0d_9aI7SOQNybweiM2QKI7kcqIG8ufUFU21AvZwJwAEx6C-SAO5UGK62YkEgm89kvC2m-xwLesZA4t1_TBtEjIQMVaBU0KsVe-V60ZLBwWu83YREcUp3diAFvHjBvLtkxT3「Win11」という名前のベース イメージのテナント例にデバイスを登録するためのコマンドラインは、次のようになります。
STAgentCtl.exe register --asBaseImage Win11 --instanceIdType MacAddress --enrollmentkey “11111111-1111-1111-1111-4gc02d5b0f0d_9aI7SOQNybweiM2QKI7kcqIG8ufUFU21AvZwJwAEx6C-SAO5UGK62YkEgm89kvC2m-xwLesZA4t1_TBtEjIQMVaBU0KsVe-V60ZLBwWu83YREcUp3diAFvHjBvLtkxT3" --baseurl https://agentreg.ivanticloud.com
- ベース イメージを更新するには:
- 次の Ivanti コミュニティ ポータルから PowerShell スクリプトをダウンロードします: Ivanti-Neurons - 非永続 VDI - エージェント スクリプト。
エンドポイント上の実行ポリシーによりブロックされる場合があるため、PowerShell スクリプトを実行する前に、管理者がベース イメージでこれを実行するための適切な権限を持っていることを確認します。
チェックするには、「Get-ExecutionPolicy」コマンドを使用します。
変更するには、「Set-ExecutionPolicy RemoteSigned」コマンドを使用します。
- パラメータなしでスクリプトを実行すると、スクリプトは次のことを試行します。
- エージェントの保護を解除します。
- エージェントとエンジンの両方の更新を実行します。
- エージェントを再度保護します。
- エージェントが既に保護解除されていた場合は、更新後も保護解除されたままになります。 「DoNotSeal」パラメータを渡して同じ手順 (イメージの保護を除く) を実行します。
- 更新の既定のタイムアウトは300秒です。 この時間は、「-TimeoutSec」引数を渡すことで変更できます。
追加の参考情報については、「付録」セクションでスクリプトのコピーにアクセスできます。
- ベース イメージを準備するには:
- 「STAgentCtl.exe prepareImage」コマンドを実行して、NP-VDI で利用できる、保護された、非永続的なベース イメージを作成します。
このコマンドは必須であり、すべてのサービスを停止し、ベース イメージの実際のエージェント ID に関連するデータを削除します。 非永続インスタンスが起動すると、サービスが再起動されます。エージェント ID は、ベース イメージからスピンアップされる非永続インスタンスごとに変更されます。
- 次のコマンドを使用して、ベース イメージを手動で保護します (アップグレード スクリプト外)。
- ベース イメージを手動でシールするには、「STAgentCtl.exe seal」を入力します。
「unseal」コマンドを使用して、イメージの保護を解除します。 これは、システムが再起動されるか、または「Seal」コマンドが使用されるまで、アクティブなままになります。
- ベース イメージを手動でシールするには、「STAgentCtl.exe seal」を入力します。
- 「STAgentCtl.exe prepareImage」コマンドを実行して、NP-VDI で利用できる、保護された、非永続的なベース イメージを作成します。
- (任意)「sysprep」で使用するイメージを準備します。
非永続デスクトップの準備に sysprep ステージが含まれていると、エージェント証明書のプライベート鍵が証明書ストアから削除されます。 この問題に対処するには、次の手順に従います。管理者が独自の証明書エクスポート パスワードを提供する必要があります。そうでないと、このプロセスは失敗します。
- certutil -store “Ivanti Cloud Agent Agent” コマンドを使用して、証明書のシリアル番号を検索します。
これにより、エージェント証明書ストア内の証明書がリストされます。 - 手順7でコマンド「stagentctl prepareImage」を実行した後、次のコマンドを使用してエージェント証明書をエクスポートできます。
certutil -exportPFX -privatekey -p "%pfxpass%" “Ivanti Cloud Agent Agent” %serialnumber% "%ProgramFiles%\Ivanti\Ivanti Cloud Agent\agent.pfx" NoChain,NoRoot
上記のコマンドの内容:
%pfxpass% は管理者が提供したパスワードです。
%serialnumber% は見つかったエージェント証明書のシリアル番号です。
- Ivanti のコミュニティ Web サイト: エクスポートされた .pfx ファイル スクリプトからプライベート鍵を復元するから入手可能な SetupComplete.cmd スクリプトをダウンロードします。
- SetupComplete.cmd スクリプトを編集し、手順2で使用した証明書のエクスポートに使用したパスワードと一致するようにパスワードを更新します。
- スクリプトをゴールド イメージの %WINDIR%\Setup\Scripts\SetupComplete.cmd に保存します。
Windows セットアップが完了すると、スクリプトが自動的に実行されます (これは、sysprep 後にデバイスが初めて起動されたときに実行されます)。これにより、エクスポートされた .pfx ファイルからプライベート鍵が復元された後、.pfx ファイルとスクリプト自体の両方が削除されます。したがって、シークレットが非永続的インスタンスに漏出することはありません。
subject OU が「ST Agent」の証明書 (例: 「Subject: OU=ST Agent, CN=011472EFB12BABF06122F2903BBE12AC4E32EBFE5082」) を、そのエントリの対応するシリアル番号を使用してエクスポートする必要があります。
- certutil -store “Ivanti Cloud Agent Agent” コマンドを使用して、証明書のシリアル番号を検索します。
手順2: NP-VDI インスタンスの作成とテスト
ベース イメージから新しいインスタンスをスピンするには、次の手順に従います。
-
新しい各インスタンスに新しい MAC アドレスが割り当てられていることを確認します (instanceIdType MacAddress オプションが動作することを確認するため)。
または、別のホスト名またはタグが使用されていることを確認します。 -
仮想マシン インスタンスを起動し、インスタンスが一意の NP-VDI エンドポイントとして Neurons UI に表示されることを確認します。 これを行うには、Ivanti Neurons インターフェイスの [エージェント] > [エージェント] 管理セクションに移動します。
デバイスがベース イメージ デバイスなのか、非永続デバイスなのか、永続デバイスなのかを示す、新しい [VDI タイプ] 列が利用可能です。 -
各エージェント エンドポイントが [エージェント管理] ページから [デバイス] ページにリンクされるようになりました。 エージェント エンドポイント名をクリックすると、デバイス ビューにデバイスを表示する新しいウィンドウが開きます。
-
[リンクされているインスタンス]オプションをクリックして、このベース イメージからスピンアップされたすべてのインスタンスを表示します。
非永続インスタンスは、デバイス ビューに非永続としてタグ付けされます。
手順3: NP-VDI を使用したポリシーの再割り当て
Ivanti Neurons インターフェイスから直接、ベース イメージにポリシーを再割り当てするには、次の手順に従います。
-
[エージェント] > [エージェント ポリシー] に移動します。
-
ベース イメージを選択し、[アクション] メニューから [ポリシーの再割り当て] を選択します。
-
再割り当てするポリシーを選択し、[保存] をクリックします。
これにより、ポリシーが再割り当てされます。 ポリシーがベース イメージに再割り当てされると、すべての非永続 VDI インスタンスは次回チェックイン時 (既定では200分ごと) にポリシーを継承します。 -
Ivanti Neurons エージェントまたはエージェント管理インターフェイスを使用するか、非永続イメージ自体から、コマンド ライン「Stagentctl status」を使用して、新しいインスタンスがチェックイン後に更新されたポリシーを継承していることを確認します。
NP-VDI インスタンスで再起動アクションを実行することはできません。 これを行うと、デバイスが初期状態にリセットされ、ユーザがセッション中に完了した作業が失われることになります。
また、非永続インスタンスのデバイスビューで使用可能なアクションには、再起動アクションは含まれていません。
追加の STAgentCtle.exe コマンド
次の追加のコマンドを使用できます。
-
更新のためにベース エージェントを開くには、「Stagentctl.exe unseal」コマンドを入力します。
-
更新が完了するまで待ってから制御を返すようにコマンド ラインに強制するには、「Stagentctl.exe update -wait」コマンドを入力します。
「wait」コマンドは、エージェントの実行ファイルが実行中であることを必要とするため、エージェントが更新されるときに問題が発生する場合があり、この場合、更新後の再起動が必要になります。 この問題を回避するため、Ivanti では、同期更新コマンドの実行を許可する PowerShell スクリプトを提供しています。
-
更新のためにベース エージェントを閉じるには、「Stagentclt.exe seal」コマンドを入力します。
カスタム タグ リスト
カスタム タグ リストは、NP-VDI インスタンスも含め、Ivanti Neurons でエンドポイントに割り当てることができるユーザ定義のタグ コレクションです。 カスタム タグは、ビジネス、運用、または技術要件に基づいてデバイスを分類、整理、識別するのに役立ちます。
たとえば、タグ「Sales-[MacAddress]」を使用すると、[MacAddress] をデバイスの実際の MAC アドレスに置き換えることで、各デバイスの一意のタグが生成されます。 この機能は、非永続 VDI インスタンスの一意のタグを生成する場合に特に有用です。
デバイス属性と環境変数を参照するには、次の構文を使用します。
-
MAC アドレスを参照するには、"[MacAddress]" を使用します。
-
ホスト名を参照するには、"[HostName]" を使用します。
-
環境変数を参照するには、"[env:<name>]" を使用します。
ここで、<name> は大文字と小文字が区別される環境変数名です。
タグの更新
ベース イメージ登録中に割り当てられたタグは、関連付けられたすべてのインスタンスで静的なままです。 タグを更新する必要がある場合、ベース イメージを再登録/再保護するか、自動化を使用して VM 配布後に現在の条件に基づいてタグを割り当てる必要があります。
付録
.SYNOPSIS
非永続エージェントを更新し、更新の完了を待機してから、任意でエージェントを保護します。
.PARAMETER TimeoutSec
合計タイムアウト (秒)。
.PARAMETER UpgradeCode
更新中のエージェントのコードをアップグレードします。
.PARAMETER DoNotSeal
このパラメータは、更新後にエージェントを保護しないことを指定します。
#>
param
(
[int] $TimeoutSec = 300,
[guid] $UpgradeCode = "{0C5D7E9C-0424-462D-A570-2A7469EE3739}", # Neurons Agent
[switch] $DoNotSeal
)
function Check-UpdatersComplete
{
$updaters = Get-Process -Name "STAgentUpdater" -ErrorAction SilentlyContinue
return ($updaters.Count -eq 0)
}
function Wait-UpdatersComplete([DateTime] $timeoutDateTime, [int] $checkIntervalSec)
{
while ((Get-Date) -lt $timeoutDateTime)
{
if (Check-UpdatersComplete)
{
return
}
Start-Sleep -Seconds $checkIntervalSec
}
throw "すべての更新の完了待機がタイムアウトしました。"
}
function Get-AgentVersionFromManifest([string] $manifestPath, [string] $platformKey)
{
if (-not (Test-Path $manifestPath))
{
throw "'$manifestPath' でマニフェストが見つかりませんでした。"
}
[xml]$manifestXml = Get-Content $manifestPath
$agentFile = $manifestXml.manifest.files.file | Where-Object { $_.key -eq $platformKey }
if (-not ($agentFile))
{
throw "key='$platformKey' を持つエージェント エントリが見つかりません。"
}
if (-not ($agentFile.version))
{
throw "マニフェストでエージェント バージョンを検索できません。"
}
return [version]::Parse($agentFile.version)
}
function Get-CurrentAgentVersion([guid] $agentUpgradeCode)
{
$agentProductCode = Get-ProductCode -UpgradeCode $agentUpgradeCode
$agentObject = Get-WmiObject -Class Win32_Product | Where-Object { [guid]::Parse($_.IdentifyingNumber) -eq $agentProductCode }
if (-not ($agentObject))
{
throw "インストールされたエージェントが見つかりません。"
}
if (-not ($agentObject.Version))
{
throw "インストールされたエージェント バージョンが見つかりません。"
}
return [version]::Parse($agentObject.Version)
}
function Check-AgentUpToDate([string] $manifestPath, [guid] $agentUpgradeCode, [string] $platformKey)
{
$manifestVersion = Get-AgentVersionFromManifest -ManifestPath $manifestPath -PlatformKey $platformKey
$currentVersion = Get-CurrentAgentVersion -AgentUpgradeCode $agentUpgradeCode
return ($currentVersion -ge $manifestVersion)
}
function Wait-AgentUpdated([DateTime] $timeoutDateTime, [int] $checkIntervalSec, [string] $manifestPath, [guid] $agentUpgradeCode, [string] $platformKey)
{
while ((Get-Date) -lt $timeoutDateTime)
{
if (Check-AgentUpToDate -ManifestPath $manifestPath -AgentUpgradeCode $agentUpgradeCode -PlatformKey $platformKey)
{
return
}
Start-Sleep -Seconds $checkIntervalSec
}
throw "エージェント更新の待機がタイムアウトしました。"
}
function Update-Agent([DateTime] $timeoutDateTime, [string] $manifestPath, [guid] $agentUpgradeCode, [string] $agentCtlPath, [string] $platformKey)
{
$timeoutSec = [math]::Ceiling(($timeoutDateTime - (Get-Date)).TotalSeconds)
if ($timeoutSec -le 0)
{
throw "タイムアウトが短いため、チェックインを実行できません。"
}
& $agentCtlPath update --checkin --wait $timeoutSec
if (-not $?)
{
throw "チェックイン操作が失敗しました。"
}
& $agentCtlPath update --all
if (-not $?)
{
throw "更新操作が失敗しました。"
}
Wait-AgentUpdated -TimeoutDateTime $timeoutDateTime -CheckIntervalSec 1 -ManifestPath $manifestPath -AgentUpgradeCode $agentUpgradeCode -PlatformKey $platformKey
Wait-UpdatersComplete -TimeoutDateTime $timeoutDateTime -CheckIntervalSec 1
$timeoutSec = [math]::Ceiling(($timeoutDateTime - (Get-Date)).TotalSeconds)
if ($timeoutSec -le 0)
{
throw "タイムアウトが短いため、最終更新を実行できません。"
}
& $agentCtlPath update --all --wait $timeoutSec
if (-not $?)
{
throw "最終更新操作が失敗しました。"
}
}
function Get-ProductCode([guid] $upgradeCode)
{
$upgradeCodeStr = $upgradeCode.ToString("B") # Format as {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
$wmiFilter = "Property='UpgradeCode' AND Value='$upgradeCodeStr'"
$productCode = @(Get-WmiObject -Class Win32_Property -Filter $wmiFilter).ProductCode
if ($productCode.Count -eq 0)
{
throw "アップグレード コード '$upgradeCode' が見つかりません。"
}
if ($productCode.Count -gt 1)
{
throw "同じアップグレード コード '$upgradeCode' を持つ製品が複数見つかりました。"
}
return [guid]::Parse($productCode)
}
function Get-AgentEnvironment([string] $installDirPath)
{
$configPath = Join-Path $installDirPath "AgentEnvironment.config"
if (-Not (Test-Path $configPath))
{
throw "$configPath に構成ファイルが見つかりません。"
}
[xml]$agentEnvironment = Get-Content $configPath
return $agentEnvironment
}
function Get-InstallLocation([guid] $productCode)
{
$productObject = Get-WmiObject -Class Win32_Product | Where-Object { [guid]::Parse($_.IdentifyingNumber) -eq $productCode }
if ($null -eq $productObject)
{
throw "製品 GUID が見つかりませんでした。"
}
return $productObject.InstallLocation
}
$timeoutDateTime = (Get-Date).AddSeconds($TimeoutSec)
Write-Host "情報を収集しています..."
try
{
$agentProductCode = Get-ProductCode -UpgradeCode $UpgradeCode
$installDirPath = Get-InstallLocation -ProductCode $agentProductCode
Write-Host "インストール ディレクトリ: $installDirPath"
$agentEnvironment = Get-AgentEnvironment -InstallDirPath $installDirPath
$dataDirPath = $agentEnvironment.agentEnvironment.agentDataDirectory
Write-Host "データ ディレクトリ: $dataDirPath"
$regPath = Join-Path "HKLM:" $agentEnvironment.agentEnvironment.registryPath
Write-Host "レジストリ キー: $regPath"
$platformKey = Get-ItemProperty -Path $regPath -Name "PlatformManifestKey" -ErrorAction Stop | Select-Object -ExpandProperty "PlatformManifestKey" -ErrorAction Stop
Write-Host "プラットフォーム キー: $platformKey"
$agentCtlPath = Join-Path $installDirPath "STAgentCtl.exe"
$manifestPath = Join-Path $dataDirPath "manifest.xml"
}
catch
{
Write-Error "エラー: $_"
exit -1
}
if ((Get-Date) -gt $timeoutDateTime)
{
Write-Error "エラー: エージェント更新を開始する前にタイムアウトが期限切れになりました。"
exit -1
}
Write-Host "エージェントの保護を解除しています..."
& $agentCtlPath unseal
if (-not $?)
{
Write-Error "エラー: 保護解除操作を実行できません。"
exit -1
}
Write-Host "エージェントを更新しています..."
$result = -1
try
{
Update-Agent -TimeoutDateTime $timeoutDateTime -ManifestPath $manifestPath -AgentUpgradeCode $UpgradeCode -AgentCtlPath $agentCtlPath -PlatformKey $platformKey
$result = 0
}
catch
{
Write-Error "エラー: $_"
}
if ($DoNotSeal)
{
if ($result -eq 0)
{
Write-Host "エージェントが正常に更新されました。"
}
else
{
Write-Error "エラー: エージェント更新に失敗しました。"
}
exit $result
}
Write-Host "エージェントを保護しています..."
& $agentCtlPath seal
if (-not $?)
{
Write-Error "エラー: エージェントを保護できません"
exit -1
}
if ($result -eq 0)
{
Write-Host "エージェントが正常に更新され、保護されました。"
}
exit $result
(@ECHO OFF
set pw=supersecret
set logfile=C:\ProgramData\Ivanti\Ivanti Cloud Agent\Logs\agentkeyrestore.log
set certFile=C:\Program Files\Ivanti\Ivanti Cloud Agent\agent.pfx
set scriptPath=%0%
ECHO %date% %time% エージェント キーの復元が開始されました >> "%logfile%"
IF EXIST "%certFile%" (
certutil -f -p "%pw%" -privatekey -importPFX "Ivanti Cloud Agent Agent" "%certFile%" NoCert,NoExport >> "%logfile%" 2>&1
IF %ERRORLEVEL% NEQ 0 (
ECHO エージェント キーのインポートに失敗しました >> "%logfile%"
)
ECHO エージェント キー ファイルを削除しています >> "%logfile%"
del "%certFile%" >> "%logfile%" 2>&1
IF %ERRORLEVEL% NEQ 0 (
ECHO エージェント キー ファイルの削除に失敗しました >> "%logfile%"
)
ECHO %date% %time% エージェント キーの復元が完了しました >> "%logfile%"
) ELSE (
ECHO %date% %time% エージェント キーの復元に失敗しました: エージェント キー ファイルが見つかりません >> "%logfile%"
)
ECHO スクリプトを削除しています >> "%logfile%"
del "%scriptPath%" >> "%logfile%" 2>&1
"REM スクリプト ファイルが削除されたため、この時点から、これ以上コマンドを実行することはできません"