Dynamics 365 CE (Sales, CRM) IAM PowerShell Module

Hot on the heels of the post from earlier this week with my Dynamics 365 F&O IAM PowerShell Module here is another Dynamics 365 PowerShell Module. This one for Dynamics 365 CE (Sales, CRM). This one is to expose and simplify integration with Dynamics 365 CE (Sales, CRM) for Users and Roles.

PowerShell Gallery Module Link

GitHub Link

Prerequisites

API integration for One Identity to Dynamics 365 Sales, CRM requires:

  • registration of an Entra ID Application
  • registration of a Dynamics 365 Entra ID Application association

Entra ID (for D365 CE Connections)

  • Register an Entra ID Application through the Application Registration Entra ID Blade
    • Configure the RedirectURI as https://localhost
    • Record the following configuration items
      • from the Overview Page
        • Application (client) ID
        • Directory (tenant) ID
    • Create a Client secret from the Certificates & secrets page
      • record the Client Secret value
    • Add the following API Permissions to allow created of Entra ID User Accounts, License Assignment and D365 Sales API Access
      • Dynamics CRM => Delegated => user_impersonation
      • Microsoft Graph
        • Application => LicenseAssignment.ReadWrite
        • Application => User.ReadWrite.All
        • Delegated (default) => User.Read

Dynamics 365 CRM, Sales

  • Create a Dynamics 365 Application User with permissions for D365 Sales User administration. Replace the YOURTENANT section of the URL with you D365 Sales Tenant. https://admin.powerplatform.microsoft.com/environments/YOURTENANT/appusers Application Users
    • for App Name or App ID select the Application (client) ID from your Entra ID Application registration
    • Assign role(s) to allow D365 User and Role Administraton (e.g. System Administration)

Dependancies

The following PowerShell Modules are leveraged by the D365SalesIAM PowerShell Module and should be installed via an Administrative PowerShell session on the host interacting with D365 Sales. They are also included in this repo and auto-imported on module load.

D365 Sales User Creation Overview

  1. To create/enable a user for D365 Sales, an Entra ID (fka Azure AD) user account is required.
  2. Add a Dynamics License to the Entra ID User Account
  3. A D365 Sales user account is created linked to the Entra ID account (using ObjectID)
  4. D365 Sales roles are added to the D365 Sales user account

D365SalesIAM PowerShell Module

Install the Module

Install the module from the PowerShell Gallery.

Install-Module D365SalesIAM

Import the Module

Assuming the PowerShell Module has been installed from the PowerShell Gallery

Import-Module D365SalesIAM 

Module cmdlets: This is the list of cmdlets in the module

Get-Command -Module D365SalesIAM | Sort-Object Name  

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Add-D365SalesRoleToSystemUser                      1.0.1      D365SalesIAM
Function        Get-D365SalesBusinessUnits                         1.0.1      D365SalesIAM
Function        Get-D365SalesOrganisations                         1.0.1      D365SalesIAM
Function        Get-D365SalesRolesSystemUserMembership             1.0.1      D365SalesIAM
Function        Get-D365SalesSystemRoles                           1.0.1      D365SalesIAM
Function        Get-D365SalesSystemUserRoles                       1.0.1      D365SalesIAM
Function        Get-D365SalesSystemUsers                           1.0.1      D365SalesIAM
Function        Get-D365SalesToken                                 1.0.1      D365SalesIAM
Function        Get-MicrosoftGraphToken                            1.0.1      D365SalesIAM
Function        Invoke-MicrosoftGraphPostRequest                   1.0.1      D365SalesIAM
Function        New-D365SalesSystemUser                            1.0.1      D365SalesIAM
Function        Remove-D365SalesRoleFromSystemUser                 1.0.1      D365SalesIAM
Function        Update-D365SalesSystemUser                         1.0.1      D365SalesIAM

Environment Configuration

Environment configuration sets a series of Global Variables that are used to interact with D365 Sales via the WebAPI and maintain an Access Token from Entra ID.

$myD365SalesCreds = Get-Credential -Message "Enter the ClientID of the Entra ID Application you've registered and the associated ClientSecret you created."
Set-D365SalesGlobals -D365SalesOrgURI "https://orgc1234ebd.api.crm6.dynamics.com/" -EntraIDTenantID "d227e874-80EE-4640-8a16-a86e1d3e111e" -D365SalesCreds $myD365SalesCreds

D365 Sales Organisation

Creation of users requires assignment with a Organisation. To get the name of your organisation the Get-D365SalesOrganisations cmdlet can be used.

