このブログは、2016年11月に Citrix 米国本社マイクロソフトソリューションエンジニアリング部門 Ole Larsen が執筆したブログ「Azure Role Based Access Control in XenApp and XenDesktop」を日本語訳し、一部修正/加筆したものです。

https://citrixblogs.wpengine.com/2016/11/09/azure-role-based-access-control-in-xenapp-xendesktop/

■サマリー

XenApp および XenDesktop の Azure Resource Manager のサポートにより、Azure クラウドの仮想マシンのカタログを作成および管理できます。このブログ記事では、Azure サービスプリンシパルへロール権限やスコープを定義する方法、およびカスタムロールを定義したサービスプリンシパルを利用して XenAppおよびXenDesktop からAzure への接続を許可する方法について用例を交えながらお伝えしていきます。

■以下本文

Azure サービスプリンシパル
Azure Resource Manager (ARM) のサポートは、ARM プラグインと呼ばれる XenApp および XenDesktop の標準機能コンポーネントでカプセル化されています。Azure でマシンをプロビジョニングするには、関連するAzure リソースへのアクセス権が割り当てられているサービスプリンシパルを介して、ARM プラグインに Azure サブスクリプションへのアクセスを許可する必要があります。サービスプリンシパルは、ユーザーアカウントと同じ目的で利用され、ARM プラグインに Azure Active Directory ID を提供します。Azure リソースに対する認証とアクセス許可のための資格情報であるユーザーアカウントと同様に、サービスプリンシパルは役割ベースのアクセス制御 (Role-Based Access Control : RBAC) を使用して構成されます。

Azure サービスプリンシパルについては、こちらもご覧ください。

アクセス権を付与するには、ユーザー、グループ、およびアプリケーションに適切な RBAC ロールを特定のスコープに割り当てます。ロール割り当てのスコープには、サブスクリプション全体、または特定のリソースやグループに対しても指定可能です。スコープがどのように定義されているかに応じて、サービスプリンシパルは次のように分類されます。

1. サブスクリプションにスコープを持つサービスプリンシパル
2. 特定のリソースにスコープを持つサービスプリンシパル

サブスクリプションにスコープを持つサービスプリンシパル
サブスクリプションにスコープを持つサービスプリンシパルは、サブスクリプション内のすべてのリソースに対する共同作成者権限を持ち、作成および管理が可能です。サブスクリプションにスコープを持つサービスプリンシパルを利用して Citrix Studio で作成プロセスを自動化するか、PowerShell を利用して手動で作成することもできます。もう1つの利点は、ARM プラグインが Azure リソースグループを作成し、リソースの管理を完全に自動化できることです。欠点は、サブスクリプション内のリソースに対して、ARM プラグインが管理しているリソースと関係の無い権限を持つ可能性があることです。このブログ記事ではサブスクリプションにスコープを持つサービスプリンシパルを使用して XenApp および XenDesktop から Azure Resource Manager に接続する方法、および認証プロセスの詳細な説明を記載します。
共同作成者ロールは、サブスクリプション内のすべての Azure リソースに対して作成、削除、読み書き全てを Azure プラグインに許可しますが、他のユーザーへアクセス権を付与することはできません。

特定のリソースにスコープを持つサービスプリンシパル
特定のリソースにスコープを持つサービスプリンシパルを使用すると、ARM プラグインは、ユーザーが定義した限られたリソースセットにアクセスできます。Azure ではリソースグループを作成するためにサブスクリプションへのアクセス許可が必要なため、特定のリソースにスコープを持つサービスプリンシパルを使用した場合、Azure プラグインでリソースグループを作成できません。そのため、マシンをプロビジョニングする各カタログに対してリソースグループを予め用意しておく必要があります。
執筆時点では、Citrix Studio は特定のリソースにスコープを持つサービスプリンシパルを使用する事や、特定のリソースに対してスコープを持つサービスプリンシパルでカタログを作成することをサポートしていません。そのため、これらを実現するためには PowerShell を使用して実行する必要があります。ただし、カタログが作成されると、Citrix Studio で管理されている他のカタログと同様に、カタログの追加や削除などの操作が可能です。リソースグループの新しいプールにおいて特定のリソースにスコープを持つサービスプリンシパルを使用する場合は、PowerShell を使用して明示的にアクセス許可を追加する必要があります。

