共用方式為


用於對資料模型自訂進行分析的應用程式生命週期管理

適用於:Dynamics 365 Customer Service

本文提供有關對根據 Dynamics 365 Customer Service 全通路資料模型自訂所建立之自訂報表進行應用程式生命週期管理 (ALM) 的詳細資訊。

ALM 概觀

ALM 是您希望跨環境移動自訂報表所需變更,但又要盡量減少手動參與時的重要步驟。 適當設定的 ALM 有助於減少人工錯誤並節省時間。

進行報表自訂時,請考慮使用 ALM 來部署 Power BI 報表。 您可以透過不同的方式完成此部署:

  • 您可以使用 Power BI 部署管線跨環境移動 Power BI 報表。 或者,也可以指向正確的資料來源,以手動方式部署 Power BI 報表。
  • 由於 Customer Service 系統管理中心是模型導向應用程式,因此可以將自訂報表內嵌在其中。

適用於 Dynamics 365 的 ALM 需要在 Customer Service 系統管理中心進行報表設定。

下圖說明這些程序。 含白色文字的藍色方塊表示您在 Dynamics 365 中執行的工作。 含黑色文字的黃色方塊表示您在 Power BI 中執行的工作。

顯示 ALM 步驟的圖表。

移轉報表設定

Dynamics 365 中新增的報表會儲存在 Dataverse 實體中。

下列實體/資料表用於儲存自訂報表資訊:

  • msdyn_dataanalyticsworkspaces 含有進行歷史/即時分析初始設定時所設定的工作區資訊。

  • msdyn_dataanalyticsreport 儲存網站地圖中新增的自訂報表。

    • msdyn_reportid:Power BI 工作區中的報表識別碼。
    • msdyn_dataanalyticsreportid:Dynamics 365 中的主索引鍵。
    • msdyn_displayname:顯示在 Customer Service workspace 模型導向應用程式的自訂報表中的顯示名稱。
    • msdyn_name:報表在 Power BI 工作區中的名稱。
    • msdyn_workspaceid:上一個步驟中所設定 Power BI 工作區的工作區識別碼。
    • msdyn_datainsightsandanalyticsfeatureidf2266eb4-226f-4cf1-b422-89c5f48b40cb 是歷史資料模型自訂的功能識別碼,而 09c168be-efe2-4f08-a986-3aab7095c863 則是即時資料模型自訂的功能識別碼。

若要移轉已新增至網站地圖的報表,您必須從實體/資料表移動資料。 您可以使用設定移轉工具來達成此目的。 本文提供範例結構描述文件。 請至使用設定移轉工具在不同環境之間移動設定資料了解詳細資訊。

移轉後,您必須更新報表參考。 換句話說,您必須更新報表託管所在的 Power BI 工作區以及工作區中的報表識別碼。 本文提供範例 PowerShell 指令碼作參考,但也可以使用任何語言取得同樣的結果。

範例指令碼

本節包含下列範例指令碼:

範例結構描述檔案

下列 XML 結構描述檔案由已建立且已部署至 Power BI 工作區的自訂報表所組成。 您可以使用該檔案,從設定移轉工具匯出資料。 請至使用設定移轉工具在不同環境之間移動設定資料建立結構描述以匯出設定資料了解詳細資訊。