$myOrganisations = Get-D365SalesOrganisations
$myOrganisations.name
orgc1234ebd

D365 Sales Business Unit

Creation of users requires assignment with a Business Unit. To get a list of the business units the Get-D365SalesBusinessUnits cmdlet can be used.

$myBusinessUnit = Get-D365SalesBusinessUnits 
$myBusinessUnit | select-object -Property name

name
----
orgc1234ebd

Create Entra ID User Account

Create an Entra ID Account that will then be assigned a D365 Sales License and linked in D365 Sales. The following creates a template and then assigns user account property values for a user named Andrew Ellis.

# New Entra ID User
$newEntraIDUserTemplate = @{
    accountEnabled    = $true 
    givenName         = $null
    surname           = $null 
    mail              = $null 
    displayName       = $null 
    mailNickname      = $null
    userPrincipalName = $null 
    passwordProfile   = @{
        "forceChangePasswordNextSignIn" = $true
        "password"                      = (New-Guid).Guid
    }
    usageLocation     = $null 
}

$newEntraIDUserDetails = $newEntraIDUserTemplate.PsObject.Copy()
$newEntraIDUserDetails.givenName = "Andrew"
$newEntraIDUserDetails.surname = "Ellis"
$newEntraIDUserDetails.displayName = "Andrew Ellis"
$newEntraIDUserDetails.mailNickname = "AndrewE"
$newEntraIDUserDetails.userPrincipalName = "Andrew.Ellis@idmspecialist.onmicrosoft.com"
$newEntraIDUserDetails.mail = $newEntraIDUserDetails.userPrincipalName
$newEntraIDUserDetails.usageLocation = "AU"
$newEntraIDUserDetails | ConvertTo-Json

$entraIDToken = Get-MicrosoftGraphToken -msgraphCredential $Global:d365SalesCreds -EntraIDTenantID $Global:EntraIDTenantID 

$newEntraIDUser = Invoke-MicrosoftGraphPostRequest -URI "https://graph.microsoft.com/v1.0/users" -msgraphAccessToken $entraIDToken -postBody ($newEntraIDUserDetails | ConvertTo-Json)
$newEntraIDUser

@odata.context    : https://graph.microsoft.com/v1.0/$metadata#users/$entity
id                : ddc2e8c1-836d-471d-871c-516eabe4c012
businessPhones    : {}
displayName       : Andrew Ellis
givenName         : Andrew
jobTitle          : 
mail              : Andrew.Ellis@idmspecialist.onmicrosoft.com
mobilePhone       : 
officeLocation    : 
preferredLanguage : 
surname           : Ellis
userPrincipalName : Andrew.Ellis@idmspecialist.onmicrosoft.com

Assign Entra D365 Sales Licenses

The licenses to be applied to users will depend on the D365 Modules being used.

A list of a few are shown below.

<#

id                                          skuId                                skuPartNumber
--                                          -----                                -------------
dNgn0jOAQEaKFqhuHT6YLsVvEuqeoeJCpzHanUN7_88 ea126fc5-a19e-42e2-a731-da9d437bffcf DYN365_ENTERPRISE_PLAN1
dNgn0jOAQEaKFqhuHT6YLlgpyW7BPNtJlb28azeY33E 6ec92958-3cc1-49db-95bd-bc6b3798df71 Dynamics_365_Sales_Premium_Viral_Trial
dNgn0jOAQEaKFqhuHT6YLtyh2i5tlnVEk9aO6N_ZaHc 2edaa1dc-966d-4475-93d6-8ee8dfd96877 DYN365_SALES_PREMIUM

#>

$d365SalesLicenses = '{
    "addLicenses": [
    {
        "disabledPlans": [],
        "skuId": "ea126fc5-a19e-42e2-a731-da9d437bffcf",
        "skuId": "6ec92958-3cc1-49db-95bd-bc6b3798df71",
        "skuId": "2edaa1dc-966d-4475-93d6-8ee8dfd96877"
    }
    ],
    "removeLicenses": []
}'

Invoke-MicrosoftGraphPostRequest -URI "https://graph.microsoft.com/v1.0/users/$($newEntraIDUser.id)/assignLicense" -msgraphAccessToken $entraIDToken -postBody $d365SalesLicenses

Create a new D365 Sales System User

This example creates a D365 Sales System User template then populates it with the values from the Entra ID account created earlier before creating the D365 Sales System User.

