Featured image of post Migrating Microsoft Dataverse Access Team Templates via Azure DevOps

Migrating Microsoft Dataverse Access Team Templates via Azure DevOps

Access Team Templates is a feature that has been available within the Microsoft Dataverse / the Dynamics 365 Customer Engagement applications for many years now, but one which I don’t think is actively used (at least, based on the projects I’ve been involved in). For situations where you have more unusual access requirements for a particular table type, they provide a flexible mechanism of allowing a specific group of users to have a set of access permissions targeting one row but a completely different set for another. This is achieved by enrolling each user into a unique team created for each row, which grants the permission set defined as part of the template. Access Team Templates are a powerful feature but do come with some baggage and gotchas. Depending on the number of rows in your system, you could end up with many thousands of different Access Teams setup, all of which required a degree of administration. In addition, each table type is limited to a maximum of two access team templates. It’s also impossible to include templates as part of a solution or migrate them easily using methods such as the Configuration Migration tool. When you’re considering using them, keep these in mind to avoid any potential annoyance further down the road. đŸ˜‰

As part of a recent project, we were actively using Access Team Templates. One of the above gotchas hit us quickly: the inability to migrate Access Team Templates across environments. Most crucially, we wanted to retain the Globally Unique Identifier (GUID) value to support some integrations that we had built out. Due to the limitations outlined above, we needed an alternate solution to straightforwardly import a single Access Team Template within an Azure Pipeline deployment step. The solution we opted for was a PowerShell script, leveraging the very excellent Microsoft.Xrm.Data.PowerShell module. Here’s the script in full:

param(
    #objectTypeCode: Unique Code that identifies the table in the environment for the Access Team Template. Always potentially different.
    [Parameter(Mandatory=$true)]
    [int]$objectTypeCode,
    #atName: Name of the Access Team Template
    [Parameter(Mandatory=$true)]
    [String]$atName,
    #accessRights: Number which represents the access rights defined for the template. Refer to this article for details on how to construct: https://docs.microsoft.com/en-us/dynamics365/customer-engagement/web-api/accessrights?view=dynamics-ce-odata-9
    [Parameter(Mandatory=$true)]
    [int]$accessRights,
    #d365URL: URL of the environment to connect to
    [Parameter(Mandatory=$true)]
    [String]$d365URL,
    #clientID: AAD Client ID for the Application User linked to this environment
    [Parameter(Mandatory=$true)]
    [String]$clientID,
    #clientSecret: AAD Client Secret for the Application User linked to this environment
    [Parameter(Mandatory=$true)]
    [String]$clientSecret
)

#Install dependencies
Install-Module Microsoft.Xrm.Data.PowerShell -Scope CurrentUser -Force
#Connect to the D365 environment
$conn = Connect-CrmOnline -ServerUrl $d365URL -ClientSecret $clientSecret -OAuthClientId $clientID -OAuthRedirectUri "http://localhost"
#We first attempt to retrieve the rows if they already exist, and update it accordingly; if this errors, then the row does not exist, so we need to create it instead
Write-Host "Processing Access Team Template for $atName..."
try
{
    $atTemplate = Get-CrmRecord -conn $conn -EntityLogicalName teamtemplate -Id "44396647-CEDF-EB11-BACB-000D3A5810F2" -Fields teamtemplateid,teamtemplatename,objecttypecode,defaultaccessrightsmask,issystem
    $atTemplateId = $atTemplate.teamtemplateid
    Write-Host "Got existing Access Team Template row with ID $atTemplateId!"
    $atTemplate.teamtemplatename = $atName
    $atTemplate.objecttypecode = $objectTypeCode
    $atTemplate.defaultaccessrightsmask = $accessRights
    $atTemplate.issystem = 0
    Set-CrmRecord -conn $conn -CrmRecord $atTemplate
    Write-Host "Successfully updated Access Team Template row with ID $atTemplateId!"
}
catch [System.Management.Automation.RuntimeException]
{
    Write-Host "Access Template row with ID $atTemplateId does not exist, creating..."
    $atTemplateId = New-CrmRecord -conn $conn -EntityLogicalName teamtemplate `
    -Fields @{"teamtemplateid"=[guid]"{44396647-CEDF-EB11-BACB-000D3A5810F2}";"teamtemplatename"=$atName;"objecttypecode"=$objectTypeCode;"defaultaccessrightsmask"=$accessRights;"issystem"=0}   
    Write-Host "Successfully created new Access Template row with ID $atTemplateId"
}
Write-Host "Script execution finished!"

Now, a couple of things to note with this:

  • To run this script, you will need to have an Application User setup with the appropriate Client ID / Secret setup. I recommend that the secret value be saved as a secure parameter when using it from your pipeline.
  • The accessRights parameter value needs to be a sum of all the potential access rights you wish to grant, based on the numbers shown in this article. For example, if you wanted to assign Read, Append and Append To permissions, the number you would supply would be 21 (1 + 4 + 16 = 21)
  • You may need to replace the ID value (in this scenario, 44396647-CEDF-EB11-BACB-000D3A5810F2) to match the ID of any existing template you’ve created in your Dev system.
  • The objectTypeCode is the unique integer value that Dataverse assigns to each table in the environment. For out of the box tables, such as Account, Contact etc., the value will always be the same, regardless of the environment. However, custom tables are not subject to this rule, so take care and use tools such as the Metadata Browser to verify before deploying.

Other than that, the script works pretty reliably and, most importantly, can be called as part of any PowerShell task during your build or deployment steps.

Many features available as part of Microsoft Dataverse today have been around since the very early days of Microsoft Dynamics CRM. Access Team Templates is one of these features and one that we shouldn’t be too hasty to discard entirely as being unsuitable or “too legacy” to consider using today. It is very much the right solution to consider for specific situations and one that Microsoft will actively encourage you to consider as well. It’s just a shame that some of the tooling available today doesn’t easily support the ability to move Access Team Templates between environments. If you get a spare second, I’d encourage you to upvote this Idea on the Microsoft Dynamics 365 site to increase the likelihood that the product team will address this gap for us.

comments powered by Disqus