Friday, February 27, 2026

Converting Dynamics 365 Security Roles to Read-Only with PowerShell

 In Dynamics 365 / Dataverse, security roles often contain unintended modification privileges (Create, Write, Delete, etc.) even when they are meant to be read-only. Manually correcting them is time-consuming and error-prone — especially in environments with many custom entities.

This post shows how to safely convert existing roles into read-only roles using PowerShell.


The Problem

Roles labeled as “Read Only” still had privileges such as:

  • Create

  • Write

  • Delete

  • Append

  • AppendTo

  • Assign

  • Share

The goal was to remove modification privileges while preserving:

  • All Read privileges

  • System-level privileges

  • App and workflow functionality


The Solution

Using the Microsoft.Xrm.Data.PowerShell module:

  1. Connect via Client Secret authentication

  2. Retrieve the role

  3. Fetch associated privileges

  4. Remove only modification privileges

  5. Leave system and read privileges intact




Import-Module Microsoft.Xrm.Data.PowerShell

$crmUrl = "https://yourorg.crm.dynamics.com"
$clientId = ""
$clientSecret = ""
$tenantId = ""

$conn = Get-CrmConnection -ConnectionString "
AuthType=ClientSecret;
Url=$crmUrl;
ClientId=$clientId;
ClientSecret=$clientSecret;
TenantId=$tenantId"

$rolesToFix = @(
    "AdvancedTemp - Read Only",
    "StandardTemp - Read Only"
)

# Only remove modification privileges
$removePattern = "^(prvCreate|prvWrite|prvDelete|prvAppend$|prvAppendTo|prvAssign|prvShare)"

foreach ($roleName in $rolesToFix) {

    $role = Get-CrmRecords `
        -conn $conn `
        -EntityLogicalName role `
        -FilterAttribute name `
        -FilterOperator eq `
        -FilterValue $roleName `
        -Fields roleid

    if ($role.CrmRecords.Count -eq 0) { continue }

    $roleId = $role.CrmRecords[0].roleid

    $fetch = @"
<fetch>
  <entity name='roleprivileges'>
    <filter>
      <condition attribute='roleid' operator='eq' value='$roleId' />
    </filter>
    <link-entity name='privilege'
                 from='privilegeid'
                 to='privilegeid'
                 alias='p'>
      <attribute name='privilegeid'/>
      <attribute name='name'/>
    </link-entity>
  </entity>
</fetch>
"@

    $privileges = Get-CrmRecordsByFetch -conn $conn -Fetch $fetch

    foreach ($priv in $privileges.CrmRecords) {

        $privId = $priv."p.privilegeid"
        $privName = $priv."p.name"

        if (-not $privId -or -not $privName) { continue }

        if ($privName -match $removePattern) {

            Remove-CrmRecordAssociation `
                -conn $conn `
                -EntityLogicalName1 role `
                -Id1 ([Guid]$roleId) `
                -EntityLogicalName2 privilege `
                -Id2 ([Guid]$privId) `
                -RelationshipName roleprivileges_association
        }
    }
}



Result

This script:

  • Removes all modification privileges

  • Preserves read and system access

  • Prevents app or workflow breakage

  • Automates role cleanup at scale

PowerShell provides a reliable and repeatable way to enforce true read-only security roles in Dynamics 365.