将 Session Recording 资源部署到云订阅

本文提供有关将 Session Recording 资源部署到 Azure 订阅的信息。

您可以从 Session Recording 服务中将以下 Session Recording 资源部署到 Azure 订阅中:

  • Session Recording Server
  • 数据库
  • 存储
  • 负载平衡器

将 Session Recording 资源部署到 Azure 订阅的方法有两种:

  • 使用连接到 Azure 订阅的主机连接。创建主机连接需要您提供订阅信息。有关详细信息,请参阅本文后面的通过主机连接创建和部署站点

  • 如果您不想提供订阅信息,请创建一个 Azure Resource Manager 模板(ARM 模板),其中包含部署方法以及要部署的资源。有关详细信息,请参阅本文后面的通过 ARM 模板创建和部署站点

通过主机连接创建和部署站点

本部分内容将引导您完成通过主机连接创建和部署站点的过程,以及可以在以这种方式部署的站点上执行的以下操作:

  • 向部署在 Azure 上的现有站点中添加资源
  • 更改允许用于访问负载平衡器的 IP 地址
  • 查看使用 Azure 的实际成本

通过主机连接创建和部署站点

  1. 在 Session Recording 服务的左侧导航栏中选择配置 > 服务器管理

    “服务器管理”页面

  2. 服务器管理页面上,单击创建站点。此时将显示创建站点页面。

    “创建站点”页面

  3. 选择通过主机连接创建和部署站点。左侧导航栏中列出了主要步骤。

    通过主机连接创建和部署站点

  4. 输入站点名称和说明,选择连接到您的 Azure 订阅的主机连接,然后指定区域。

    • 如果您没有主机连接,请参考本文后面的添加主机连接来添加一个主机连接。

    • 不支持 Azure 政府区域。

  5. 完成站点信息后,单击下一步以继续。

  6. (可选)要获取有关 VM 和存储配置的建议,请提供与您的录制需求有关的信息。

    可以通过单击 I’m good, skip this step(我很好,跳过此步骤)或者在未选择任何内容的情况下单击下一 步来跳过此步骤。

    您的录制需求

    当您从下拉列表中选择选项时,系统会根据您的选择显示建议。建议旁边有一个可用的重置按钮。它使您可以清除该部分中的选择和相应的建议。

  7. 为您的 Session Recording 服务器指定要加入的虚拟网络和子网。如果您的 VDA 与 Session Recording Server 位于不同的虚拟网络中,或者位于本地网络中,请建立连接以确保 Session Recording Server 能够与您的 VDA 通信。

    虚拟网络和子网

  8. 创建虚拟机 (VM) 作为您的 Session Recording Server。

    将 VM 创建为 Session Recording Server

    注意:

    • 如果有 VM 数量字段,则会预先填充建议的数字。请根据需要更改该数字。
    • 估算的成本基于标准定价,不考虑折扣。您可以期望实际成本低于预期。
  9. 将 Session Recording Server 与您的 VDA 加入到同一个域中,并为 Session Recording Server 指定证书。

    • 如果您的 VDA 连接到 Active Directory 域,请选中 Join servers to an Active Directory domain(将服务器加入到 Active Directory 域)复选框并输入相关信息。选中 Join servers to an Active Directory domain(将服务器加入到 Active Directory 域)复选框后,即可为混合方案配置部署,将本地 Active Directory 与 Azure AD 集成。

    • 如果您的 VDA 连接到 Azure Active Directory (Azure AD) 域,请取消选中 Join servers to an Active Directory domain(将服务器加入到 Active Directory 域)复选框。完成当前站点的创建后,请务必手动将 Session Recording Server 加入到同一个 Azure AD 域。请注意,纯 Azure AD 部署仅适用于 Session Recording 2402 及更高版本。

    将服务器加入到 Active Directory 域

    将服务器加入到 Azure Active Directory 域

    注意:

    自 2023 年 7 月起,Microsoft 已将 Azure Active Directory (Azure AD) 重命名为 Microsoft Entra ID。在本文档中,任何提及 Azure Active Directory、Azure AD 或 AAD 的内容现在均指 Microsoft Entra ID。

  10. 配置 Azure 存储帐户和文件共享以存储您的录制文件。有关定价信息,请参阅 Azure 文件定价

    配置 Azure 存储帐户和文件共享以存储您的录制文件

  11. 在 Azure 中创建两个 SQL 数据库。一个用作 Session Recording 数据库(名为 sessionrecording),另一个用作管理员日志记录数据库(名为 sessionrecordinglogging)。

    在 Azure 中创建两个 SQL 数据库

  12. 创建负载平衡器以在 Session Recording Server 之间分发工作负载。在 Restrict access of the load balancer to only the following addresses(将负载平衡器的访问权限限制为仅限以下地址)字段中输入 VDA 的 IP 地址或范围并将其用逗号 (,) 分隔。有关定价信息,请参阅负载平衡定价

    创建负载平衡器

  13. (可选)对要创建的 Azure 资源应用标记。

    将标记应用到 Azure 资源

  14. 创建安全客户端,以将 Session Recording Server 载入到 Session Recording 服务。

    单击创建客户端,让 Citrix 代表您创建安全客户端。或者,您可以通过 Citrix Cloud 控制台的 Identity and Access Management(身份识别和访问管理)> API Access(API 访问权限)选项卡创建安全客户端,然后填写以下信息。

    “创建客户端”按钮

  15. 查看有关要创建的站点的摘要。如果需要,请单击铅笔图标以编辑设置,或者单击按钮开始部署。

    关于要创建的站点的摘要

    下面是部署过程的示例:

    部署正在进行中:

    部署正在进行中

    在站点部署过程中,您可以单击查看状态以查看进度。

    部署失败:

    部署失败

    如果在部署过程中出现错误,请单击查看状态以查看错误详细信息。有关错误详细信息的示例:

    部署错误详细信息

    可以单击 Back to configuration(返回到配置)或 cancel the deployment(取消部署)。如果单击 Back to configuration(返回到配置),则会返回到 Create Site(创建站点)页面,您可以在其中更改配置并重试。如果确定要取消部署,请按照向导删除该站点以及为该站点创建的 Azure 资源。例如:

    部署已取消

    部署成功:

    站点部署完成后,您可以扩展站点以及查看和管理在其下创建的资源。View status(查看状态)按钮更改为 Settings(设置)。Azure 图标可用来表示在 Azure 上部署的站点。 有关站点设置的信息,请参阅站点和服务器设置

    已部署的站点