どちらのスコープを持ったサービスプリンシパルを利用すればよいのか
次のセクションでの解説は、どちらのスコープを持つサービスプリンシパルを利用するのか、といった指針を示しています。特定の状況に合わせて検討実施や顧客要件に合わせて調整する必要がありますが、ガイドラインは次のとおりです。

次の場合は、サブスクリプションにスコープを持つサービスプリンシパルの使用を検討してください。

• 最も簡単な管理方法で運用したい。
• PowerShell の使用を避け、Citrix Studio にてすべてを管理したい。
• Azure サブスクリプションが、XenApp および XenDesktop のみに利用される場合。
• XenApp および XenDesktop のインストールや PoC を実施したい。
• XenApp および XenDesktopの管理者は Azure のサブスクリプションスコープで共同作成者ロール権限を持っている。

以下の場合は、特定のリソースにスコープを持つサービスプリンシパルの使用を検討してください。

• 利用している Azure サブスクリプションは、お互いに関係の無い複数のサービスで利用されている。
• Azure 管理者はロールに応じて異なるサブスクリプション権限を持つ。
• 要件として細かいレベルでアクセス制御が必要なセキュリティ基準がある。
• 特定のリソースにスコープを持つサービスプリンシパルを作成するためのプロセスが既に存在している。

Azure 内の各サブスクリプションは、1つの Azure Active Directory に所属していますが、プライマリサブスクリプションに「子」サブスクリプションを作成できます。この方法で関係の無いリソースへのアクセスを制限可能になります。

特定のリソースにスコープを持つサービスプリンシパルを利用するには
特定のリソースにスコープを持つサービスプリンシパルを利用してカタログを作成する前に確認が必要な事項は、導入時や将来において仮想マシンをホストするために必要となるリソースグループの数量を決定することです。ARM プラグインを通じて動作する Machine Creation Services には仕様上の制限があるため、カタログが作成された後にリソースグループを作成することはできません。

リソースグループプールごとに1つのカタログをプロビジョニングすることをお勧めします。

ARM プラグインは、ストレージアカウント、セキュリティグループ、ネットワークインターフェイス、仮想マシンなどで構成される各リソースグループに必要なインフラストラクチャを作成します。ストレージアカウントは、マシンがカタログに追加された際、必要に応じてオンデマンドで作成されます。カタログのサイズは、リソースグループのプール、および Azure サブスクリプションのクォータサイズによって設定された上限まで拡大することができます。ストレージアカウントが作成されると、カタログが削除されるまでストレージアカウントは削除されません。これはまれなケースですが、仮想マシンは利用可能なストレージアカウントに分散してしまう可能性がるため、不要なストレージアカウントを削除した後に仮想マシンを展開することをお勧めします。
Azure は、リソースグループ内の仮想マシンの数を 800 に制限しますが、ARM プラグインは異なる指針になります。標準の Azure ディスクの IOPS は 500 で、標準のストレージアカウントにおける IOPS 制限は 20,000 です。このため、ARM プラグインはストレージアカウントに 40台以下のマシンをプロビジョニング可能になります。この制限は現在、スタンダードストレージとプレミアムストレージの両方に適用されています。また、ARM プラグインは、リソースグループ内に 19 以下のストレージアカウントを作成します。

最大マシン数に基づいてリソースグループの数を計算するための基本的な式は以下のとおりです。

リソースグループの数 = 少数点以下切り上げ (最大マシンの数/(40 * 19))