# Define the new system user details
$newD365SalesUserTemplate = @{
    "firstname"                    = $null
    "lastname"                     = $null
    "fullname"                     = $null
    "domainname"                   = $null
    "userlicensetype"              = $null
    "businessunitid@odata.bind"    = "/businessunits($($myBusinessUnit.businessunitid))"
    "internalemailaddress"         = $null
    "accessmode"                   = "0"
    "windowsliveid"                = $null 
    "issyncwithdirectory"          = "True"
    "setupuser"                    = "False"
    "defaultodbfoldername"         = "Dynamics365"
    "isintegrationuser"            = "False"
    "azurestate"                   = "0"
    "isdisabled"                   = "False"
    "azureactivedirectoryobjectid" = $null 
} 

$newD365SalesUserDetails = $newD365SalesUserTemplate.PsObject.Copy()
$newD365SalesUserDetails.firstname = $newEntraIDUserDetails.givenName
$newD365SalesUserDetails.lastname = $newEntraIDUserDetails.surname
$newD365SalesUserDetails.fullname = $newEntraIDUserDetails.displayName
$newD365SalesUserDetails.domainname = $newEntraIDUserDetails.userPrincipalName
$newD365SalesUserDetails.userlicensetype = "31"
$newD365SalesUserDetails.internalemailaddress = $newEntraIDUserDetails.userPrincipalName
$newD365SalesUserDetails.windowsliveid = $newEntraIDUserDetails.userPrincipalName
$newD365SalesUserDetails.azureactivedirectoryobjectid = $newEntraIDUser.id 

New-D365SalesSystemUser -userDetails $newD365SalesUserDetails 

$newD365SalesUser = Get-D365SalesSystemUsers | Where-Object {$_.fullname -eq $newEntraIDUserDetails.displayName}
$newD365SalesUser

@odata.etag                        : W/"4743687"
address2_addresstypecode           : 1
islicensed                         : False
accessmode                         : 0
systemuserid                       : 500c2500-d58c-ee11-8179-002248e1d423
windowsliveid                      : Andrew.Ellis@idmspecialist.onmicrosoft.com
address1_composite                 : 
issyncwithdirectory                : True
address2_shippingmethodcode        : 1
domainname                         : Andrew.Ellis@idmspecialist.onmicrosoft.com
modifiedon                         : 27/11/2023 3:28:17 AM
preferredaddresscode               : 1
displayinserviceviews              : False
defaultfilterspopulated            : False
address1_shippingmethodcode        : 1
preferredemailcode                 : 1
lastname                           : Ellis
msdyn_usertype                     : 192350000
caltype                            : 0
_calendarid_value                  : 3ea9edfb-c413-4310-8772-ba98bf9ebfbf
firstname                          : Andrew
userlicensetype                    : 31
yomifullname                       : Andrew Ellis
_businessunitid_value              : 63d50198-4d85-ee11-be36-6045bd3eb6c4
incomingemaildeliverymethod        : 2
setupuser                          : False
fullname                           : Andrew Ellis
address1_addressid                 : 1a4b7895-dbb7-4bea-9a22-93552d7cbc62
msdyn_gdproptout                   : False
createdon                          : 27/11/2023 3:28:17 AM
msdyn_isexpertenabledforswarm      : False
deletedstate                       : 0
defaultodbfoldername               : Dynamics365
_defaultmailbox_value              : 560c2500-d58c-ee11-8179-002248e1d423
address1_country                   : 
versionnumber                      : 4743687
isintegrationuser                  : False
outgoingemaildeliverymethod        : 2
azurestate                         : 0
address1_addresstypecode           : 1
_modifiedby_value                  : 02fce338-d888-ee11-8179-002248e1d423
_createdby_value                   : 02fce338-d888-ee11-8179-002248e1d423
preferredphonecode                 : 1
msdyn_agentType                    : 192350000
msdyn_botprovider                  : 192350001
invitestatuscode                   : 0
identityid                         : 141
isdisabled                         : True
address2_addressid                 : 16d563f1-bd27-40f9-80ef-60635336da4f
emailrouteraccessapproval          : 2
organizationid                     : 4c7f53cd-5087-ee11-8175-002248d3a9cb
internalemailaddress               : Andrew.Ellis@idmspecialist.onmicrosoft.com
isemailaddressapprovedbyo365admin  : False
_queueid_value                     : 570c2500-d58c-ee11-8179-002248e1d423
ownerid                            : 500c2500-d58c-ee11-8179-002248e1d423
yammeruserid                       : 
nickname                           : 
address1_upszone                   : 
address2_city                      : 
address1_postofficebox             : 
importsequencenumber               : 
utcconversiontimezonecode          : 
overriddencreatedon                : 
_siteid_value                      : 
stageid                            : 
photourl                           : 
address1_latitude                  : 
address1_utcoffset                 : 
yomifirstname                      : 
msdyn_gridwrappercontrolfield      : 
address2_fax                       : 
_transactioncurrencyid_value       : 
governmentid                       : 
address2_line1                     : 
msdyn_botsecretkeys                : 
address1_telephone3                : 
applicationid                      : 
address1_telephone2                : 
address1_telephone1                : 
msdyn_owningenvironmentid          : 
address2_postofficebox             : 
address2_latitude                  : 
processid                          : 
address2_composite                 : 
traversedpath                      : 
address1_city                      : 
_positionid_value                  : 
address2_line2                     : 
address2_stateorprovince           : 
sharepointemailaddress             : 
address2_postalcode                : 
entityimage_url                    : 
msdyn_botapplicationid             : 
jobtitle                           : 
timezoneruleversionnumber          : 
address2_telephone3                : 
address2_telephone2                : 
address2_telephone1                : 
address1_postalcode                : 
address2_upszone                   : 
userpuid                           : 
address2_line3                     : 
msdyn_bothandle                    : 
msdyn_botdescription               : 
personalemailaddress               : 
address2_longitude                 : 
_modifiedonbehalfby_value          : 
address1_line2                     : 
address1_county                    : 
_territoryid_value                 : 
address1_fax                       : 
_createdonbehalfby_value           : 
address2_name                      : 
disabledreason                     : 
address2_utcoffset                 : 
applicationiduri                   : 
_mobileofflineprofileid_value      : 
address1_line1                     : 
address2_county                    : 
address1_line3                     : 
azuredeletedon                     : 
azureactivedirectoryobjectid       : ddc2e8c1-836d-471d-871c-516eabe4c012
msdyn_botendpoint                  : 
address1_stateorprovince           : 
_parentsystemuserid_value          : 
entityimage_timestamp              : 
title                              : 
mobilephone                        : 
_msdyn_defaultpresenceiduser_value : 
employeeid                         : 
exchangerate                       : 
skills                             : 
entityimageid                      : 
passporthi                         : 
yomilastname                       : 
passportlo                         : 
yomimiddlename                     : 
homephone                          : 
address1_name                      : 
address1_longitude                 : 
yammeremailaddress                 : 
entityimage                        : 
middlename                         : 
salutation                         : 
msdyn_capacity                     : 
address2_country                   : 
mobilealertemail                   : 