<entities>
  <entity name="msdyn_dataanalyticsreport" displayname="Data Analytics Report" etc="10427" primaryidfield="msdyn_dataanalyticsreportid" primarynamefield="msdyn_name" disableplugins="false">
    <fields>
      <field displayname="Report Id" name="msdyn_reportid" type="string" customfield="true" />
      <field displayname="Data Analytics Report" name="msdyn_dataanalyticsreportid" type="guid" primaryKey="true" />
      <field displayname="Display name" name="msdyn_displayname" type="string" customfield="true" />
      <field displayname="Name" name="msdyn_name" type="string" customfield="true" />
      <field displayname="Report Display Order" name="msdyn_displayorder" type="number" customfield="true" />
      <field displayname="Report Provision Status" name="msdyn_provisionstatus" type="bool" customfield="true" />
      <field displayname="Report Page" name="msdyn_reportpage" type="string" customfield="true" />
      <field displayname="Report Group" name="msdyn_reportgroup" type="string" customfield="true" />
      <field displayname="Report Entity Name" name="msdyn_reportentityname" type="string" customfield="true" />
      <field displayname="Report Template Id" name="msdyn_reporttemplateid" type="string" customfield="true" />
      <field displayname="Dataset Id" name="msdyn_datasetid" type="string" customfield="true" />
      <field displayname="Is Enabled" name="msdyn_isenabled" type="bool" customfield="true" />
      <field displayname="Workspace Id" name="msdyn_workspaceid" type="string" customfield="true" />
      <field displayname="datainsightsandanalyticsfeatureId" name="msdyn_datainsightsandanalyticsfeatureid" type="entityreference" lookupType="msdyn_datainsightsandanalyticsfeature" customfield="true" />
      <field displayname="Analytics Checksum" name="msdyn_analyticschecksum" type="number" customfield="true" />
    </fields>
    <filter>&lt;filter type = 'and'&gt;
          &lt;condition attribute = 'msdyn_datainsightsandanalyticsfeatureid' operator = 'eq' value = '<<Feature ID of the model customization>> '/&gt;
          &lt;condition attribute = 'msdyn_displayname' operator = 'eq' value = '<<custom report name>>'/&gt; 
        &lt;/filter&gt;</filter>
  </entity>
</entities>

使用 f2266eb4-226f-4cf1-b422-89c5f48b40cb 做為歷史資料的功能識別碼,並使用 xxxx 做為即時資料的功能識別碼。

範例 PowerShell 指令碼

下列指令碼使用用戶端識別碼祕密驗證機制。 不過,您可以修改指令碼來使用任何類型的驗證。

此指令碼透過 Power BI Rest API 連接至 Power BI。 又透過 Dataverse Web API 連接至 Dataverse。

