Private/_Auth.ps1
function Create-PACAuth($ServerUrl) { if ($ServerUrl -eq $null) { $ServerUrl = $global:devops_ServerUrl } if ($global:devops_DataverseCredType -eq "servicePrincipal") { $pdel = & $env:APPDATA\Capgemini.PowerPlatform.DevOps\PACTools\tools\pac.exe auth delete --name ppdo # Added escaped quotes required to get secrets working that started with - (https://github.com/dylanhaskins/Capgemini.PowerPlatform.DevOps/issues/150) & $env:APPDATA\Capgemini.PowerPlatform.DevOps\PACTools\tools\pac.exe auth create --applicationId $global:devops_ClientID --clientSecret `"$global:clientSecret`" --environment $ServerUrl --tenant $global:devops_TenantID --name ppdo #--name $global:devops_SolutionName # Select ppdo connection to use (user may have more than one pac auth saved) & $env:APPDATA\Capgemini.PowerPlatform.DevOps\PACTools\tools\pac.exe auth select --name ppdo } if ($global:devops_DataverseCredType -eq "user") { $pdel = & $env:APPDATA\Capgemini.PowerPlatform.DevOps\PACTools\tools\pac.exe auth delete --name ppdo & $env:APPDATA\Capgemini.PowerPlatform.DevOps\PACTools\tools\pac.exe auth create --username $global:devops_DataverseEmail --password $global:Password --environment $ServerUrl --name ppdo #--name $global:devops_SolutionName # Select ppdo connection to use (user may have more than one pac auth saved) & $env:APPDATA\Capgemini.PowerPlatform.DevOps\PACTools\tools\pac.exe auth select --name ppdo } } function Get-AccessToken { #Get Access Token Write-Host "Getting Updated Access Token" $azureDevopsResourceId = "499b84ac-1321-427f-aa17-267ca6975798" $token = az account get-access-token --resource $azureDevopsResourceId | ConvertFrom-Json $authValue = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":" + $token.accessToken)) $env:AZURE_DEVOPS_EXT_PAT = $token.accessToken # $REPO_URL = git remote get-url origin # Write-Host "git URL is $REPO_URL" # git config http.$REPO_URL.extraHeader "Authorization:Basic $authValue" # Write-Host "Updated Auth Token" } function Get-AzureAccounts($reason) { try { $sel = $null $AZAccounts = az account list | ConvertFrom-Json [array]$AZoptions = "Login to a New Account" $AZoptions += $AZAccounts | ForEach-Object { "$($_.name) $($_.user.name) ($($_.tenantId))" } do { $sel = Invoke-Menu -MenuTitle "---- Please Select your Subscription ($reason) ------" -MenuOptions $AZoptions } until ($sel -ge 0) if ($sel -eq 0) { if ($global:devops_isDocker) { az login --use-device-code --allow-no-subscriptions } else { az config set core.allow_broker=true az login --allow-no-subscriptions } #Get Azure Accounts / Subscriptions $AZAccounts = az account list | ConvertFrom-Json Get-AzureAccounts($reason) } else { $global:devops_selectedSubscription = $AZAccounts[$sel - 1].id $global:devops_selectedSubscriptionName = $AZAccounts[$sel - 1].name az account set --subscription $global:devops_selectedSubscription } } catch { Write-Error $_ pause } } function Get-AzureLogins($reason) { [array]$options = "Login to a New Account" $AZCredentials = az account list --query '[].{Name:user.name, Type:user.type, ID:id, Subscription:name, TenantID:tenantId}' --output json | ConvertFrom-Json $UniqueCredentials = $AZCredentials | Sort-Object -Property * -Unique $options += $UniqueCredentials | ForEach-Object { "$($_.Name) ($($_.Type))($($_.Subscription))($($_.TenantID))" } do { $sel = Invoke-Menu -MenuTitle "---- Please Select your Subscription ($reason) ------" -MenuOptions $options } until ($sel -ge 0) if ($sel -eq 0) { if ($global:devops_isDocker) { az login --use-device-code --allow-no-subscriptions } else { az login --allow-no-subscriptions } Get-AzureLogins($reason) } else { $global:devops_DataverseEmail = $UniqueCredentials[$sel - 1].Name $global:devops_DataverseADSubscription = $UniqueCredentials[$sel - 1].id $global:devops_DataverseCredType = $UniqueCredentials[$sel - 1].Type $global:devops_HasDataverseLogin = $true } } function Get-DataverseLogin { Param( [bool] [Parameter(Mandatory = $false)] $overrideSP = $false, [bool] [Parameter(Mandatory = $false)] $requiresManagementApi = $true ) if (!$global:devops_HasDataverseLogin -or ($global:devops_projectFile.OverrideSPCheck -eq "True")) { $message = "Connecting to Dataverse Environment" Write-Host $message Write-Host "" [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 if (($global:devops_ClientID -and $overrideSP -eq $false) -and !($global:devops_projectFile.OverrideSPCheck -eq "True" -and $requiresManagementApi -eq $true)) { $global:devops_DataverseCredType = "servicePrincipal" } else { Get-AzureLogins("Power Platform") } if ($global:devops_DataverseCredType -eq "user") { [String]$userString = $global:devops_DataverseEmail Authenticate-PowerPlatform($userString, "https://management.azure.com/") Authenticate-PowerPlatform($userString, "https://service.powerapps.com/") } else { Write-Host "Using Service Principal" if ($global:devops_projectFile.ClientSecretAKVName) { Write-Host "Retrieving Secret from KeyVault" az account set --subscription $global:devops_projectFile.selectedSubscription $KVRetrieve = az keyvault secret show --name $global:devops_projectFile.ClientSecretAKVName --vault-name $global:devops_projectFile.AzureKeyVaultName | ConvertFrom-Json $global:clientSecret = $KVRetrieve.value Write-Host "Secret Retrieved" } else { if (!$global:clientSecret) { Write-Host "Retrieving Secret from Local Store" $clientKeySS = ($global:devops_configFile.Projects[$global:devops_projectConfigID].ClientSecret) | ConvertTo-SecureString $global:clientSecret = (New-Object PSCredential "user", $clientKeySS).GetNetworkCredential().Password Write-Host "Secret Retrieved" } else { Write-Host "Secret Provided by Command Line/Cache" } } try { Add-PowerAppsAccount -ApplicationId $global:devops_ClientID -ClientSecret "$global:clientSecret" -TenantID $global:devops_TenantID $ManagementApps = Get-PowerAppManagementApps $SPAdminPortalAccess = $ManagementApps.value | Where-Object applicationId -eq $global:devops_ClientID $SPAdminPortalAccess = $SPAdminPortalAccess.applicationId Write-Host "Service Principal Check Override : $($global:devops_projectFile.OverrideSPCheck)" if (!$SPAdminPortalAccess -and ($global:devops_projectFile.OverrideSPCheck -eq "False")) { $EnableAccess = Read-Host -Prompt "Service Principal $global:devops_ClientID does not have permissions to administer Power Platform, would you like to [e]nable access, login as another [u]ser or [c]ontinue without Power Platform admin (some actions will not be available)? [e/u/c]" if ($EnableAccess.ToLower() -eq 'e') { Write-Host "Please Login to Power Platform with a User who has access to Administer Environments" $global:currentSession.loggedIn = $false $tempClientID = $global:devops_ClientID $global:devops_ClientID = $null Get-DataverseLogin $global:devops_ClientID = $tempClientID $global:devops_DataverseCredType = "servicePrincipal" $SPAdminPortalAccess = New-PowerAppManagementApp -ApplicationId $global:devops_ClientID if ($SPAdminPortalAccess.StatusCode -eq "403") { Throw $SPAdminPortalAccess } $global:currentSession.loggedIn = $false Add-PowerAppsAccount -ApplicationId $global:devops_ClientID -ClientSecret "$global:clientSecret" -TenantID $global:devops_TenantID $global:devops_HasDataverseLogin = $true Write-Host "Connected to Dataverse" } elseif ($EnableAccess.ToLower() -eq 'u') { Get-DataverseLogin -overrideSP $true } elseif ($EnableAccess.ToLower() -eq 'c') { $global:devops_HasDataverseLogin = $true Write-Host "Connected to Dataverse" } else { Throw $SPAdminPortalAccess } } else { $global:devops_HasDataverseLogin = $true Write-Host "Connected to Dataverse" } } catch { $global:devops_HasDataverseLogin = $false Write-Host $_ pause } } } } function Authenticate-PowerPlatform() { [CmdletBinding()] Param( [Parameter(Mandatory = $True)]$args ) $theUser = $args[0] if ($args[1]) { $theAudience = $args[1] } else { $theAudience = "https://management.azure.com/" } $authBaseUri = switch ($Endpoint) { "usgovhigh" { "https://login.microsoftonline.us" } "dod" { "https://login.microsoftonline.us" } default { "https://login.windows.net" } }; [string]$Endpoint = "prod" [string]$Audience = $theAudience [string]$TenantID = $null [string]$CertificateThumbprint = $null [string]$ClientSecret = $null $getToken = az account get-access-token --resource $Audience -s $global:devops_DataVerseADSubscription if (!$getToken) { Write-Host "Re-Authenticating $theUser" if ($global:devops_isDocker) { az login --use-device-code --allow-no-subscriptions } else { az login --allow-no-subscriptions } Authenticate-PowerPlatform($theUser, $Audience) } $authResult = $getToken | ConvertFrom-Json $claims = Get-JwtTokenClaimsForPA -JwtToken $authResult.AccessToken $global:currentSession = @{ loggedIn = $true; idToken = $authResult.IdToken; upn = $claims.upn; tenantId = $claims.tid; userId = $claims.oid; applicationId = $claims.appid; certificateThumbprint = $CertificateThumbprint; clientSecret = $ClientSecret; secureClientSecret = $SecureClientSecret; refreshToken = $authResult.RefreshToken; expiresOn = (Get-Date).AddHours(8); resourceTokens = @{ $Audience = @{ accessToken = $authResult.AccessToken; expiresOn = $authResult.ExpiresOn; } }; selectedEnvironment = "~default"; authBaseUri = $authBaseUri; flowEndpoint = switch ($Endpoint) { "prod" { "api.flow.microsoft.com" } "usgov" { "gov.api.flow.microsoft.us" } "usgovhigh" { "high.api.flow.microsoft.us" } "dod" { "api.flow.appsplatform.us" } "china" { "api.powerautomate.cn" } "preview" { "preview.api.flow.microsoft.com" } "tip1" { "tip1.api.flow.microsoft.com" } "tip2" { "tip2.api.flow.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; powerAppsEndpoint = switch ($Endpoint) { "prod" { "api.powerapps.com" } "usgov" { "gov.api.powerapps.us" } "usgovhigh" { "high.api.powerapps.us" } "dod" { "api.apps.appsplatform.us" } "china" { "api.powerapps.cn" } "preview" { "preview.api.powerapps.com" } "tip1" { "tip1.api.powerapps.com" } "tip2" { "tip2.api.powerapps.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; bapEndpoint = switch ($Endpoint) { "prod" { "api.bap.microsoft.com" } "usgov" { "gov.api.bap.microsoft.us" } "usgovhigh" { "high.api.bap.microsoft.us" } "dod" { "api.bap.appsplatform.us" } "china" { "api.bap.partner.microsoftonline.cn" } "preview" { "preview.api.bap.microsoft.com" } "tip1" { "tip1.api.bap.microsoft.com" } "tip2" { "tip2.api.bap.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; graphEndpoint = switch ($Endpoint) { "prod" { "graph.windows.net" } "usgov" { "graph.windows.net" } "usgovhigh" { "graph.windows.net" } "dod" { "graph.windows.net" } "china" { "graph.windows.net" } "preview" { "graph.windows.net" } "tip1" { "graph.windows.net" } "tip2" { "graph.windows.net" } default { throw "Unsupported endpoint '$Endpoint'" } }; cdsOneEndpoint = switch ($Endpoint) { "prod" { "api.cds.microsoft.com" } "usgov" { "gov.api.cds.microsoft.us" } "usgovhigh" { "high.api.cds.microsoft.us" } "dod" { "dod.gov.api.cds.microsoft.us" } "preview" { "preview.api.cds.microsoft.com" } "tip1" { "tip1.api.cds.microsoft.com" } "tip2" { "tip2.api.cds.microsoft.com" } default { throw "Unsupported endpoint '$Endpoint'" } }; }; } function Get-JwtTokenClaimsForPA { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$JwtToken ) $tokenSplit = $JwtToken.Split(".") $claimsSegment = $tokenSplit[1].Replace(" ", "+").Replace("-", "+"); $mod = $claimsSegment.Length % 4 if ($mod -gt 0) { $paddingCount = 4 - $mod; for ($i = 0; $i -lt $paddingCount; $i++) { $claimsSegment += "=" } } $decodedClaimsSegment = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($claimsSegment)) return ConvertFrom-Json $decodedClaimsSegment } function Get-DataverseConnection { Param( [string] [Parameter(Mandatory = $true)] $DeployServerUrl ) try { Get-DataverseLogin -requiresManagementApi $false if ($global:devops_DataverseCredType -eq "user") { try { if ($global:devops_isDocker) { Write-Host "Warning: It is recommended you configure and use a Service Principal instead of Username and Password (to prevent MFA related issues)" -ForegroundColor Yellow Write-Host "" $SecurePassword = Read-Host "Enter Password for $global:devops_DataverseEmail" -AsSecureString $global:Password = (New-Object PSCredential "user", $SecurePassword).GetNetworkCredential().Password [string]$CrmConnectionString = "AuthType=OAuth;Username=$global:devops_DataverseEmail;Password=$global:Password;Url=$DeployServerUrl;AppId=51f81489-12ee-4a9e-aaae-a2591f45987d;RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97;TokenCacheStorePath=$env:APPDATA\Capgemini.PowerPlatform.DevOps\dataverse_cache.data;LoginPrompt=Never" } else { [string]$CrmConnectionString = "AuthType=OAuth;Username=$global:devops_DataverseEmail;Password=$global:Password;Url=$DeployServerUrl;AppId=51f81489-12ee-4a9e-aaae-a2591f45987d;RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97;TokenCacheStorePath=$env:APPDATA\Capgemini.PowerPlatform.DevOps\dataverse_cache.data;LoginPrompt=Auto" } $conn = Connect-CrmOnline -ConnectionString $CrmConnectionString -ConnectionTimeoutInSeconds 600 return $conn } catch { Write-Host $_ pause } } elseif ($global:devops_DataverseCredType -eq "servicePrincipal") { try { [string]$CrmConnectionString = "AuthType=ClientSecret;Url=$DeployServerUrl;ClientId=$global:devops_ClientID;ClientSecret='$global:clientSecret'" $conn = Connect-CrmOnline -ConnectionString $CrmConnectionString -ConnectionTimeoutInSeconds 600 if (!$conn.IsReady -and $conn.LastCrmError.Contains("Invalid Login")) { $AddSPAsSysAdmin = Read-Host -Prompt "The Service Principal $global:devops_ClientID does not have access to $DeployServerUrl, would you like to add access now ? [y/n]" if ($AddSPAsSysAdmin.ToLower() -eq "y") { try { $global:currentSession.loggedIn = $false Add-D365ApplicationUser -d365ResourceName $DeployServerUrl -servicePrincipal $global:devops_ClientID -roleNames "System Administrator" } catch { Write-Host $_ pause } #add CLI auth create $conn = Connect-CrmOnline -ConnectionString $CrmConnectionString -ConnectionTimeoutInSeconds 600 } } return $conn } catch { Write-Host $_ pause } } else { try { $conn = Connect-CrmOnline -ServerUrl $DeployServerUrl -ConnectionTimeoutInSeconds 600 -ForceOAuth return $conn } catch { Write-Host $_ pause } } } catch { Write-Host $_ pause } } |