Update D365 Sales System User

Example of updating a user. In this example a user that was disabled is enabled.


$updateUser = @{
    "isdisabled"                   = "False"
} 

Update-D365SalesSystemUser -userDetails $updateUser -systemuserid $newD365SalesUser.systemuserid

Add D365 Roles to System User

With the D365 System User account created and linked to the Entra ID account assign D365 Roles appropriate for the user.

$baseRoles = Get-D365SalesSystemUserRoles -systemuserid $newD365SalesUser.systemuserid | Select-Object -Property name, roleid

<#

name                        roleid
----                        ------
Sales Enterprise app access cad52a75-568c-e611-80d4-00155d42a122
System Administrator        6fd50198-4d85-ee11-be36-6045bd3eb6c4
Basic User                  d3d90198-4d85-ee11-be36-6045bd3eb6c4

#>

foreach ($r in $baseRoles) {
    Add-D365SalesRoleToSystemUser -systemuserid $newD365SalesUser.systemuserid -roleid $r.roleid
}

Get D365 Sales System User Roles

Verify new roles have been assigned.

Get-D365SalesSystemUserRoles -systemuserid $newD365SalesUser.systemuserid

@odata.etag               : W/"4145227"
overwritetime             : 1/01/1900 12:00:00 AM
organizationid            : 4c7f53cd-5087-ee11-8175-002248d3a9cb
isinherited               : 0
solutionid                : 7ce2e43c-64be-44cd-9040-c16877ea6e48
roleidunique              : 7191e47b-0a34-4c21-93b8-d304d26f32af
ismanaged                 : True
roleid                    : cad52a75-568c-e611-80d4-00155d42a122
componentstate            : 0
modifiedon                : 20/11/2023 11:59:13 PM
_modifiedby_value         : 88dc0198-4d85-ee11-be36-6045bd3eb6c4
_parentrootroleid_value   : cad52a75-568c-e611-80d4-00155d42a122
createdon                 : 17/11/2023 7:33:49 PM
versionnumber             : 4145227
_businessunitid_value     : 63d50198-4d85-ee11-be36-6045bd3eb6c4
name                      : Sales Enterprise app access
_createdby_value          : 61da1f5c-0d3b-4339-be1d-2755e72f5793
_parentroleid_value       : 
overriddencreatedon       : 
importsequencenumber      : 
_modifiedonbehalfby_value : 
_roletemplateid_value     : 
_createdonbehalfby_value  : 
iscustomizable            : @{Value=False; CanBeChanged=True; ManagedPropertyLogicalName=iscustomizableanddeletable}
canbedeleted              : @{Value=True; CanBeChanged=True; ManagedPropertyLogicalName=canbedeleted}

