functions/Add-PpacUser.ps1
|
<#
.SYNOPSIS Add a user to a Power Platform environment. .DESCRIPTION Creates the Dataverse systemuser for a Microsoft Entra ID user when it does not exist yet, enables the user when Dataverse created it disabled, and optionally assigns one or more security roles. Add-PpacSecurityRoleMember requires the systemuser to already exist and be enabled, and Entra users without a mail value need the UPN as e-mail fallback. Only depends on exported cmdlets (Get-BapEnvironment, Get-PpacUser, Add-PpacSecurityRoleMember) and the internal Get-GraphUser helper. Business Unit lookup is done inline via REST so this file can also be tested standalone. .PARAMETER EnvironmentId The id of the environment that you want to work against. Can be either the environment name, the environment GUID (PPAC) or the LCS environment ID. .PARAMETER User The user that you want to add to the Power Platform environment. Can be either the User Principal Name (UPN), mail address, display name or Entra object id. .PARAMETER Role One or more security roles to assign to the user after creation. The name of the security role, as accepted by Add-PpacSecurityRoleMember. When omitted no role is assigned. .EXAMPLE PS C:\> Add-PpacUser -EnvironmentId "ContosoEnv" -User "megan.bowen@contoso.com" -Role "System Administrator" Creates (or enables) the systemuser for megan.bowen@contoso.com and assigns the System Administrator role. .NOTES Author: Mötz Jensen (@Splaxi) #> function Add-PpacUser { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] [CmdletBinding()] param ( [Parameter (Mandatory = $true)] [string] $EnvironmentId, [Parameter (Mandatory = $true)] [string] $User, [Alias('RoleName')] [string[]] $Role ) begin { $envObj = Get-BapEnvironment ` -EnvironmentId $EnvironmentId | ` Select-Object -First 1 if ($null -eq $envObj) { $messageString = "The supplied EnvironmentId: <c='em'>$EnvironmentId</c> didn't return any matching environment details. Please verify that the EnvironmentId is correct - try running the <c='em'>Get-BapEnvironment</c> cmdlet." Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because environment was NOT found based on the id." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) } if (Test-PSFFunctionInterrupt) { return } $baseUri = $envObj.PpacEnvUri $secureToken = (Get-AzAccessToken -ResourceUrl $baseUri -AsSecureString).Token $tokenWebApiValue = ConvertFrom-SecureString -AsPlainText -SecureString $secureToken $headersWebApi = @{ "Authorization" = "Bearer $($tokenWebApiValue)" "Content-Type" = "application/json" } $entraUser = Get-GraphUser ` -User $User if ($null -eq $entraUser) { $messageString = "The supplied User: <c='em'>$User</c> didn't return any matching user details in Azure AD / Entra ID. Please verify that the User is correct - try running the <c='em'>Get-AzADUser</c> cmdlet." Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because user was NOT found based on the UPN." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) } if (Test-PSFFunctionInterrupt) { return } } process { if (Test-PSFFunctionInterrupt) { return } # Entra mail is not guaranteed (e.g. some users have mail = null), fall back to UPN. $emailAddress = $entraUser.mail if ([System.String]::IsNullOrEmpty($emailAddress)) { $emailAddress = $entraUser.userPrincipalName } $crmUser = Get-PpacUser ` -EnvironmentId $envObj.PpacEnvId ` -User $emailAddress | ` Select-Object -First 1 if ($null -eq $crmUser -and -not [System.String]::IsNullOrEmpty($entraUser.userPrincipalName)) { $crmUser = Get-PpacUser ` -EnvironmentId $envObj.PpacEnvId ` -User $entraUser.userPrincipalName | ` Select-Object -First 1 } if ($null -eq $crmUser) { $colBuRaw = Invoke-RestMethod -Method Get ` -Uri ($baseUri + '/api/data/v9.2/businessunits') ` -Headers $headersWebApi 4> $null | ` Select-Object -ExpandProperty value $businessId = ($colBuRaw | Where-Object { $null -eq $_._parentbusinessunitid_value } | Select-Object -First 1).businessunitid if ([System.String]::IsNullOrEmpty($businessId)) { $businessId = ($colBuRaw | Select-Object -First 1).businessunitid } if ([System.String]::IsNullOrEmpty($businessId)) { $messageString = "No Business Unit was found in the Power Platform environment. Please verify the environment - <c='em'>https://aka.ms/ppac</c>" Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because no Business Unit was found." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } # Split display name into first / last name for the systemuser record. $firstName = $entraUser.displayName $lastName = "" $nameParts = ($entraUser.displayName -split '\s+', 2) if ($nameParts.Count -eq 2) { $firstName = $nameParts[0] $lastName = $nameParts[1] } $payloadUser = [PsCustomObject][ordered]@{ "firstname" = $firstName "lastname" = $lastName "domainname" = $entraUser.userPrincipalName "internalemailaddress" = $emailAddress "azureactivedirectoryobjectid" = $entraUser.id "businessunitid@odata.bind" = "/businessunits($businessId)" } | ConvertTo-Json -Depth 10 Invoke-RestMethod -Method Post ` -Uri ($baseUri + "/api/data/v9.2/systemusers") ` -Headers $headersWebApi ` -Body $payloadUser ` -StatusCodeVariable statusUser > $null 4> $null if (-not ($statusUser -like "2*")) { $messageString = "Failed to create the user: <c='em'>$emailAddress</c> in the Power Platform environment. HTTP status: <c='em'>$statusUser</c>. Please try creating the user manually via the Power Platform admin center - <c='em'>https://aka.ms/ppac</c>" Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because creating the user failed." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } $crmUser = Get-PpacUser ` -EnvironmentId $envObj.PpacEnvId ` -User $emailAddress | ` Select-Object -First 1 } if ($null -eq $crmUser) { $messageString = "The user: <c='em'>$emailAddress</c> was expected to exist in the Power Platform environment after creation, but was not found. Please investigate via the Power Platform admin center - <c='em'>https://aka.ms/ppac</c>" Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because user was NOT found after creation." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } # Dataverse creates users disabled. Role assignment fails on disabled users (0x80048d35), so enable first. $userDetails = Invoke-RestMethod -Method Get ` -Uri ($baseUri + "/api/data/v9.2/systemusers($($crmUser.PpacSystemUserId))?`$select=isdisabled") ` -Headers $headersWebApi 4> $null if ($userDetails.isdisabled -eq $true) { $payloadEnable = [PsCustomObject][ordered]@{ "isdisabled" = $false } | ConvertTo-Json -Depth 10 Invoke-RestMethod -Method Patch ` -Uri ($baseUri + "/api/data/v9.2/systemusers($($crmUser.PpacSystemUserId))") ` -Headers $headersWebApi ` -Body $payloadEnable ` -StatusCodeVariable statusEnable > $null 4> $null if (-not ($statusEnable -like "2*")) { $messageString = "The user: <c='em'>$emailAddress</c> is disabled and enabling it failed. HTTP status: <c='em'>$statusEnable</c>. Security roles cannot be assigned to disabled users." Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because enabling the user failed." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } } foreach ($roleName in $Role) { Add-PpacSecurityRoleMember ` -EnvironmentId $envObj.PpacEnvId ` -User $emailAddress ` -Role $roleName if (Test-PSFFunctionInterrupt) { return } } Get-PpacUser ` -EnvironmentId $envObj.PpacEnvId ` -User $emailAddress } end { } } |