#Get Power BI access token
function Get-PBIAccessToken {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $TenantId,
        [Parameter(Mandatory=$true)]
        $PBIAppId,
        [Parameter(Mandatory=$true)]
        $PBIClientSecret
    )

    $authority = "https://login.microsoftonline.com/$TenantId/oauth2/token"
    $resource = "https://analysis.windows.net/powerbi/api"
    $body = @{
        "grant_type" = "client_credentials"
        "client_id" = $PBIAppId
        "client_secret" = $PBIClientSecret
        "resource" = $resource
    }
    Write-Host "Retreiving PBI Access Token"
    $tokenResponse = Invoke-RestMethod -Method Post -Uri $authority -Body $body
    
    return $tokenResponse.access_token
}
#Get Dataverse access token
function Get-DVAccessToken{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $tenantId,
        [Parameter(Mandatory=$true)]
        $clientId,
        [Parameter(Mandatory=$true)]
        $clientSecret,
        [Parameter(Mandatory=$true)]
        $dataVerseURL
    )
    $oAuthTokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"

    # OAuth Body Access Token Request
    $authBody = @{
        client_id = $clientId;
        client_secret = $ClientSecret;    
        scope = "$($dataVerseURL)/.default"    
        grant_type = 'client_credentials'
    }

    # Parameters for OAuth Access Token Request
    $authParams = @{
        URI = $oAuthTokenEndpoint
        Method = 'POST'
        ContentType = 'application/x-www-form-urlencoded'
        Body = $authBody
    }
    Write-Host "Retreiving CRM Access Token"
    # Get Access Token
    $authResponseObject = Invoke-RestMethod @authParams -ErrorAction Stop
    return $authResponseObject
}
function Get-DVWorkspaceId {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $dvAuthResponseObject,
        [Parameter(Mandatory=$true)]
        $dataVerseURL
    )

    $getDataRequestUri = 'msdyn_dataanalyticsworkspaces?$top=5&$select=msdyn_workspaceid,msdyn_name&$filter=(msdyn_name ne ''Customer Service Managed Workspace'' and _msdyn_datainsightsandanalyticsfeatureid_value eq ''f2266eb4-226f-4cf1-b422-89c5f48b40cb'')'
    # Set up web API call parameters, including a header for the access token
    $getApiCallParams = @{
        URI = "$($dataVerseURL)/api/data/v9.1/$($getDataRequestUri)"
        Headers = @{
            "Authorization" = "$($dvAuthResponseObject.token_type) $($dvAuthResponseObject.access_token)"
            "Accept" = "application/json"
            "OData-MaxVersion" = "4.0"
            "OData-Version" = "4.0"
        }
        Method = 'GET'
    }
    Write-Host "Retreiving Dataverse DCCP Workspace Id"
    # Call API to Get Response
    $getApiResponseObject = Invoke-RestMethod @getApiCallParams -ErrorAction Stop

    return $getApiResponseObject.value[0].msdyn_workspaceid
}
function Get-DVDCCPReports {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $dvAuthResponseObject,
        [Parameter(Mandatory=$true)]
        $workspaceId,
        [Parameter(Mandatory=$true)]
        $dataVerseURL
    )
    Write-Host "Retreiving DV DCCP Reports"
    $getDataRequestUri = 'msdyn_dataanalyticsreports?$select=msdyn_dataanalyticsreportid,msdyn_name,msdyn_workspaceid&$filter=(msdyn_workspaceid ne '''+$workspaceId+''' and _msdyn_datainsightsandanalyticsfeatureid_value eq ''f2266eb4-226f-4cf1-b422-89c5f48b40cb'')'
      # Set up web API call parameters, including a header for the access token
      $getApiCallParams = @{
          URI = "$($dataVerseURL)/api/data/v9.1/$($getDataRequestUri)"
          Headers = @{
              "Authorization" = "$($dvAuthResponseObject.token_type) $($dvAuthResponseObject.access_token)"
              "Accept" = "application/json"
              "OData-MaxVersion" = "4.0"
              "OData-Version" = "4.0"
          }
          Method = 'GET'
      }
      $getApiResponseObject = Invoke-RestMethod @getApiCallParams -ErrorAction Stop
      # Output
      $dvReports = $getApiResponseObject.value
    return $dvReports    
}

function Get-PBIReports {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $accessToken,
        [Parameter(Mandatory=$true)]
        $workspaceId
    )
    Write-Host "Retreiving PBI Workspace Reports"
    $headers = @{
        "Authorization" = "Bearer $accessToken"
          'Content-Type' = 'application/json'
      }
    $uri = "https://api.powerbi.com/v1.0/myorg/groups/$workspaceId/reports"
    $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get
    $pbiReports = $response.value
    return $pbiReports
    
}

function Update-DVReportReferences
{
      [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        $pbiAccessToken,
        [Parameter(Mandatory=$true)]
        $dvAuthResponseObject,
        [Parameter(Mandatory=$true)]
        $workspaceId,
        [Parameter(Mandatory=$true)]
        $dataVerseURL
    )
    
    $pbiReports = Get-PBIReports -accessToken $pbiAccessToken -workspaceId $workspaceId
    Write-Host $pbiReports.Count
    $dvReports = Get-DVDCCPReports -dvAuthResponseObject $dvAuthResponseObject -workspaceId $workspaceId -dataVerseURL $dataVerseURL
    Write-Host $dvReports.Count
    Write-Host "Updating DCCP report references"
    $pbiReports
    foreach ($item in $dvReports)
      {
          $item.msdyn_name          
          $report = $pbiReports.value | Where-Object {$_.name -eq $item.msdyn_name}
          if($report -ne $null)
          {
              
              Write-Host "Updating report reference for $($item.msdyn_name) with PBI $($report.id)"
              $dvReportId = $item.msdyn_dataanalyticsreportid
              $patchRequestUri = "msdyn_dataanalyticsreports($($dvReportId))"+'?$select=msdyn_workspaceid,msdyn_dataanalyticsreportid'
              $updateBody  = @{
                  'msdyn_workspaceid' = ''+$workspaceId+''
                  'msdyn_reportid' = ''+$report.id+''
              } | ConvertTo-Json
              # Set up web API call parameters, including a header for the access token
              $patchApiCallParams = @{
                  URI = "$($dataVerseURL)/api/data/v9.1/$($patchRequestUri)"
                  Headers = @{
                      "Authorization" = "$($dvAuthResponseObject.token_type) $($dvAuthResponseObject.access_token)"
                      "Accept" = "application/json"
                      "OData-MaxVersion" = "4.0"
                      "OData-Version" = "4.0"
                      "Content-Type" = "application/json; charset=utf-8"
                      "Prefer" = "return=representation"  # in order to return data
                      "If-Match" = "*" 
                  }
                  Method = 'PATCH'
                  Body = $updateBody
              }
              
              $patchApiResponseObject = Invoke-RestMethod @patchApiCallParams -ErrorAction Stop   
          }
          else
          {
              Write-Host "Corresponding PBI report not found in PBI workspace with name $($item.msdyn_name)"
          }
      }
      return $pbiReports
}

###Sample usage########
#$PBIAppId = '<<Client ID which has access to Power BI workspace>>' 
#$TenantId = '<<Tenant Id of the DV/PBI organization>>'    
#$PBIClientSecret = "<<Secret of application user PBI>>" 
#$AppId = '<<Dataverse App id>>' 
#$ClientSecret = '<<DV client Secret>>' 
#$PowerPlatformEnvironmentUrl = "<<DV URL>>" 
#$PBIAccessToken = Get-PBIAccessToken -TenantId $TenantId -PBIAppId $PBIAppId -PBIClientSecret $PBIClientSecret
#$CRMAccessToken = Get-DVAccessToken -tenantId $TenantId -dataVerseURL $PowerPlatformEnvironmentUrl -clientId $AppId -clientSecret $ClientSecret
#$workspaceId = Get-DVWorkspaceId -dvAuthResponseObject $CRMAccessToken -dataVerseURL $PowerPlatformEnvironmentUrl
#Update-DVReportReferences -pbiAccessToken $PBIAccessToken -dvAuthResponseObject $CRMAccessToken -workspaceId $workspaceId -dataVerseURL $PowerPlatformEnvironmentUrl 

範例 Azure DevOps YAML 管線

# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- main
pool:
  vmImage: windows-latest
steps:
- task: CopyFiles@2
  inputs:
    Contents: '**'
    TargetFolder: '$(Build.ArtifactStagingDirectory)'
    CleanTargetFolder: true
    ignoreMakeDirErrors: true
  displayName: 'Copy files from Repo'
- task: PowerPlatformToolInstaller@2
  inputs:
    DefaultVersion: true
- task: PowerPlatformExportData@2
  inputs:
    authenticationType: 'PowerPlatformSPN'
    PowerPlatformSPN: 'Optimize25CRM'
    Environment: '$(BuildTools.EnvironmentUrl)'
    SchemaFile: '$(Build.ArtifactStagingDirectory)\source\Optimize25Schema\schema_sample.xml'
    DataFile: 'data.zip'
  displayName: 'Export reports sitemap data from Source'
- task: PowerPlatformImportData@2
  inputs:
    authenticationType: 'PowerPlatformSPN'
    PowerPlatformSPN: 'Optimize25POC'
    Environment: '$(BuildTools.EnvironmentUrl)'
    DataFile: 'data.zip'
  displayName: 'Import reports sitemap data to Target' 
- task: PowerShell@2  
  inputs:
    targetType: 'inline'
    script: |
      $ScriptContent = Get-Content "$(Build.ArtifactStagingDirectory)\source\automation\PipelineScript.ps1" -Raw
      Invoke-Expression $ScriptContent      
      # Write your PowerShell commands here.
      Write-Host "Assigning connection variables"   
      $PBIAppId = '$(PBIClientId)' 
      $PBIClientSecret = '$(PBIClientSecret)'
      $TenantId = '$(TenantId)'    
      $AppId = '$(CRMClientId)' 
      $ClientSecret = '$(CRMClientSecret)'  
      $PowerPlatformEnvironmentUrl = '$(GetConnectionVar.PowerPlatformEnvironmentUrl)' 
      $PBIAccessToken = Get-PBIAccessToken -TenantId $TenantId -PBIAppId $PBIAppId -PBIClientSecret $PBIClientSecret
      $CRMAccessToken = Get-DVAccessToken -tenantId $TenantId -dataVerseURL $PowerPlatformEnvironmentUrl -clientId $AppId -clientSecret $ClientSecret
      $featureId = 'f2266eb4-226f-4cf1-b422-89c5f48b40cb'      
      $workspaceId = Get-DVWorkspaceId -dvAuthResponseObject $CRMAccessToken -dataVerseURL $PowerPlatformEnvironmentUrl -featureId $featureId
      Write-Host $workspaceId
      Update-DVReportReferences -pbiAccessToken $PBIAccessToken -dvAuthResponseObject $CRMAccessToken -workspaceId $workspaceId -dataVerseURL $PowerPlatformEnvironmentUrl  -featureId $featureId

下列資源可協助您進一步了解 Dynamics 365 Customer Service 全通路的內建分析功能。

下一個步驟