@odata.etag               : W/"4000975"
overwritetime             : 1/01/1900 12:00:00 AM
organizationid            : 4c7f53cd-5087-ee11-8175-002248d3a9cb
isinherited               : 1
solutionid                : d68f9d76-c81a-4eea-be2b-141e69f9dd0c
roleidunique              : adb23f21-3aa0-43c7-a1e5-e4cfd63d7ccc
ismanaged                 : True
roleid                    : 6fd50198-4d85-ee11-be36-6045bd3eb6c4
componentstate            : 0
modifiedon                : 17/11/2023 3:30:46 PM
_modifiedby_value         : 61da1f5c-0d3b-4339-be1d-2755e72f5793
_parentrootroleid_value   : 6fd50198-4d85-ee11-be36-6045bd3eb6c4
createdon                 : 17/11/2023 1:31:28 PM
versionnumber             : 4000975
_businessunitid_value     : 63d50198-4d85-ee11-be36-6045bd3eb6c4
name                      : System Administrator
_createdby_value          : 
_parentroleid_value       : 
overriddencreatedon       : 
importsequencenumber      : 
_modifiedonbehalfby_value : 
_roletemplateid_value     : 627090ff-40a3-4053-8790-584edc5be201
_createdonbehalfby_value  : 
iscustomizable            : @{Value=True; CanBeChanged=True; ManagedPropertyLogicalName=iscustomizableanddeletable}
canbedeleted              : @{Value=True; CanBeChanged=True; ManagedPropertyLogicalName=canbedeleted}

@odata.etag               : W/"4145413"
overwritetime             : 1/01/1900 12:00:00 AM
organizationid            : 4c7f53cd-5087-ee11-8175-002248d3a9cb
isinherited               : 1
solutionid                : 640c0bc1-3459-48a1-ac7d-f312872b5204
roleidunique              : 79944d29-fd69-4272-a3a8-65849c6f58b5
ismanaged                 : True
roleid                    : d3d90198-4d85-ee11-be36-6045bd3eb6c4
componentstate            : 0
modifiedon                : 20/11/2023 11:59:15 PM
_modifiedby_value         : 88dc0198-4d85-ee11-be36-6045bd3eb6c4
_parentrootroleid_value   : d3d90198-4d85-ee11-be36-6045bd3eb6c4
createdon                 : 17/11/2023 1:31:28 PM
versionnumber             : 4145413
_businessunitid_value     : 63d50198-4d85-ee11-be36-6045bd3eb6c4
name                      : Basic User
_createdby_value          : 
_parentroleid_value       : 
overriddencreatedon       : 
importsequencenumber      : 
_modifiedonbehalfby_value : 
_roletemplateid_value     : 236750cd-45ae-4939-ab12-b24b920ced93
_createdonbehalfby_value  : 
iscustomizable            : @{Value=False; CanBeChanged=True; ManagedPropertyLogicalName=iscustomizableanddeletable}
canbedeleted              : @{Value=True; CanBeChanged=True; ManagedPropertyLogicalName=canbedeleted}

Get-D365SalesSystemUserRoles -systemuserid $newD365SalesUser.systemuserid | Select-Object -Property name, roleid

name                        roleid
----                        ------
Sales Enterprise app access cad52a75-568c-e611-80d4-00155d42a122
System Administrator        6fd50198-4d85-ee11-be36-6045bd3eb6c4
Basic User                  d3d90198-4d85-ee11-be36-6045bd3eb6c4

Summary

A foundational IAM module for Dynamics 365 CE (Sales and CRM) for discovering accounts and roles as well as creating accounts and assigning roles.