向部署在 Azure 上的现有站点中添加资源

对于通过主机连接在 Azure 上部署的现有站点,可以向其中添加包括服务器和存储在内的资源。为此,请完成以下步骤:

  1. 在 Session Recording 服务的左侧导航栏中选择配置 > 服务器管理
  2. 服务器管理页面上,找到并展开目标站点。Azure 图标可用来表示在 Azure 上部署的站点。
  3. 单击添加资源

    “添加资源”按钮

  4. 添加资源页面上,根据需要单击添加服务器添加存储

    “添加资源”页面

    • 要添加服务器,请单击添加服务器,然后完成以下步骤:

      添加服务器

      1. 指定要添加的服务器数量。
      2. 单击提供凭据将新服务器加入到与现有服务器相同的域中。
      3. 单击创建客户端将新服务器加入到 Session Recording 服务。
      4. 单击开始部署
    • 要添加存储空间以存储录制文件,请单击添加存储,然后相应地完成以下步骤:

      1. 如果您的站点是使用标准存储帐户创建的,系统会提示您指定要添加的文件共享数量。例如:

        要为标准存储帐户添加的文件共享

      2. 如果您的站点是使用高级存储帐户创建的,则可以指定要添加的文件共享数量,也可以自定义每个文件共享的容量。例如:

        要为标准存储帐户添加的文件共享

      3. 单击开始部署

        注意:

        • 满足以下任一条件时,开始部署按钮可用:
          • 已经指定了至少一台服务器并配置了域和安全客户端。
          • 已指定至少一个文件共享。
        • 部署资源过程中,负载平衡器的设置按钮将被禁用。
        • 部署添加的资源可能会失败,Session Recording 服务可能无法从您的订阅中删除这些资源。在这种情况下,将提供类似于以下内容的提示供您采取行动:

          无法清理资源

更改允许用于访问负载平衡器的 IP 地址

对于通过主机连接在 Azure 上部署的现有站点,可以更改允许访问负载平衡器的 IP 地址。为此,请完成以下步骤:

  1. 在 Session Recording 服务的左侧导航栏中选择配置 > 服务器管理
  2. 服务器管理页面上,找到并展开目标站点。Azure 图标可用来表示在 Azure 上部署的站点。
  3. 单击负载平衡器部分中的设置按钮。

    负载平衡器的“设置”按钮

  4. 负载平衡器设置页面上,输入您的 VDA 的新 IP 地址或范围,并在 Restrict access of the load balancer to only the following addresses(将负载平衡器的访问权限限制为仅限以下地址)字段中用逗号 (,) 分隔。

    设置哪些 VDA 可以访问负载平衡器

  5. 单击保存

查看使用 Azure 的实际成本

对于通过主机连接在 Azure 上部署的现有站点,单击成本金额可查看成本详细信息。例如:

使用 Azure 的成本

成本分析

查看实际成本的提示:

  • 当您将鼠标悬停在当月的区域图上时,日期和当天数据的参考线将以叠加形式出现。
  • 不同资源的历史成本用折线图表示。当有至少两个月的数据时,可以使用折线图。当您将鼠标悬停在折线图上时,该月的参考线和成本明细显示为叠加图。要仅查看特定资源的折线图,请将鼠标悬停在该资源上。

添加主机连接