Azure Role Based Access Control (RBAC) の基礎
サブスクリプション、リソースグループまたは特定のリソースへスコープを持つサービスプリンシパルに Azure RBACロールを割り当てることによって、Azure リソースへのアクセスが許可されます。リソースは階層で整理され、ロールによって定義された権限は適用されるスコープの下にあるすべてのリソースに適用されます。たとえば、サブスクリプションに適用されるロールはサブスクリプション内のすべてのリソースに適用され、リソースグループに適用されるロールはリソースグループに含まれるすべてのリソースに適用されるようになります。
Azure のリソース階層では、サブスクリプションスコープのアクセス許可を持つサービスプリンシパルだけがリソースグループを作成できます。しかしながら、ARM プラグインなどのアプリケーションがフルサブスクリプションで広範な権限を持たない限りオンデマンドでリソースグループを作成したり、リソースを管理できないため、使い勝手が良くありません。

Azure でのアクセス制御の詳細な説明は、こちらもご覧ください。

Azure にはさまざまな組み込みロールがあり、カスタムロールの作成もサポートされています。このブログでは、PowerShell でカスタムロールを作成する方法を説明しています。

サブスクリプションにスコープを持つサービスプリンシパルの作成
この例ではサブスクリプションにスコープを持つサービスプリンシパルを作成する方法を説明します。その後、このサービスプリンシパルを使用して Citrix Studio で Azure 接続を作成したり、PowerShell で Azure 接続を手動で作成することができます。

Azure で PowerShell を利用するには、こちらもご覧ください。

param(
[string]$applicationName = “SubscriptionScopeSP”,
[Parameter(Mandatory=$true)][string]$applicationPassword,
[Parameter(Mandatory=$true)][string]$subscriptionId
)

$application = New-AzureRmADApplication -DisplayName $applicationName -HomePage “https://localhost/$applicationName” `
-IdentifierUris “https://$applicationName” -Password $applicationPassword

New-AzureRmADServicePrincipal -ApplicationId $application.ApplicationId

# Wait for the service principal to become available
Start-Sleep -s 60

New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId”

Write-Host (“Application ID: ” + $application.ApplicationId)

必要な Azure 上の権限について
ARM プラグインでは、次のリソースへの権限が必要です。

1. ネットワークの場所
2. マスターイメージ VHD
3. マシンの仮想ネットワーク
4. マシンがプロビジョニングされるリソースグループ
5. Citrix Studio 機能のサポート:プロビジョニングされるマシンが使用可能なリソースグループの一覧

ARM プラグインを修正し、新しい機能を追加すると、リソースへのアクセスに必要な権限が変更されることがありますのでご注意ください。

Azure サブスクリプションの中には、コアとネットワークインターフェイスへのクォータが非常低く設定されているものもあります。クォータの割り当てが不十分な事によりプロビジョニングタスクが終了してしまわないようにするために、プロビジョニングタスクを開始する前に十分なクォータがあることを Azure プラグインが確認します。確認には、ネットワーク関連の API を利用可能です。

Azureでのクォータの詳細な説明は、こちらもご覧ください。

