MSCloudLoginAssistant.psm1
function Connect-M365Tenant { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateSet('ExchangeOnline', ` 'SecurityComplianceCenter', 'PnP', 'PowerPlatforms', ` 'MicrosoftTeams', 'MicrosoftGraph', 'Tasks')] [System.String] $Workload, [Parameter()] [System.String] $Url, [Parameter()] [Alias('o365Credential')] [System.Management.Automation.PSCredential] $Credential, [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.String] $ApplicationSecret, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [Switch] $UseModernAuth, [Parameter()] [SecureString] $CertificatePassword, [Parameter()] [System.String] $CertificatePath, [Parameter()] [System.Boolean] $SkipModuleReload = $false, [Parameter()] [Switch] $Identity, [Parameter()] [SecureString[]] $AccessTokens ) $VerbosePreference = 'SilentlyContinue' $workloadInternalName = $Workload if ($Workload -eq 'MicrosoftTeams') { $workloadInternalName = 'Teams' } elseif ($Workload -eq 'PowerPlatforms') { $workloadInternalName = 'PowerPlatform' } if ($null -eq $Global:MSCloudLoginConnectionProfile) { $Global:MSCloudLoginConnectionProfile = New-Object MSCloudLoginConnectionProfile } # Only validate the parameters if we are not already connected elseif ( $Global:MSCloudLoginConnectionProfile.$workloadInternalName.Connected ` -and (Compare-InputParametersForChange -CurrentParamSet $PSBoundParameters)) { Write-Verbose -Message 'Resetting connection profile' $Global:MSCloudLoginConnectionProfile.$workloadInternalName.Connected = $false } Write-Verbose -Message "Trying to connect to platform {$Workload}" switch ($Workload) { 'ExchangeOnline' { $Global:MSCloudLoginConnectionProfile.ExchangeOnline.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.ExchangeOnline.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.ExchangeOnline.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.ExchangeOnline.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.ExchangeOnline.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.ExchangeOnline.SkipModuleReload = $SkipModuleReload $Global:MSCloudLoginConnectionProfile.ExchangeOnline.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.ExchangeOnline.Identity = $Identity $Global:MSCloudLoginConnectionProfile.ExchangeOnline.Connect() } 'MicrosoftGraph' { $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.Identity = $Identity $Global:MSCloudLoginConnectionProfile.MicrosoftGraph.Connect() } 'MicrosoftTeams' { $Global:MSCloudLoginConnectionProfile.Teams.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.Teams.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.Teams.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.Teams.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.Teams.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.Teams.CertificatePath = $CertificatePath $Global:MSCloudLoginConnectionProfile.Teams.CertificatePassword = $CertificatePassword $Global:MSCloudLoginConnectionProfile.Teams.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.Teams.Identity = $Identity $Global:MSCloudLoginConnectionProfile.Teams.Connect() } 'PnP' { $Global:MSCloudLoginConnectionProfile.PnP.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.PnP.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.PnP.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.PnP.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.PnP.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.PnP.CertificatePath = $CertificatePath $Global:MSCloudLoginConnectionProfile.PnP.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.PnP.Identity = $Identity $Global:MSCloudLoginConnectionProfile.PnP.CertificatePassword = $CertificatePassword # Mark as disconnected if we are trying to connect to a different url then we previously connected to. if ($Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl -ne $Url -or ` -not $Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl -and ` $Url -or (-not $Url -and -not $Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl)) { $ForceRefresh = $false if ($Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl -ne $Url -and ` -not [System.String]::IsNullOrEmpty($url)) { $ForceRefresh = $true } $Global:MSCloudLoginConnectionProfile.PnP.Connected = $false $Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl = $Url $Global:MSCloudLoginConnectionProfile.PnP.Connect($ForceRefresh) } else { try { $contextUrl = (Get-PnPContext).Url if ([System.String]::IsNullOrEmpty($url)) { $Url = $Global:MSCloudLoginConnectionProfile.PnP.AdminUrl if (-not $Url.EndsWith('/') -and $contextUrl.EndsWith('/')) { $Url += '/' } } if ($contextUrl -ne $Url) { $ForceRefresh = $true $Global:MSCloudLoginConnectionProfile.PnP.Connected = $false if ($url) { $Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl = $Url } else { $Global:MSCloudLoginConnectionProfile.PnP.ConnectionUrl = $Global:MSCloudLoginConnectionProfile.PnP.AdminUrl } $Global:MSCloudLoginConnectionProfile.PnP.Connect($ForceRefresh) } } catch { Write-Information -MessageData "Couldn't acquire PnP Context" } } # If the AdminUrl is empty and a URL was provided, assume that the url # provided is the admin center; if (-not $Global:MSCloudLoginConnectionProfile.PnP.AdminUrl -and $Url) { $Global:MSCloudLoginConnectionProfile.PnP.AdminUrl = $Url } } 'PowerPlatforms' { $Global:MSCloudLoginConnectionProfile.PowerPlatform.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.PowerPlatform.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.PowerPlatform.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.PowerPlatform.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.PowerPlatform.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.PowerPlatform.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.PowerPlatform.Connect() } 'SecurityComplianceCenter' { $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CertificatePath = $CertificatePath $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.CertificatePassword = $CertificatePassword $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.SkipModuleReload = $SkipModuleReload $Global:MSCloudLoginConnectionProfile.SecurityComplianceCenter.Connect() } 'Tasks' { $Global:MSCloudLoginConnectionProfile.Tasks.Credentials = $Credential $Global:MSCloudLoginConnectionProfile.Tasks.ApplicationId = $ApplicationId $Global:MSCloudLoginConnectionProfile.Tasks.ApplicationSecret = $ApplicationSecret $Global:MSCloudLoginConnectionProfile.Tasks.TenantId = $TenantId $Global:MSCloudLoginConnectionProfile.Tasks.CertificateThumbprint = $CertificateThumbprint $Global:MSCloudLoginConnectionProfile.Tasks.CertificatePath = $CertificatePath $Global:MSCloudLoginConnectionProfile.Tasks.CertificatePassword = $CertificatePassword $Global:MSCloudLoginConnectionProfile.Tasks.AccessTokens = $AccessTokens $Global:MSCloudLoginConnectionProfile.Tasks.Connect() } } } <# .SYNOPSIS This functions compares the authentication parameters for a change compared to the currently used parameters. .DESCRIPTION This functions compares the authentication parameters for a change compared to the currently used parameters. It is used to determine if a new connection needs to be made. .OUTPUTS Boolean. Compare-InputParametersForChange returns $true if something changed, $false otherwise. .EXAMPLE Compare-InputParametersForChange -CurrentParamSet $PSBoundParameters #> function Compare-InputParametersForChange { param ( [Parameter()] [System.Collections.Hashtable] $CurrentParamSet ) $currentParameters = $currentParamSet if ($null -ne $currentParameters['Credential'].UserName) { $currentParameters.Add('UserName', $currentParameters['Credential'].UserName) } $currentParameters.Remove('Credential') | Out-Null $currentParameters.Remove('SkipModuleReload') | Out-Null $currentParameters.Remove('UseModernAuth') | Out-Null $currentParameters.Remove('ProfileName') | Out-Null $currentParameters.Remove('Verbose') | Out-Null $globalParameters = @{} $workloadProfile = $Global:MSCloudLoginConnectionProfile if ($null -eq $workloadProfile) { # No Workload profile yet, so we need to connect # This should not happen, but just in case # We are not able to detect a change, so we return $false return $false } else { $workload = $currentParameters['Workload'] if ($Workload -eq 'MicrosoftTeams') { $workloadInternalName = 'Teams' } elseif ($Workload -eq 'PowerPlatforms') { $workloadInternalName = 'PowerPlatform' } else { $workloadInternalName = $workload } $workloadProfile = $Global:MSCloudLoginConnectionProfile.$workloadInternalName } # Clean the global Params if (-not [System.String]::IsNullOrEmpty($workloadProfile.TenantId)) { $globalParameters.Add('TenantId', $workloadProfile.TenantId) } if (-not [System.String]::IsNullOrEmpty($workloadProfile.Credentials.UserName)) { $globalParameters.Add('UserName', $workloadProfile.Credentials.UserName) # If the tenant id is part of the username, we need to remove it from the global parameters if ($workloadInternalName -eq 'MicrosoftGraph' ` -and $globalParameters.ContainsKey('TenantId') ` -and $globalParameters.TenantId -eq $workloadProfile.Credentials.UserName.Split('@')[1]) { $globalParameters.Remove('TenantId') | Out-Null } } if ($workloadInternalName -eq 'PNP' -and $currentParameters.ContainsKey("Url") -and ` -not [System.String]::IsNullOrEmpty($currentParameters.Url)) { $globalParameters.Add('Url', $workloadProfile.ConnectionUrl) } # This is the global graph application id. If it is something different, it means that we should compare the parameters if (-not [System.String]::IsNullOrEmpty($workloadProfile.ApplicationId) ` -and -not($workloadInternalName -eq 'MicrosoftGraph' -and $workloadProfile.ApplicationId -eq '14d82eec-204b-4c2f-b7e8-296a70dab67e')) { $globalParameters.Add('ApplicationId', $workloadProfile.ApplicationId) } if (-not [System.String]::IsNullOrEmpty($workloadProfile.ApplicationSecret)) { $globalParameters.Add('ApplicationSecret', $workloadProfile.ApplicationSecret) } if (-not [System.String]::IsNullOrEmpty($workloadProfile.CertificateThumbprint)) { $globalParameters.Add('CertificateThumbprint', $workloadProfile.CertificateThumbprint) } if (-not [System.String]::IsNullOrEmpty($workloadProfile.CertificatePassword)) { $globalParameters.Add('CertificatePassword', $workloadProfile.CertificatePassword) } if (-not [System.String]::IsNullOrEmpty($workloadProfile.CertificatePath)) { $globalParameters.Add('CertificatePath', $workloadProfile.CertificatePath) } if ($workloadProfile.Identity) { $globalParameters.Add('Identity', $workloadProfile.Identity) } # Clean the current parameters # Remove the workload, as we don't need to compare that $currentParameters.Remove('Workload') | Out-Null if ([System.String]::IsNullOrEmpty($currentParameters.ApplicationId)) { $currentParameters.Remove('ApplicationId') | Out-Null } if ([System.String]::IsNullOrEmpty($currentParameters.TenantId)) { $currentParameters.Remove('TenantId') | Out-Null } if ([System.String]::IsNullOrEmpty($currentParameters.ApplicationSecret)) { $currentParameters.Remove('ApplicationSecret') | Out-Null } if ([System.String]::IsNullOrEmpty($currentParameters.CertificateThumbprint)) { $currentParameters.Remove('CertificateThumbprint') | Out-Null } if ([System.String]::IsNullOrEmpty($currentParameters.CertificatePassword)) { $currentParameters.Remove('CertificatePassword') | Out-Null } if ([System.String]::IsNullOrEmpty($currentParameters.CertificatePath)) { $currentParameters.Remove('CertificatePath') | Out-Null } if ($currentParameters.ContainsKey('Identity') -and -not ($currentParameters.Identity)) { $currentParameters.Remove('Identity') | Out-Null } if ($null -ne $globalParameters) { $diffKeys = Compare-Object -ReferenceObject @($currentParameters.Keys) -DifferenceObject @($globalParameters.Keys) -PassThru $diffValues = Compare-Object -ReferenceObject @($currentParameters.Values) -DifferenceObject @($globalParameters.Values) -PassThru } if ($null -eq $diffKeys -and $null -eq $diffValues) { # no differences were found return $false } # We found differences, so we need to connect return $true } function Get-SPOAdminUrl { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter()] [System.Management.Automation.PSCredential] $Credential ) Write-Verbose -Message 'Connection to Microsoft Graph is required to automatically determine SharePoint Online admin URL...' try { $result = Invoke-MgGraphRequest -Uri /v1.0/sites/root -ErrorAction SilentlyContinue $weburl = $result.webUrl if (-not $weburl) { Connect-M365Tenant -Workload 'MicrosoftGraph' -Credential $Credential $weburl = (Invoke-MgGraphRequest -Uri /v1.0/sites/root).webUrl } } catch { Connect-M365Tenant -Workload 'MicrosoftGraph' -Credential $Credential try { $weburl = (Invoke-MgGraphRequest -Uri /v1.0/sites/root).webUrl } catch { if (Assert-IsNonInteractiveShell -eq $false) { # Only run interactive command when Exporting Write-Verbose -Message 'Requesting access to read information about the domain' Connect-MgGraph -Scopes Sites.Read.All -ErrorAction 'Stop' $weburl = (Invoke-MgGraphRequest -Uri /v1.0/sites/root).webUrl } else { if ($_.Exception.Message -eq 'Insufficient privileges to complete the operation.') { throw "The Graph application does not have the correct permissions to access Domains. Make sure you run 'Connect-MgGraph -Scopes Sites.Read.All' first!" } } } } if ($null -eq $weburl) { throw 'Unable to retrieve SPO Admin URL. Please check connectivity and if you have the Sites.Read.All permission.' } $spoAdminUrl = $webUrl -replace '^https:\/\/(\w*)\.', 'https://$1-admin.' Write-Verbose -Message "SharePoint Online admin URL is $spoAdminUrl" return $spoAdminUrl } function Get-AzureADDLL { [CmdletBinding()] [OutputType([System.String])] param( ) [array]$AzureADModules = Get-Module -ListAvailable | Where-Object { $_.name -eq 'AzureADPreview' } if ($AzureADModules.count -eq 0) { Throw "Can't find Azure AD DLL. Install the module manually 'Install-Module AzureADPreview'" } else { $AzureDLL = Join-Path (($AzureADModules | Sort-Object version -Descending | Select-Object -First 1).Path | Split-Path) Microsoft.IdentityModel.Clients.ActiveDirectory.dll return $AzureDLL } } function Get-TenantLoginEndPoint { [CmdletBinding()] [OutputType([System.String])] Param( [Parameter(Mandatory = $True)] [System.String] $TenantName, [Parameter(Mandatory = $false)] [System.String] [ValidateSet('MicrosoftOnline', 'EvoSTS')] $LoginSource = 'EvoSTS' ) $TenantInfo = @{ } if ($LoginSource -eq 'EvoSTS') { $webrequest = Invoke-WebRequest -Uri https://login.windows.net/$($TenantName)/.well-known/openid-configuration -UseBasicParsing } else { $webrequest = Invoke-WebRequest -Uri https://login.microsoftonline.com/$($TenantName)/.well-known/openid-configuration -UseBasicParsing } if ($webrequest.StatusCode -eq 200) { $TenantInfo = $webrequest.Content | ConvertFrom-Json } return $TenantInfo } function New-ADALServiceInfo { [CmdletBinding()] [OutputType([System.Collections.HashTable])] Param( [Parameter(Mandatory = $True)] [System.String] $TenantName, [Parameter(Mandatory = $True)] [System.String] $UserPrincipalName, [Parameter(Mandatory = $false)] [System.String] [ValidateSet('MicrosoftOnline', 'EvoSTS')] $LoginSource = 'EvoSTS' ) $AzureADDLL = Get-AzureADDLL if ([string]::IsNullOrEmpty($AzureADDLL)) { Throw "Can't find Azure AD DLL" Exit } else { Write-Verbose -Message "AzureADDLL: $AzureADDLL" $tMod = [System.Reflection.Assembly]::LoadFrom($AzureADDLL) } $TenantInfo = Get-TenantLoginEndPoint -TenantName $TenantName if ([string]::IsNullOrEmpty($TenantInfo)) { Throw "Can't find Tenant Login Endpoint" Exit } else { [string] $authority = $TenantInfo.authorization_endpoint } $PromptBehavior = [Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Auto $Service = @{ } $Service['authContext'] = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($authority, $false) $Service['platformParam'] = New-Object 'Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters' -ArgumentList $PromptBehavior $Service['userId'] = New-Object 'Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier' -ArgumentList $UserPrincipalName, 'OptionalDisplayableId' Write-Verbose -Message "Current Assembly for AD AuthenticationContext: $([Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext].Assembly | Out-String)" return $Service } function Get-AuthHeader { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] [System.String] $UserPrincipalName, [Parameter(Mandatory = $True)] [Alias('RessourceURI')] # For backward compat with anything using the misspelled parameter $ResourceURI, [Parameter(Mandatory = $True)] $clientId, [Parameter(Mandatory = $True)] [System.String] $RedirectURI ) if ($null -eq $Global:ADALServicePoint) { $TenantName = $UserPrincipalName.split('@')[1] $Global:ADALServicePoint = New-ADALServiceInfo -TenantName $TenantName -UserPrincipalName $UserPrincipalName } try { Write-Debug 'Looking for a refresh token' $authResult = $Global:ADALServicePoint.authContext.AcquireTokenSilentAsync($ResourceURI, $clientId) if ($null -eq $authResult.result) { $RedirectURI = [System.Uri]::new($RedirectURI) $authResult = $Global:ADALServicePoint.authContext.AcquireTokenAsync($ResourceURI, $clientId, $RedirectURI, $Global:ADALServicePoint.platformParam, $Global:ADALServicePoint.userId, '', '') } $AuthHeader = $authResult.result.CreateAuthorizationHeader() } catch { Throw "Can't create Authorization header: $_" } Return $AuthHeader } function Get-AccessToken { [CmdletBinding()] Param( [Parameter(Mandatory = $True)] $TargetUri, [Parameter(Mandatory = $True)] $AuthUri, [Parameter(Mandatory = $True)] $ClientId, [Parameter(Mandatory = $False)] [System.Management.Automation.PSCredential] $Credentials ) try { Write-Verbose "There was no existing Access Token for $ClientId. Requesting a new one from $TargetUri" $AzureADDLL = Get-AzureADDLL if ([string]::IsNullOrEmpty($AzureADDLL)) { throw "Can't find Azure AD DLL" } [System.Reflection.Assembly]::LoadFrom($AzureADDLL) | Out-Null $context = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($AuthUri, $false, [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared) Write-Verbose -Message "AuthURI = $AuthURI" Write-Verbose -Message "TargetURI = $TargetUri" Write-Verbose -Message "ClientID = $ClientID" Write-Verbose -Message "Content = $context" $authResult = $context.AcquireTokenSilentAsync($TargetUri, $ClientId) $AccessToken = $authResult.result.AccessToken if ([System.String]::IsNullOrEmpty($AccessToken)) { $jobName = 'AcquireTokenAsync' + (New-Guid).ToString() Start-Job -Name $jobName -ScriptBlock { Param( [Parameter(Mandatory = $True)] $TargetUri, [Parameter(Mandatory = $True)] $AuthUri, [Parameter(Mandatory = $True)] $ClientId, [Parameter(Mandatory = $False)] [System.Management.Automation.PSCredential] $Credentials, [Parameter(Mandatory = $true)] [System.String] $AzureADDLL ) try { [System.Reflection.Assembly]::LoadFrom($AzureADDLL) | Out-Null $UserPasswordCreds = [Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential]::new($Credentials.UserName, $Credentials.Password) $context = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($AuthUri, $false, [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared) $authResult = $context.AcquireTokenSilentAsync($TargetUri, $ClientId) if ($null -eq $authResult.result) { $authResult = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($context, $targetUri, $ClientId, $UserPasswordCreds) } $token = $authResult.result.AccessToken return $token } catch { Write-Verbose -Message "Error {Get-AccessToken}: $_" return $null } } -ArgumentList @($targetUri, $AuthUri, $ClientId, $Credentials, $AzureADDLL) | Out-Null $job = Get-Job | Where-Object -FilterScript { $_.Name -eq $jobName } do { Start-Sleep -Seconds 1 } while ($job.JobStateInfo.State -ne 'Completed') $AccessToken = Receive-Job -Name $jobName } Write-Verbose "Token Found --> $AccessToken" return $AccessToken } catch { Write-Verbose $_ throw $_ } } function Get-PowerPlatformTokenInfo { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [System.String] $Audience, [Parameter()] [System.Management.Automation.PSCredential] $Credentials ) $jobName = 'AcquireTokenAsync' + (New-Guid).ToString() Start-Job -Name $jobName -ScriptBlock { Param( [Parameter(Mandatory = $true)] [System.Management.Automation.PSCredential] $O365Credentials, [Parameter(Mandatory = $true)] [System.String] $Audience ) try { $WarningPreference = 'SilentlyContinue' Import-Module -Name 'Microsoft.PowerApps.Administration.PowerShell' -Force $authContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext('https://login.windows.net/common') $credential = [Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential]::new($O365Credentials.Username, $O365Credentials.Password) $authResult = $authContext.AcquireToken($Audience, '1950a258-227b-4e31-a9cf-717495945fc2', $credential) $JwtToken = $authResult.IdToken $tokenSplit = $JwtToken.Split('.') $claimsSegment = $tokenSplit[1].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)) $claims = ConvertFrom-Json $decodedClaimsSegment } catch { $_ | Out-File "$env:temp\MSCloudLoginAssistant_Error.txt" } return @{ JwtToken = $JwtToken Claims = $claims RefreshToken = $authResult.RefreshToken AccessToken = $authResult.AccessToken ExpiresOn = $authResult.ExpiresOn } } -ArgumentList @($Credentials, $Audience) | Out-Null $job = Get-Job | Where-Object -FilterScript { $_.Name -eq $jobName } do { Start-Sleep -Seconds 1 } while ($job.JobStateInfo.State -ne 'Completed') $TokenInfo = Receive-Job -Name $jobName return $TokenInfo } function Test-MSCloudLoginCommand { [CmdletBinding()] [OutputType([System.Boolean])] Param( [Parameter(Mandatory = $true)] [System.String] $Command ) try { $testResult = Invoke-Command $Command return $true } catch { return $false } } function Get-CloudEnvironmentInfo { [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter()] [System.Management.Automation.PSCredential] $Credentials, [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.String] $ApplicationSecret, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [switch] $Identity ) try { if ($null -ne $Credentials) { $tenantName = $Credentials.UserName.Split('@')[1] } elseif (-not [string]::IsNullOrEmpty($TenantId)) { $tenantName = $TenantId } elseif ($Identity.IsPresent) { return } else { throw 'TenantId or Credentials must be provided' } ## endpoint will work with TenantId or tenantName $response = Invoke-WebRequest -Uri "https://login.microsoftonline.com/$tenantName/v2.0/.well-known/openid-configuration" -Method Get -UseBasicParsing $content = $response.Content $result = ConvertFrom-Json $content return $result } catch { throw $_ } } function Get-MSCloudLoginTenantDomain { param( [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [switch] $Identity, [Parameter()] [SecureString[]] $AccessTokens ) if (-not [string]::IsNullOrEmpty($ApplicationId)) { Connect-M365Tenant -Workload MicrosoftGraph ` -ApplicationId $ApplicationId ` -TenantId $TenantId ` -CertificateThumbprint $CertificateThumbprint } elseif ($Identity.IsPresent) { Connect-M365Tenant -Workload MicrosoftGraph ` -Identity ` -TenantId $TenantId } elseif ($null -ne $AccessTokens) { Connect-M365Tenant -Workload MicrosoftGraph ` -AccessTokens $AccessTokens } try { $domain = Get-MgDomain | Where-Object { $_.IsInitial -eq $True } } catch { $domain = Get-MgBetaDomain | Where-Object { $_.IsInitial -eq $True } } if ($null -ne $domain) { return $domain.Id.split('.')[0] } } function Get-MSCloudLoginOrganizationName { param( [Parameter()] [System.String] $ApplicationId, [Parameter()] [System.String] $TenantId, [Parameter()] [System.String] $CertificateThumbprint, [Parameter()] [System.String] $ApplicationSecret, [Parameter()] [switch] $Identity, [Parameter()] [SecureString[]] $AccessTokens ) try { if (-not [string]::IsNullOrEmpty($ApplicationId) -and -not [System.String]::IsNullOrEmpty($CertificateThumbprint)) { Connect-M365Tenant -Workload MicrosoftGraph -ApplicationId $ApplicationId -TenantId $TenantId -CertificateThumbprint $CertificateThumbprint } elseif (-not [string]::IsNullOrEmpty($ApplicationId) -and -not [System.String]::IsNullOrEmpty($ApplicationSecret)) { Connect-M365Tenant -Workload MicrosoftGraph -ApplicationId $ApplicationId -TenantId $TenantId -ApplicationSecret $ApplicationSecret } elseif ($Identity.IsPresent) { Connect-M365Tenant -Workload MicrosoftGraph -Identity -TenantId $TenantId } elseif ($null -ne $AccessTokens) { Connect-M365Tenant -Workload MicrosoftGraph -AccessTokens $AccessTokens } $domain = Get-MgDomain -ErrorAction Stop | Where-Object { $_.IsInitial -eq $True } if ($null -ne $domain) { return $domain.Id } } catch { Write-Verbose -Message "Couldn't get domain. Using TenantId instead" return $TenantId } } function Assert-IsNonInteractiveShell { # Test each Arg for match of abbreviated '-NonInteractive' command. $NonInteractive = [Environment]::GetCommandLineArgs() | Where-Object { $_ -like '-NonI*' } if ([Environment]::UserInteractive -and -not $NonInteractive) { # We are in an interactive shell. return $false } return $true } |