要添加主机连接,请完成以下步骤:

  1. 创建站点页面上单击添加连接,并选择通过主机连接创建和部署站点。或者,在主机连接页面上单击添加连接

    要访问创建站点页面,请从 Session Recording 服务的左侧导航栏中选择配置 > 服务器管理,然后单击创建站点

    “服务器管理”页面

    要访问主机连接页面,请从 Session Recording 服务的左侧导航栏中选择配置 > 主机连接

    “主机连接”页面

  2. 添加连接页面上,为新主机连接指定名称和说明(可选)。输入您的 Azure 订阅 ID 以及有关应用程序注册的以下必填信息:

    • 应用程序(客户端)ID
    • 服务主体对象 ID(与应用程序关联的服务主体对象的 ID)
    • 目录(租户)ID
    • 客户端密钥
    • 机密过期日期

    “添加连接”页面

    要查找您的 Azure 订阅 ID,请执行以下操作:

    1. 登录 Azure 门户。
    2. Azure 服务部分下,选择订阅
    3. 在列表中找到您的订阅,然后复制第二列中显示的 订阅 ID

      查找 Azure 订阅 ID

    要获取有关应用程序注册的所需信息,请执行以下操作:

    1. (如果您已经注册了应用程序,请跳过此步骤。)向 Azure AD 租户注册应用程序。必须注册应用程序才能将身份识别和访问管理功能委派给 Azure AD。

      注册应用程序有两种方法。

      方法 1:

      1. 复制 Citrix 提供的以下脚本并将其命名为 AppRegistration.ps1

        <#
        .SYNOPSIS
            Copyright (c) Citrix Systems, Inc. All Rights Reserved.
        .DESCRIPTION
            Create Azure app registrations and give proper permissions for Citrix Session Recording service deployment
        .Parameter azureTenantID
        .Parameter azureSubscriptionID
        .Parameter appName
        .Parameter role
        #>
        
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory = $true)] [String] $tenantId,
            [Parameter(Mandatory = $true)] [String] $subscriptionId,
            [Parameter(Mandatory = $true)] [String] $appName,
            [Parameter(Mandatory = $true)] [String] $role
        )
        
        if ($role -ne "Citrix Session Recording service" -and $role -ne "Citrix Session Recording Deployment" -and $role -ne "Contributor") {
            throw [System.Exception] "Invalid role '$role', only support 'Citrix Session Recording service', 'Citrix Session Recording Deployment', and 'Contributor'."
        }
        
        try {
            Get-InstalledModule -Name "Az.Accounts" -ErrorAction Stop
        }
        catch {
            Install-Module -Name "Az.Accounts" -Scope CurrentUser -Repository PSGallery -SkipPublisherCheck -Force
        }
        try {
            Get-InstalledModule -Name "Az.Resources" -ErrorAction Stop
        }
        catch {
            Install-Module -Name "Az.Resources" -Scope CurrentUser -Repository PSGallery -SkipPublisherCheck -Force
        }
        
        Connect-AzAccount -TenantId $tenantId -Subscription $subscriptionId
        
        try {
        
            $azureAdApplication = Get-AzADApplication -DisplayName $appName
            if ($null -eq $azureAdApplication) {
                Write-Host "Create a new app registration for Citrix Session Recording" -ForegroundColor Green
                $azureAdApplication = New-AzADApplication -DisplayName $appName -AvailableToOtherTenants $false
            }
            else {
                Write-Host "App registration '$appName' already exists." -ForegroundColor Yellow
            }
        
            $azureAdApplicationServicePrincipal = Get-AzADServicePrincipal -DisplayName $appName
            if($null -eq $azureAdApplicationServicePrincipal) {
                $azureAdApplicationServicePrincipal = New-AzADServicePrincipal -AppId $azureAdApplication.AppId
                Write-Host "Create a service principal for app registration '$appName'" -ForegroundColor Green
            }else{
                Write-Host "Service principal already exists for app registration '$appName'" -ForegroundColor Yellow
            }
        
            if ($role -eq "Citrix Session Recording service" -or $role -eq "Citrix Session Recording Deployment") {
                $rootPath = Get-Location
                $customRolePath = $(Join-Path -Path $rootPath -ChildPath "sessionrecording.json") | Resolve-Path
                $customRoleJson = Get-Content $customRolePath | ConvertFrom-Json
                $customRoleJson.AssignableScopes[0] = "/subscriptions/" + $subscriptionId
                $tmpCustomRolePath = Join-Path -Path $rootPath -ChildPath "sessionrecording_tmp.json"
        
                $roleDef = Get-AzRoleDefinition -Name $role
                if ($null -eq $roleDef) {
                    try {
                        $customRoleJson | ConvertTo-Json -depth 32 | Set-Content $tmpCustomRolePath
                        Write-Host "Create a custom role '$role'" -ForegroundColor Green
                        New-AzRoleDefinition -InputFile $tmpCustomRolePath
                    }
                    catch {
                        Write-Host "Failed to create custom role, error: $_" -ForegroundColor Red
                        throw $_.Exception
                    }
                }
                else {
                    try {
                        $customRoleJson | Add-Member -MemberType NoteProperty -Name 'id' -Value $($roleDef.Id)
                        $customRoleJson | ConvertTo-Json -depth 32 | Set-Content $tmpCustomRolePath
                        Write-Host "Upadate the custom role '$role'" -ForegroundColor Green
                        Set-AzRoleDefinition -InputFile $tmpCustomRolePath
                    }
                    catch {
                        Write-Host "Failed to update custom role, error: $_" -ForegroundColor Red
                        throw $_.Exception
                    }
                }
            }
        
            $roleAssignment = Get-AzRoleAssignment -RoleDefinitionName $role -ObjectId $($azureAdApplicationServicePrincipal.Id)
            if ($null -eq $roleAssignment) {
                Write-Host "Assign role '$role' to app '$appName'" -ForegroundColor Green
                New-AzRoleAssignment -RoleDefinitionName $role -ApplicationId $azureAdApplication.AppId
            }
            else {
                Write-Host "Role '$role' already assigned to app '$appName'" -ForegroundColor Yellow
            }
        
            Write-Host "Tenant ID:                   $tenantId" -ForegroundColor Green
            Write-Host "Subscription ID:             $subscriptionId" -ForegroundColor Green
            Write-Host "Application ID:              $($azureAdApplication.AppId)" -ForegroundColor Green
            Write-Host "Service principal object ID: $($azureAdApplicationServicePrincipal.Id)" -ForegroundColor Green
        }
        catch {
            Write-Host "Failed to assign role assignment to this app, error: $_" -ForegroundColor Red
            Write-Host "Please make sure the current azure admin has permission to assign roles" -ForegroundColor Red
        }
        <!--NeedCopy-->
        
      2. 复制以下自定义角色文件并将其命名为 sessionrecording.json。此自定义角色文件有助于为要注册的应用程序分配最少的权限。

        {
            "Name":  "Citrix Session Recording service",
            "Description":  "Custom role for session recording service",
            "AssignableScopes":  [
                                     "/subscriptions/*"
                                 ],
            "Actions":  [
                "Microsoft.Authorization/roleAssignments/write",
                "Microsoft.Authorization/roleDefinitions/delete",
                "Microsoft.Authorization/roleDefinitions/write",
                "Microsoft.Compute/availabilitySets/write",
                "Microsoft.Compute/virtualMachines/delete",
                "Microsoft.Compute/virtualMachines/extensions/read",
                "Microsoft.Compute/virtualMachines/extensions/write",
                "Microsoft.Compute/virtualMachines/read",
                "Microsoft.Compute/virtualMachines/runCommands/read",
                "Microsoft.Compute/virtualMachines/runCommands/write",
                "Microsoft.Compute/virtualMachines/write",
                "Microsoft.CostManagement/forecast/read",
                "Microsoft.CostManagement/query/read",
                "Microsoft.KeyVault/locations/deletedVaults/purge/action",
                "Microsoft.KeyVault/vaults/read",
                "Microsoft.KeyVault/vaults/write",
                "Microsoft.ManagedIdentity/userAssignedIdentities/assign/action",
                "Microsoft.ManagedIdentity/userAssignedIdentities/read",
                "Microsoft.ManagedIdentity/userAssignedIdentities/write",
                "Microsoft.Network/loadBalancers/backendAddressPools/join/action",
                "Microsoft.Network/loadBalancers/write",
                "Microsoft.Network/networkInterfaces/join/action",
                "Microsoft.Network/networkInterfaces/read",
                "Microsoft.Network/networkInterfaces/write",
                "Microsoft.Network/networkSecurityGroups/delete",
                "Microsoft.Network/networkSecurityGroups/join/action",
                "Microsoft.Network/networkSecurityGroups/read",
                "Microsoft.Network/networkSecurityGroups/securityRules/read",
                "Microsoft.Network/networkSecurityGroups/securityRules/write",
                "Microsoft.Network/networkSecurityGroups/write",
                "Microsoft.Network/publicIPAddresses/join/action",
                "Microsoft.Network/publicIPAddresses/read",
                "Microsoft.Network/publicIPAddresses/write",
                "Microsoft.Network/virtualNetworks/read",
                "Microsoft.Network/virtualNetworks/subnets/join/action",
                "Microsoft.Network/virtualNetworks/subnets/read",
                "Microsoft.Resources/deployments/operationstatuses/read",
                "Microsoft.Resources/deployments/read",
                "Microsoft.Resources/deployments/write",
                "Microsoft.Resources/subscriptions/resourceGroups/delete",
                "Microsoft.Resources/subscriptions/resourceGroups/read",
                "Microsoft.Resources/subscriptions/resourceGroups/write",
                "Microsoft.Sql/servers/auditingSettings/write",
                "Microsoft.Sql/servers/databases/write",
                "Microsoft.Sql/servers/firewallRules/write",
                "Microsoft.Sql/servers/read",
                "Microsoft.Sql/servers/write",
                "Microsoft.Storage/storageAccounts/fileServices/shares/delete",
                "Microsoft.Storage/storageAccounts/fileServices/shares/write",
                "Microsoft.Storage/storageAccounts/listkeys/action",
                "Microsoft.Storage/storageAccounts/read",
                "Microsoft.Storage/storageAccounts/write"
                ],
            "NotActions":  [
        
                           ],
            "DataActions":  [
        
                            ],
            "NotDataActions":  [
        
                               ]
        }
        <!--NeedCopy-->
        
      3. AppRegistration.ps1sessionrecording.json 放置在同一个文件夹中。
      4. 根据需要运行以下任一命令。

        要创建应用程序并使用之前的自定义角色文件 (sessionrecording.json) 为其分配最低权限,请运行:

        .\AppRegistration.ps1 -tenantId <tenant ID> -subscriptionId <subscription ID> -appName <application name> -role "Citrix Session Recording service"
        <!--NeedCopy-->
        

        要创建应用程序并为其分配 Azure 内置贡献者角色,请运行:

        .\AppRegistration.ps1 -tenantId <tenant ID> -subscriptionId <subscription ID> -appName <application name> -role "Contributor"
        <!--NeedCopy-->
        

      方法 2:

      转至 Azure 门户自行注册应用程序。为应用程序授予适当的权限。有关所需的最低权限,请参阅方法 1 中的 sessionrecording.json 文件。

    2. 单击应用程序的显示名称。

      应用程序显示名称

    3. 在概述页面上,找到应用程序(客户端)ID 和目录(租户)ID。单击 Managed application in local directory(本地目录中的托管应用程序)旁边的链接,查找与该应用程序关联的服务主体对象的 ID。单击客户端凭据旁边的链接,查找客户端密钥 ID 及其过期日期。

      应用程序基础知识

      例如,与应用程序关联的服务主体对象的 ID:

      服务主体对象 ID

      例如,客户端密钥 ID 及其过期日期:

      客户端密钥 ID 及其过期日期

  3. 单击保存以测试您指定的主机连接是否可用。

    如果您指定的主机连接可用,则会将您返回到主机连接页面,并提示已成功添加主机连接。

    Session Recording 服务分别使用错误和警告图标提醒您客户端密钥已过期和即将过期。您可以单击相应的主机连接,然后在连接详细信息页面上单击 Change secret(更改密钥),以更新客户端密钥及其过期日期。

    更改密钥