ネットワークの場所:
次の権限がネットワークの場所のスコープでは必要です。
• Microsoft.Network/locations/*/read

マスターイメージ VHD:
次の権限がイメージリソースグループのスコープでは必要です。
• Microsoft.Storage/storageAccounts/read
• Microsoft.Storage/storageAccounts/listKeys/action

マシンの仮想ネットワーク:
次の権限が仮想ネットワークリソースグループのスコープでは必要です。
• Microsoft.Network/virtualNetworks/read
• Microsoft.Network/virtualNetworks/subnets/join/action

マシンがプロビジョニングされるリソースグループ
これらのリソースグループには、ARM プラグインで作成されないリソースが含まれるべきではありません。また、共同作成者ロールを使用すると、ARM プラグインが変更された場合にサービスプリンシパルの変更が必要になる可能性が低くなります。次の権限がマシンリソースグループのスコープでは必要です。

• Microsoft.Compute / virtualMachines / *
• Microsoft.Network/networkInterfaces/*
• Microsoft.Network/networkSecurityGroups/*
• Microsoft.Resources / deployments / *
• Microsoft.Resources / subscriptions / resourceGroups / read
• Microsoft.Storage/storageAccounts/*
• Microsoft.Storage/storageAccounts/listKeys/action

Citrix Studio 機能のサポート
Citrix Studio を使用してカタログを作成する場合、マシンがプロビジョニングされるリソースグループを選択するために、次の権限が必要です。

• Microsoft.Resources / subscriptions / resourceGroups / read

Azureでのロールベースの権限の詳細な説明は、こちらもご覧ください。

特定のリソースにスコープを持つサービスプリンシパルの作成
このセクションでは、可能な限りスコープを絞ったサービスプリンシパルを作成します。また次のセクションでは、追加のカスタムロールを使用して権限をさらに絞る方法について解説します。
まず、サブスクリプションレベルでネットワークの場所にアクセス許可を割り当てるためのカスタムロールを定義します。次にスクリプトを簡略化するために、共同作成者でのアクセス権限がリソースグループのスコープで付与されることを前提としています。ここでは、ARM プラグインは、マスターイメージ VHD が格納されているリソースグループ、仮想ネットワークが含まれるリソースグループ、およびマシンがプロビジョニングされるリソースグループプールに対する共同作成者でのアクセス権限を持ちます。

こちらは、ネットワークの場所を読み取るために使用するロールを作成するスクリプトです。
最初にJSON形式で定義した新しいカスタムロールを作成します。

{
“Name”: “Citrix-Network-Usage-Reader”,
“Description”: “Grants access to read network usage.”,
“Actions”: [
“Microsoft.Network/locations/*/read”
],
“NotActions”: [
],
“AssignableScopes”: [
“/subscriptions/”
]
}

JSON定義ファイルを読み込ませこのカスタムロールを利用できるようします。

New-AzureRmRoleDefinition -InputFile citrix-network-usage-reader.json

そして、サービスプリンシパルを作成する際に上記の新しいカスタムロールを使用します。

param(
[string]$applicationName = “BasicNarrowScopeSP”,
[Parameter(Mandatory=$true)][string]$applicationPassword,
[Parameter(Mandatory=$true)][string]$subscriptionId,
[Parameter(Mandatory=$true)][string[]]$resourceGroups
)

$application = New-AzureRmADApplication -DisplayName $applicationName -HomePage “https://localhost/$applicationName” `
-IdentifierUris “https://$applicationName” -Password $applicationPassword

New-AzureRmADServicePrincipal -ApplicationId $application.ApplicationId

# Wait for the service principal to become available
Start-Sleep -s 60

New-AzureRmRoleAssignment -RoleDefinitionName Citrix-Network-Usage-Reader -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId/”

foreach ($rg in $resourceGroups)
{
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId/resourcegroups/$rg”
}

Write-Host (“Application ID: ” + $application.ApplicationId)

カスタムロールを使用した特定のリソースにスコープを持つサービスプリンシパルの作成
前の説明は共同作成者ロールを使用しましたので、厳密に言えば Azure プラグインには必要な権限よりもわずかに広い権限を与えられていました。こちらの説明では、別途カスタムロールを定義し、アクセス権限をさらに絞ります。必要に応じて、追加のカスタムロールを使用して、ロール権限をイメージおよびネットワークリソースに直接適用して、アクセスを完全に制御することができます。

Azureのロールベースのカスタムロールを作成する説明は、こちらもご覧ください。

こちらの説明では、リソースグループのスコープで仮想ネットワークとマスターイメージへのアクセスを許可するためのカスタムロールを定義します。マシンリソースグループへの権限に対して別のカスタムロールを作成することもできますが、説明内容を簡単にするために今回はマシンリソースグループについては共同作成者ロールを引き続き使用します。

最初にJSON形式で定義し、新しいカスタムロールを作成します。

{
“Name”: “Citrix-Custom-Reader”,
“Description”: “Grants access to Citrix XenDesktop images and virtual networks.”,
“Actions”: [
“Microsoft.Storage/storageAccounts/read”,
“Microsoft.Storage/storageAccounts/listKeys/action”,
“Microsoft.Network/virtualNetworks/read”,
“Microsoft.Network/virtualNetworks/subnets/join/action”
],
“NotActions”: [
],
“AssignableScopes”: [
“/subscriptions/”
]
}

JSON定義を読み込ませ、このカスタムロールを利用できるようにします。

New-AzureRmRoleDefinition -InputFile citrix-custom-reader.json

そして、サービスプリンシパルを作成する際に、上記の新しいカスタムロールを使用します。

param(
[string]$applicationName = “NarrowScopeSP”,
[Parameter(Mandatory=$true)][string]$applicationPassword,
[Parameter(Mandatory=$true)][string]$subscriptionId,
[Parameter(Mandatory=$true)][string[]]$machineResourceGroups,
[Parameter(Mandatory=$true)][string]$imageResourceGroup,
[Parameter(Mandatory=$true)][string]$networkResourceGroup
)

$application = New-AzureRmADApplication -DisplayName $applicationName -HomePage “https://localhost/$applicationName” `
-IdentifierUris “https://$applicationName” -Password $applicationPassword

New-AzureRmADServicePrincipal -ApplicationId $application.ApplicationId

# Wait for the service principal to become available
Start-Sleep -s 60

New-AzureRmRoleAssignment -RoleDefinitionName Citrix-Network-Usage-Reader -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId/”

foreach ($rg in $machineResourceGroups)
{
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId/resourcegroups/$rg”
}

New-AzureRmRoleAssignment -RoleDefinitionName Citrix-Custom-Reader -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId/resourcegroups/$imageResourceGroup”

New-AzureRmRoleAssignment -RoleDefinitionName Citrix-Custom-Reader -ServicePrincipalName $application.ApplicationId `
-scope “/subscriptions/$subscriptionId/resourcegroups/$networkResourceGroup”

Write-Host (“Application ID: ” + $application.ApplicationId)

XenApp および XenDesktop からの Azure 接続の作成
既存のサービスプリンシパルを使用して Citrix Studio からXenApp および XenDesktop の Azure 接続を作成するのは簡単な方法ですが、Azure 接続を PowerShell で作成することもできます。次の例では PowerShell を利用した接続の作成方法を解説します。

param(
[string]$connectionName = “AzureConnection”,
[Parameter(Mandatory=$true)][string]$applicationId,
[Parameter(Mandatory=$true)][string]$applicationPassword,
[Parameter(Mandatory=$true)][string]$subscriptionId,
[Parameter(Mandatory=$true)][string]$subscriptionName,
[Parameter(Mandatory=$true)][string]$tenantId
)

Add-PsSnapin Citrix*

$customProperties = @”

“@

$connection = New-Item -ConnectionType “Custom” -CustomProperties $customProperties -HypervisorAddress @(“https://management.azure.com/”) `
-Path @(“XDHyp:\Connections\$connectionName”) -Persist -PluginId “AzureRmFactory” -Scope @() `
-SecurePassword (ConvertTo-SecureString -AsPlainText -Force $applicationPassword) -UserName $applicationId

New-BrokerHypervisorConnection -HypHypervisorConnectionUid $connection.HypervisorConnectionUid

この後に、接続に必要なリソースを追加します。これは、Citrix Studio または PowerShell で同様に設定できます。

XenApp および XenDesktop カタログの作成
次の例では、Citrix PowerShell スナップインを使用して、XenApp および XenDesktop カタログを作成します。
特定のスコープを持つサービスプリンシパルではARM プラグインでリソースグループを作成できないため、次の作業を行う必要があります。

1. リソースグループのプールを作成します。
2. リソースグループプール内のすべてのリソースグループに対してサービスプリンシパルのアクセス許可を割り当てます。
3. プロビジョニングスキームの作成時に、リソースグループ内の各リソースグループをカスタムプロパティに設定します。

カスタムプロパティの名前は ResourceGroups で、リソースグループが複数ある場合は、リソースグループをカンマ (,) で区切り列挙します。カスタムプロパティを定義する方法例を以下に示します。
マシンが展開されるリソースグループのみがカスタムプロパティに列挙されている必要があります。ここにイメージ、もしくは仮想ネットワークが配置されているリソースグループを含めることはできません。もし、これらが指定されている場合、ARM プラグインは、意図しないリソースグループにマシンをプロビジョニングする可能性があります。

この例では、xd-sales-1 および xd-sales-2 という2つのリソースグループにマシンがプロビジョニングされます。イメージパスは “.vhd.vhd” で終わることに注意してください。これは、VHD Blob 名が “.vhd” で終わり、MCS がオブジェクト “vdh” であることを示す “.vhd” を追加するためです。

Add-PsSnapin Citrix*

# The hosting unit name is the name of the Azure connection resources that should be used for this catalog
$hostingUnitName = “AzureHostingUnit”
$domain = “citrix.local”
$controllerAddress = (“ddc.” + $domain)
$adminAddress = ($controllerAddress + “:80”)
$catalogName = “catalog-name”
$network = “network-resource-group.resourcegroup\network-name”
$subnet = “subnet-name”
$serviceOffering = “Standard_A4”
$template = “image-resource-group.resourcegroup\imagestorage.storageaccount\images.container\image-name.vhd”

$customProperties = @”

“@

$identityPool = New-AcctIdentityPool -AdminAddress $adminAddress -AllowUnicode -Domain $domain `
-IdentityPoolName $catalogName -NamingScheme “vm-#” -NamingSchemeType “Numeric” -Scope @()