通过 ARM 模板创建和部署站点

您可以创建 Azure Resource Manager 模板(ARM 模板),以在 Azure 中部署 Session Recording 资源。下面是实现此目标的主要步骤:

  1. 在 Session Recording 服务中创建 ARM 模板。ARM 模板是一个 JavaScript 对象表示法 (JSON) 文件,其中包含您要部署资源的方式以及要部署的资源。
  2. 下载并解压缩 ARM 模板。在解压缩的模板文件夹中运行部署脚本,开始将在模板中指定的资源部署到 Azure。
  3. 在 Azure 中检查部署进度。部署完成后,设置 Session Recording 以使其启动并运行。要设置 Session Recording,您需要指定 Session Recording Server 的版本来安装和上载 resourceInfo.json 文件。

具体步骤如下:

  1. 在 Session Recording 服务的左侧导航栏中选择配置 > 服务器管理

    “服务器管理”页面

  2. 服务器管理页面上,单击创建站点。此时将显示创建站点页面。

    “创建站点”页面

  3. 选择 Create and deploy a site through an ARM template(通过 ARM 模板创建和部署站点)。左侧导航栏中列出了主要步骤。

    通过 ARM 模板创建和部署站点

  4. 输入站点名称和说明,然后单击下一步

  5. (可选)要获取有关 VM 和存储配置的建议,请提供与您的录制需求有关的信息。

    可以通过单击 I’m good, skip this step(我很好,跳过此步骤)或者在未选择任何内容的情况下单击下一 步来跳过此步骤。

    您的录制需求

    当您从下拉列表中选择选项时,系统会根据您的选择显示建议。建议旁边有一个可用的重置按钮。它使您可以清除该部分中的选择和相应的建议。

  6. 转至 Azure 门户并在所选区域中创建新的虚拟网络,并在新的虚拟网络与您的 VDA 连接到的虚拟网络之间设置虚拟网络对等互连。然后,在新虚拟网络中添加子网。在下面找到并输入子网 ID。

    网络设置

    要保持专用网络内的资源之间的连接,请选中 Create private endpoints for storage and databases(为存储和数据库创建专用端点)复选框。

    选中 Create private endpoints for storage and databases(为存储和数据库创建专用端点)复选框后,请考虑以下因素以决定是否输入另一个子网 ID:

    • 如果您不打算将 Session Recording Server 加入到 Active Directory 域中,则不需要子网,因此,请将子网 ID 字段留空。
    • 如果将子网 ID 字段留空,则表示您要将 Session Recording Server 加入到 Azure Active Directory 域。

    另一个子网 ID

  7. (如果您已经注册了应用程序,请跳过此步骤。)向 Azure AD 租户注册应用程序。必须注册应用程序才能将身份识别和访问管理功能委派给 Azure AD。

    注册应用程序有两种方法。

    方法 1:

    1. 复制 Citrix 提供的以下脚本并将其命名为 AppRegistration.ps1

      <#
      .SYNOPSIS
          Copyright (c) Citrix Systems, Inc. All Rights Reserved.
      .DESCRIPTION
          Create Azure app registrations and give proper permissions for Citrix Session Recording service deployment
      .Parameter azureTenantID
      .Parameter azureSubscriptionID
      .Parameter appName
      .Parameter role
      #>
      
      [CmdletBinding()]
      Param(
          [Parameter(Mandatory = $true)] [String] $tenantId,
          [Parameter(Mandatory = $true)] [String] $subscriptionId,
          [Parameter(Mandatory = $true)] [String] $appName,
          [Parameter(Mandatory = $true)] [String] $role
      )
      
      if ($role -ne "Citrix Session Recording service" -and $role -ne "Citrix Session Recording Deployment" -and $role -ne "Contributor") {
          throw [System.Exception] "Invalid role '$role', only support 'Citrix Session Recording service', 'Citrix Session Recording Deployment', and 'Contributor'."
      }
      
      try {
          Get-InstalledModule -Name "Az.Accounts" -ErrorAction Stop
      }
      catch {
          Install-Module -Name "Az.Accounts" -Scope CurrentUser -Repository PSGallery -SkipPublisherCheck -Force
      }
      try {
          Get-InstalledModule -Name "Az.Resources" -ErrorAction Stop
      }
      catch {
         Install-Module -Name "Az.Resources" -Scope CurrentUser -Repository PSGallery -SkipPublisherCheck -Force
      }
      
      Connect-AzAccount -TenantId $tenantId -Subscription $subscriptionId
      
      try {
      
          $azureAdApplication = Get-AzADApplication -DisplayName $appName
          if ($null -eq $azureAdApplication) {
              Write-Host "Create a new app registration for Citrix Session Recording" -ForegroundColor Green
              $azureAdApplication = New-AzADApplication -DisplayName $appName -AvailableToOtherTenants $false
          }
          else {
              Write-Host "App registration '$appName' already exists." -ForegroundColor Yellow
          }
      
          $azureAdApplicationServicePrincipal = Get-AzADServicePrincipal -DisplayName $appName
          if($null -eq $azureAdApplicationServicePrincipal) {
              $azureAdApplicationServicePrincipal = New-AzADServicePrincipal -AppId $azureAdApplication.AppId
              Write-Host "Create a service principal for app registration '$appName'" -ForegroundColor Green
          }else{
              Write-Host "Service principal already exists for app registration '$appName'" -ForegroundColor Yellow
          }
      
          if ($role -eq "Citrix Session Recording service" -or $role -eq "Citrix Session Recording Deployment") {
              $rootPath = Get-Location
              $customRolePath = $(Join-Path -Path $rootPath -ChildPath "sessionrecordingdeployment.json") | Resolve-Path
              $customRoleJson = Get-Content $customRolePath | ConvertFrom-Json
              $customRoleJson.AssignableScopes[0] = "/subscriptions/" + $subscriptionId
              $tmpCustomRolePath = Join-Path -Path $rootPath -ChildPath "sessionrecording_tmp.json"
      
              $roleDef = Get-AzRoleDefinition -Name $role
              if ($null -eq $roleDef) {
                  try {
                      $customRoleJson | ConvertTo-Json -depth 32 | Set-Content $tmpCustomRolePath
                      Write-Host "Create a custom role '$role'" -ForegroundColor Green
                      New-AzRoleDefinition -InputFile $tmpCustomRolePath
                  }
                  catch {
                      Write-Host "Failed to create custom role, error: $_" -ForegroundColor Red
                      throw $_.Exception
                  }
              }
              else {
                  try {
                      $customRoleJson | Add-Member -MemberType NoteProperty -Name 'id' -Value $($roleDef.Id)
                      $customRoleJson | ConvertTo-Json -depth 32 | Set-Content $tmpCustomRolePath
                      Write-Host "Upadate the custom role '$role'" -ForegroundColor Green
                      Set-AzRoleDefinition -InputFile $tmpCustomRolePath
                  }
                  catch {
                      Write-Host "Failed to update custom role, error: $_" -ForegroundColor Red
                      throw $_.Exception
                  }
              }
          }
      
          $roleAssignment = Get-AzRoleAssignment -RoleDefinitionName $role -ObjectId $($azureAdApplicationServicePrincipal.Id)
          if ($null -eq $roleAssignment) {
              Write-Host "Assign role '$role' to app '$appName'" -ForegroundColor Green
              New-AzRoleAssignment -RoleDefinitionName $role -ApplicationId $azureAdApplication.AppId
          }
          else {
              Write-Host "Role '$role' already assigned to app '$appName'" -ForegroundColor Yellow
          }
      
          Write-Host "Tenant ID:                   $tenantId" -ForegroundColor Green
          Write-Host "Subscription ID:             $subscriptionId" -ForegroundColor Green
          Write-Host "Application ID:              $($azureAdApplication.AppId)" -ForegroundColor Green
          Write-Host "Service principal object ID: $($azureAdApplicationServicePrincipal.Id)" -ForegroundColor Green
      }
      catch {
          Write-Host "Failed to assign role assignment to this app, error: $_" -ForegroundColor Red
          Write-Host "Please make sure the current azure admin has permission to assign roles" -ForegroundColor Red
      }
      <!--NeedCopy-->
      
    2. 复制以下自定义角色文件并将其命名为 sessionrecordingdeployment.json。此自定义角色文件有助于为要注册的应用程序分配最少的权限。

      {
          "name": "Citrix Session Recording Deployment",
          "description": "This role has permissions which allow users to deploy Session Recording resources using an Azure Resource Manager template (ARM template). ",
          "assignableScopes": [
            "/subscriptions/*"
          ],
          "actions": [
            "Microsoft.Compute/availabilitySets/write",
            "Microsoft.Compute/virtualMachines/extensions/read",
            "Microsoft.Compute/virtualMachines/extensions/write",
            "Microsoft.Compute/virtualMachines/read",
            "Microsoft.Compute/virtualMachines/runCommands/read",
            "Microsoft.Compute/virtualMachines/runCommands/write",
            "Microsoft.Compute/virtualMachines/write",
            "Microsoft.ContainerInstance/containerGroups/read",
            "Microsoft.ContainerInstance/containerGroups/write",
            "Microsoft.KeyVault/vaults/PrivateEndpointConnectionsApproval/action",
            "Microsoft.KeyVault/vaults/read",
            "Microsoft.KeyVault/vaults/secrets/read",
            "Microsoft.KeyVault/vaults/secrets/write",
            "Microsoft.KeyVault/vaults/write",
            "Microsoft.ManagedIdentity/userAssignedIdentities/assign/action",
            "Microsoft.ManagedIdentity/userAssignedIdentities/read",
            "Microsoft.ManagedIdentity/userAssignedIdentities/write",
            "Microsoft.Network/dnsForwardingRulesets/forwardingRules/read",
            "Microsoft.Network/dnsForwardingRulesets/forwardingRules/write",
            "Microsoft.Network/dnsForwardingRulesets/read",
            "Microsoft.Network/dnsForwardingRulesets/virtualNetworkLinks/read",
            "Microsoft.Network/dnsForwardingRulesets/virtualNetworkLinks/write",
            "Microsoft.Network/dnsForwardingRulesets/write",
            "Microsoft.Network/dnsResolvers/outboundEndpoints/join/action",
            "Microsoft.Network/dnsResolvers/outboundEndpoints/read",
            "Microsoft.Network/dnsResolvers/outboundEndpoints/write",
            "Microsoft.Network/dnsResolvers/read",
            "Microsoft.Network/dnsResolvers/write",
            "Microsoft.Network/loadBalancers/backendAddressPools/join/action",
            "Microsoft.Network/loadBalancers/write",
            "Microsoft.Network/networkInterfaces/join/action",
            "Microsoft.Network/networkInterfaces/read",
            "Microsoft.Network/networkInterfaces/write",
            "Microsoft.Network/networkSecurityGroups/join/action",
            "Microsoft.Network/networkSecurityGroups/read",
            "Microsoft.Network/networkSecurityGroups/securityRules/read",
            "Microsoft.Network/networkSecurityGroups/securityRules/write",
            "Microsoft.Network/networkSecurityGroups/write",
            "Microsoft.Network/privateDnsZones/join/action",
            "Microsoft.Network/privateDnsZones/read",
            "Microsoft.Network/privateDnsZones/virtualNetworkLinks/read",
            "Microsoft.Network/privateDnsZones/virtualNetworkLinks/write",
            "Microsoft.Network/privateDnsZones/write",
            "Microsoft.Network/privateEndpoints/privateDnsZoneGroups/read",
            "Microsoft.Network/privateEndpoints/privateDnsZoneGroups/write",
            "Microsoft.Network/privateEndpoints/read",
            "Microsoft.Network/privateEndpoints/write",
            "Microsoft.Network/publicIPAddresses/join/action",
            "Microsoft.Network/publicIPAddresses/read",
            "Microsoft.Network/publicIPAddresses/write",
            "Microsoft.Network/virtualNetworks/join/action",
            "Microsoft.Network/virtualNetworks/read",
            "Microsoft.Network/virtualNetworks/subnets/join/action",
            "Microsoft.Network/virtualNetworks/subnets/read",
            "Microsoft.Resources/deploymentScripts/read",
            "Microsoft.Resources/deploymentScripts/write",
            "Microsoft.Resources/deployments/operationstatuses/read",
            "Microsoft.Resources/deployments/read",
            "Microsoft.Resources/deployments/validate/action",
            "Microsoft.Resources/deployments/write",
            "Microsoft.Resources/subscriptions/resourceGroups/read",
            "Microsoft.Resources/subscriptions/resourceGroups/write",
            "Microsoft.Resources/templateSpecs/read",
            "Microsoft.Resources/templateSpecs/versions/read",
            "Microsoft.Resources/templateSpecs/versions/write",
            "Microsoft.Resources/templateSpecs/write",
            "Microsoft.Sql/servers/auditingSettings/write",
            "Microsoft.Sql/servers/databases/write",
            "Microsoft.Sql/servers/firewallRules/write",
            "Microsoft.Sql/servers/privateEndpointConnectionsApproval/action",
            "Microsoft.Sql/servers/read",
            "Microsoft.Sql/servers/write",
            "Microsoft.Storage/storageAccounts/PrivateEndpointConnectionsApproval/action",
            "Microsoft.Storage/storageAccounts/blobServices/containers/read",
            "Microsoft.Storage/storageAccounts/blobServices/containers/write",
            "Microsoft.Storage/storageAccounts/fileServices/shares/write",
            "Microsoft.Storage/storageAccounts/listkeys/action",
            "Microsoft.Storage/storageAccounts/read",
            "Microsoft.Storage/storageAccounts/write"
          ],
          "notActions": [],
          "dataActions": [],
          "notDataActions": []
        }
      <!--NeedCopy-->
      
    3. AppRegistration.ps1sessionrecordingdeployment.json 放置在同一个文件夹中。
    4. 根据需要运行以下任一命令。

      要创建应用程序并使用之前的自定义角色文件 (sessionrecordingdeployment.json) 为其分配最低权限,请运行:

      .\AppRegistration.ps1 -tenantId <tenant ID> -subscriptionId <subscription ID> -appName <application name> -role "Citrix Session Recording Deployment"
      <!--NeedCopy-->
      

      要创建应用程序并为其分配 Azure 内置贡献者角色,请运行:

      .\AppRegistration.ps1 -tenantId <tenant ID> -subscriptionId <subscription ID> -appName <application name> -role "Contributor"
      <!--NeedCopy-->
      

    方法 2:

    转至 Azure 门户自行注册应用程序。为应用程序授予适当的权限。有关所需的最低权限,请参阅方法 1 中的 sessionrecordingdeployment.json 文件。

  8. 为稍后安装的 Session Recording Server 指定配置。

    将 VM 创建为 Session Recording Server

    注意:

    • 如果有 VM 数量字段,则会预先填充建议的数字。请根据需要更改该数字。
    • 估算的成本基于标准定价,不考虑折扣。您可以期望实际成本低于预期。
  9. 将 Session Recording Server 与您的 VDA 加入到同一个域中,并为 Session Recording Server 指定证书。

    • 如果您的 VDA 连接到 Active Directory 域,请选中 Join servers to an Active Directory domain(将服务器加入到 Active Directory 域)复选框并输入相关信息。

    • 如果您的 VDA 连接到 Azure Active Directory (Azure AD) 域,请取消选中 Join servers to an Active Directory domain(将服务器加入到 Active Directory 域)复选框。完成当前站点的创建后,请务必手动将 Session Recording Server 加入到同一个 Azure AD 域。请注意,纯 Azure AD 部署仅适用于 Session Recording 2402 及更高版本。

    将服务器加入到 Active Directory 域

    将服务器加入到 Azure Active Directory 域

  10. 配置 Azure 存储帐户和文件共享以存储您的录制文件。有关定价信息,请参阅 Azure 文件定价

    配置 Azure 存储帐户和文件共享以存储您的录制文件

  11. 在 Azure 中创建两个 SQL 数据库。一个用作 Session Recording 数据库(名为 sessionrecording),另一个用作管理员日志记录数据库(名为 sessionrecordinglogging)。

    为 ARM 模板创建两个 SQL 数据库

  12. 创建负载平衡器以在 Session Recording Server 之间分发工作负载。在 Restrict access of the load balancer to only the following addresses(将负载平衡器的访问权限限制为仅限以下地址)字段中输入 VDA 的 IP 地址或范围并将其用逗号 (,) 分隔。有关定价信息,请参阅负载平衡定价

    为 ARM 模板创建负载平衡器

  13. (可选)对要创建的 Azure 资源应用标记。

    将标记应用到 Azure 资源

  14. 创建安全客户端,以将 Session Recording Server 载入到 Session Recording 服务。

    单击创建客户端,让 Citrix 代表您创建安全客户端。或者,您可以通过 Citrix Cloud 控制台的 Identity and Access Management(身份识别和访问管理)> API Access(API 访问权限)选项卡创建安全客户端,然后填写以下信息。

    “创建客户端”按钮

  15. 查看有关要创建的资源的摘要,并在需要时单击铅笔图标以编辑您的设置。之后,请单击下载模板。包含部署方式以及要部署的资源的 AEM 模板随后将下载到您的计算机上的下载文件夹。您还可以在服务器管理页面上查看新创建的站点。

    AEM 模板摘要

  16. 转到下载文件夹并解压缩 ARM 模板。打开解压缩的文件夹,在地址栏中键入 PowerShell,然后按 Enter 键。等到在该文件夹中打开 PowerShell 窗口。

  17. 运行名为 DeploySessionRecording.ps1 的 JavaScript 对象表示法 (JSON)。为提示的参数提供值。实际参数因您在创建模板时指定的设置而异。例如:

    运行 ARM 模板脚本

    运行 ARM 模板脚本时提示的参数

  18. 转至 Azure 门户,找到包含您的部署的资源组,然后检查部署进度。等到整个部署显示成功

    Azure 中的部署进度

  19. 返回 Session Recording 服务的服务器管理页面。找到新创建的站点,您将看到设置按钮可用。单击设置以设置 Session Recording 以使其启动并运行。

    新站点旁边的“设置”按钮

    检查设置 Session Recording 的必备条件

    要设置 Session Recording,您需要指定 Session Recording Server 的版本来安装和上载 resourceInfo.json 文件。

    选择 Session Recording Server 的版本

    上载资源信息

    输入您的数据库的凭据。

    输入数据库的凭据

    单击开始启动。然后,您可以在服务器管理页面上检查设置进度。

    安装正在进行中

    设置步骤

    可以在服务器列表中查看单个服务器的安装进度。

    单个服务器的安装进度

    当所有 Session Recording Server 都显示在列表中可用时,您的站点创建已完成,指定的资源将部署到 Azure。

    通过 ARM 进行全面部署