$brokerCatalog = New-BrokerCatalog -AdminAddress $adminAddress -AllocationType “Random” -IsRemotePC $False `
-MinimumFunctionalLevel “L7_9” -Name $catalogName -PersistUserChanges “Discard” -ProvisioningType “MCS” -Scope @() `
-SessionSupport “MultiSession”

Write-Host $brokerCatalog

$provScheme = New-ProvScheme -AdminAddress $adminAddress -CleanOnBoot -CustomProperties $customProperties `
-HostingUnitName $hostingUnitName -IdentityPoolName $catalogName `
-MasterImageVM “XDHyp:\HostingUnits\$hostingUnitName\image.folder\$template.vhd” `
-NetworkMapping @{“0″=”XDHyp:\HostingUnits\$hostingUnitName\virtualprivatecloud.folder\$network.virtualprivatecloud\$subnet.network”} `
-ProvisioningSchemeName $catalogName -Scope @() -SecurityGroup @() `
-ServiceOffering “XDHyp:\HostingUnits\$hostingUnitName\serviceoffering.folder\$serviceOffering.serviceoffering”

Write-Host $provScheme

Set-BrokerCatalog -AdminAddress $adminAddress -Name $catalogName -ProvisioningSchemeId $provScheme.ProvisioningSchemeUid

Add-ProvSchemeControllerAddress -AdminAddress $adminAddress.com -ControllerAddress $controllerAddress -ProvisioningSchemeName $catalogName

この後、Citrix Studio を利用してカタログページの更新や、マシン追加といった操作ができるようになります。最初から Citrix Studio を利用して作成されたカタログと同じようにマシンを管理することができるようになります。

■まとめ

Azure サービスプリンシパルへ適切なロール権限やスコープを定義することで、よりセキュリティレベルの高い Azure クラウドの利用が出来るようになります。XenApp および XenDesktop を Azure へ接続して利用する場合だけでなく、Azure クラウドを利用する他のアプリケーションに対しても適用を検討してみては如何でしょうか。