AzureServicePrincipalAccount.psm1
# .EXTERNALHELP AzureServicePrincipalAccount.psm1-Help.xml Function Add-AzServicePrincipalAccount { [OutputType('PSAzureProfile')] [CmdletBinding()] Param( [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $true, HelpMessage = "Please specify the Azure Automation 'AzureServicePrincipal' or 'Key Based AzureServicePrincipal' connection object")] [Object]$AzureServicePrincipalConnection, [Parameter(ParameterSetName = 'BySPKey', Mandatory = $true, HelpMessage = 'Please specify the Azure AD Application ID')] [Parameter(ParameterSetName = 'BySPCert', Mandatory = $true, HelpMessage = 'Please specify the Azure AD Application ID')] [Alias('AppId')] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [string]$ApplicationId, [Parameter(ParameterSetName = 'BySPKey', Mandatory = $false, HelpMessage = 'Please specify the Azure AD tenant ID')] [Parameter(ParameterSetName = 'BySPCert', Mandatory = $false, HelpMessage = 'Please specify the Azure AD tenant ID')] [Alias('Tenant')] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [string]$TenantId, [Parameter(ParameterSetName = 'BySPKey', Mandatory = $false, HelpMessage = 'Please specify the Azure subscription ID')] [Parameter(ParameterSetName = 'BySPCert', Mandatory = $false, HelpMessage = 'Please specify the Azure subscription ID')] [Alias('Subscription')] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [string]$SubscriptionId, [Parameter(ParameterSetName = 'BySPKey', Mandatory = $false, HelpMessage = 'Please specify the Azure environment')] [Parameter(ParameterSetName = 'BySPCert', Mandatory = $false, HelpMessage = 'Please specify the Azure environment')] [Alias('env')] [ValidateNotNullOrEmpty()] [string]$Environment = 'AzureCloud', [Parameter(ParameterSetName = 'BySPKey', Mandatory = $true, HelpMessage = 'Please specify the Azure AD Application Service Principal Key')] [Alias('Password')] [ValidateNotNullOrEmpty()] [SecureString]$ServicePrincipalKey, [Parameter(ParameterSetName = 'BySPCert', Mandatory = $true, HelpMessage = 'Please specify the Azure AD Application Service Principal certificate thumbprint')] [Alias('Thumbprint')] [ValidateNotNullOrEmpty()] [string]$CertThumbprint ) #Determine connection type If ($PSCmdlet.ParameterSetName -eq 'BySPConnection') { Write-Verbose "A connection object is specified. Determining the connection type..." $bvalidConnectionObject = $false if ($AzureServicePrincipalConnection.ContainsKey('Applicationid') -and $AzureServicePrincipalConnection.ContainsKey('TenantId') -and $AzureServicePrincipalConnection.ContainsKey('SubscriptionId')) { if ($AzureServicePrincipalConnection.ContainsKey('ServicePrincipalKey')) { $ConnectionType = "ByKey" $Applicationid = $AzureServicePrincipalConnection.ApplicationId $SPKey = $AzureServicePrincipalConnection.ServicePrincipalKey if ($SPkey -is [string]) { #Convert it to securestring $ServicePrincipalKey = New-Object System.Security.SecureString For ($i = 0; $i -lt $SPkey.length; $i++) { $char = $SPkey.Substring($i, 1) $ServicePrincipalKey.AppendChar($char) } } else { $ServicePrincipalKey = $SPKey } $TenantId = $AzureServicePrincipalConnection.TenantId $SubscriptionId = $AzureServicePrincipalConnection.SubscriptionId $bvalidConnectionObject = $true } elseif ($AzureServicePrincipalConnection.ContainsKey('CertificateThumbprint')) { $ConnectionType = "ByCert" $Applicationid = $AzureServicePrincipalConnection.ApplicationId $CertThumbprint = $AzureServicePrincipalConnection.CertificateThumbprint $TenantId = $AzureServicePrincipalConnection.TenantId $SubscriptionId = $AzureServicePrincipalConnection.SubscriptionId $bvalidConnectionObject = $true } } if (!$bvalidConnectionObject) { Write-Error "The connection object is invalid. please ensure the connection object type is either 'AzureServicePrincipal' or 'AzureServicePrincipal-KeyBased'." Exit -1 } } elseif ($PSCmdlet.ParameterSetName -eq 'BySPKey') { $ConnectionType = "ByKey" } else { $ConnectionType = "ByCert" } #Login to Azure If ($ConnectionType -eq 'ByKey') { Write-Verbose "Login using an Azure AD service principal with key (password)" $Cred = New-Object System.Management.Automation.PSCredential($ApplicationId, $ServicePrincipalKey) $Login = Connect-AzAccount -ServicePrincipal -Credential $Cred -SubscriptionId $SubscriptionId -TenantId $TenantId -Environment $Environment } else { Write-Verbose "Login using an Azure AD service principal with certificate" $Login = Connect-AzAccount -ServicePrincipal -CertificateThumbprint $CertThumbprint -ApplicationId $ApplicationId -TenantId $TenantId -SubscriptionId $SubscriptionId -Environment $Environment } $Login } # .EXTERNALHELP AzureServicePrincipalAccount.psm1-Help.xml Function Get-AzureADToken { [CmdletBinding()] [OutputType([string])] PARAM ( [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $true)] [Alias('Con', 'Connection')] [Object]$AzureServicePrincipalConnection, [Parameter(ParameterSetName = 'ByCred', Mandatory = $true)] [Parameter(ParameterSetName = 'UserInteractive', Mandatory = $true)] [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [Alias('tID')] [String]$TenantID, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [String]$ApplicationId, [Parameter(ParameterSetName = 'ByCred', Mandatory = $true, HelpMessage = 'Please specify the Azure AD credential')] [Alias('cred')] [ValidateNotNullOrEmpty()] [PSCredential]$Credential, [Parameter(ParameterSetName = 'UserInteractive', Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$UserName, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true, HelpMessage = 'Please specify the pfx Certificate file path')] [ValidateScript( { test-path $_ })] [string]$CertFilePath, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true, HelpMessage = 'Please specify the pfx Certificate file password')] [ValidateNotNullOrEmpty()] [SecureString]$CertFilePassword, [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $true, HelpMessage = "Please specify the Thumbprint of the certificate located in 'Cert:\LocalMachine\My' cert store")] [ValidateScript( { test-path "Cert:\LocalMachine\My\$_" })] [string]$CertThumbprint, [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCred', Mandatory = $false)] [Parameter(ParameterSetName = 'UserInteractive', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$OAuthURI, [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCred', Mandatory = $false)] [Parameter(ParameterSetName = 'UserInteractive', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$ResourceURI = 'https://management.azure.com/' ) #URI to get oAuth Access Token If ($PSCmdlet.ParameterSetName -eq 'BySPConnection') { $TenantId = $AzureServicePrincipalConnection.TenantId } If (!$PSBoundParameters.ContainsKey('oAuthURI')) { $oAuthURI = "https://login.microsoftonline.com/$TenantId" } #Request token If ($PSCmdlet.ParameterSetName -eq 'BySPConnection') { if ($AzureServicePrincipalConnection.ContainsKey('Applicationid') -and $AzureServicePrincipalConnection.ContainsKey('TenantId') -and $AzureServicePrincipalConnection.ContainsKey('SubscriptionId')) { if ($AzureServicePrincipalConnection.ContainsKey('ServicePrincipalKey')) { #add /oauth2/token to the end of URI since this function not using the DLL to generate token if ($OAuthURI -ieq "https://login.microsoftonline.com/$TenantId") { $OAuthURI = "https://login.microsoftonline.com/$TenantId/oauth2/token" } $token = Get-AzureADTokenForServicePrincipal -AzureServicePrincipalConnection $AzureServicePrincipalConnection -OAuthURI $OAuthURI -ResourceURI $ResourceURI } elseif ($AzureServicePrincipalConnection.ContainsKey('CertificateThumbprint')) { $token = Get-AzureADTokenForCertServicePrincipal -AzureServicePrincipalConnection $AzureServicePrincipalConnection -OAuthURI $OAuthURI -ResourceURI $ResourceURI } } else { Write-Error "The connection object is invalid. please ensure the connection object type must be either 'Key Based AzureServicePrincipal' or 'AzureServicePrincipal'." Exit -1 } } elseif ($PSCmdlet.ParameterSetName -eq 'ByCred') { $ClientId = $Credential.UserName #Check if an Azure Application service principal is used try { [System.Guid]::Parse($ClientId) | Out-Null $bIsSP = $true } catch { $bIsSP = $false } if ($bIsSP) { #add /oauth2/token to the end of URI since this function not using the DLL to generate token if ($OAuthURI -ieq "https://login.microsoftonline.com/$TenantId") { $OAuthURI = "https://login.microsoftonline.com/$TenantId/oauth2/token" } $Token = Get-AzureADTokenForServicePrincipal -TenantID $TenantID -Credential $Credential -OAuthURI $OAuthURI -ResourceURI $ResourceURI } else { $Token = Get-AzureADTokenForUser -TenantID $TenantID -Credential $Credential -OAuthURI $OAuthURI -ResourceURI $ResourceURI } } elseif ($PSCmdlet.ParameterSetName -eq 'ByCertFile') { $Token = Get-AzureADTokenForCertServicePrincipal -TenantID $TenantID -ApplicationId $ApplicationId -CertFilePath $CertFilePath -CertFilePassword $CertFilePassword -OAuthURI $OAuthURI -ResourceURI $ResourceURI } elseif ($PSCmdlet.ParameterSetName -eq 'ByCertThumbprint') { $Token = Get-AzureADTokenForCertServicePrincipal -TenantID $TenantID -ApplicationId $ApplicationId -CertThumbprint $CertThumbprint -OAuthURI $OAuthURI -ResourceURI $ResourceURI } else { #Getting an token for user principal by interactive logon - support for MFA scenario $InteractiveParam = @{ 'TenantID' = $TenantID 'OAuthURI' = $OAuthURI 'ResourceURI' = $ResourceURI } if ($PSBoundParameters.ContainsKey('UserName')) { $InteractiveParam.Add('UserName', $UserName) } $Token = Get-AzureADTokenForUserInteractive @InteractiveParam } $token } Function Get-AzureADTokenForServicePrincipal { [CmdletBinding()] [OutputType([string])] PARAM ( [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $true)] [Alias('Con', 'Connection')] [Object]$AzureServicePrincipalConnection, [Parameter(ParameterSetName = 'ByCred', Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [Alias('tID')] [String]$TenantID, [Parameter(ParameterSetName = 'ByCred', Mandatory = $true, HelpMessage = 'Please specify the Azure AD credential')] [Alias('cred')] [ValidateNotNullOrEmpty()] [PSCredential]$Credential, [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCred', Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$OAuthURI, [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCred', Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$ResourceURI = 'https://management.azure.com/' ) #Extract fields from connection (hashtable) If ($PSCmdlet.ParameterSetName -eq 'BySPConnection') { $bvalidConnectionObject = $false if ($AzureServicePrincipalConnection.ContainsKey('Applicationid') -and $AzureServicePrincipalConnection.ContainsKey('TenantId') -and $AzureServicePrincipalConnection.ContainsKey('SubscriptionId')) { if ($AzureServicePrincipalConnection.ContainsKey('ServicePrincipalKey')) { $ClientId = $AzureServicePrincipalConnection.ApplicationId $ClientSecret = $AzureServicePrincipalConnection.ServicePrincipalKey $TenantId = $AzureServicePrincipalConnection.TenantId $bvalidConnectionObject = $true } } if (!$bvalidConnectionObject) { Write-Error "The connection object is invalid. please ensure the connection object type must be 'Key Based AzureServicePrincipal'." Exit -1 } } If ($PSCmdlet.ParameterSetName -eq 'ByCred') { $ClientId = $Credential.UserName $ClientSecret = $Credential.GetNetworkCredential().Password } #URI to get oAuth Access Token If (!$PSBoundParameters.ContainsKey('oAuthURI')) { $oAuthURI = "https://login.microsoftonline.com/$TenantId/oauth2/token" } #oAuth token request $body = 'grant_type=client_credentials' $body += '&client_id=' + $ClientId $body += '&client_secret=' + [Uri]::EscapeDataString($ClientSecret) $body += '&resource=' + [Uri]::EscapeDataString($ResourceURI) $response = Invoke-RestMethod -Method POST -Uri $oAuthURI -Headers @{} -Body $body $Token = "Bearer $($response.access_token)" $Token } Function Get-AzureADTokenForUser { [CmdletBinding()] [OutputType([string])] PARAM ( [Parameter(Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [Alias('tID')] [String]$TenantID, [Parameter(Mandatory = $true)][Alias('cred')] [pscredential] [System.Management.Automation.CredentialAttribute()] $Credential, [Parameter(Mandatory = $true)] [String][ValidateNotNullOrEmpty()]$OAuthURI, [Parameter(Mandatory = $true)] [String][ValidateNotNullOrEmpty()]$ResourceURI ) Try { $Username = $Credential.Username $Password = $Credential.Password # Set well-known client ID for Azure PowerShell $clientId = '1950a258-227b-4e31-a9cf-717495945fc2' # Set Authority to Azure AD Tenant $authority = 'https://login.microsoftonline.com/common/' + $TenantID Write-Verbose "Authority: $OAuthURI" $AADcredential = [Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential]::new($UserName, $Password) $authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($OAuthURI) $authResult = $authContext.AcquireTokenAsync($ResourceURI, $clientId, $AADcredential) $Token = $authResult.Result.CreateAuthorizationHeader() } Catch { Throw $_ $ErrorMessage = 'Failed to aquire Azure AD token.' Write-Error -Message $ErrorMessage } $Token } Function Get-AzureADTokenForUserInteractive { [CmdletBinding()] [OutputType([string])] PARAM ( [Parameter(Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [Alias('tID')] [String]$TenantID, [Parameter(Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$UserName, [Parameter(Mandatory = $true)] [String][ValidateNotNullOrEmpty()]$OAuthURI, [Parameter(Mandatory = $true)] [String][ValidateNotNullOrEmpty()]$ResourceURI ) Try { # Set well-known client ID for Azure PowerShell $clientId = '1950a258-227b-4e31-a9cf-717495945fc2' # Set redirect URI for Azure PowerShell $redirectUri = "urn:ietf:wg:oauth:2.0:oob" # Set Authority to Azure AD Tenant Write-Verbose "Authority: $OAuthURI" $authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($oAuthURI) $promptBehavior = [ Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always if ($PSBoundParameters.ContainsKey('UserName')) { $userIdentifier = [Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier]::new($UserName, [Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifierType]::RequiredDisplayableId) $authResult = $authContext.AcquireTokenAsync($ResourceURI, $clientId, $redirectUri, [Microsoft.IdentityModel.Clients.ActiveDirectory.platformParameters]::new($promptBehavior), $userIdentifier) } else { $authResult = $authContext.AcquireTokenAsync($ResourceURI, $clientId, $redirectUri, [Microsoft.IdentityModel.Clients.ActiveDirectory.platformParameters]::new($promptBehavior)) } $token = $authResult.Result.CreateAuthorizationHeader() } Catch { Throw $_ $ErrorMessage = 'Failed to aquire Azure AD token.' Write-Error -Message $ErrorMessage } $token } Function Get-AzureADTokenForCertServicePrincipal { [CmdletBinding()] [OutputType([string])] PARAM ( [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $true)] [Alias('Con', 'Connection')] [Object]$AzureServicePrincipalConnection, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [String]$TenantID, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $true)] [ValidateScript( { try { [System.Guid]::Parse($_) | Out-Null $true } catch { $false } })] [String]$ApplicationId, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true, HelpMessage = 'Please specify the pfx Certificate file path')] [ValidateScript( { test-path $_ })] [string]$CertFilePath, [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $true, HelpMessage = 'Please specify the pfx Certificate file path')] [ValidateNotNullOrEmpty()] [SecureString]$CertFilePassword, [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $true, HelpMessage = "Please specify the Thumbprint of the certificate located in 'Cert:\LocalMachine\My' cert store")] [ValidateScript( { test-path "Cert:\LocalMachine\My\$_" })] [string]$CertThumbprint, [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$OAuthURI, [Parameter(ParameterSetName = 'BySPConnection', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertFile', Mandatory = $false)] [Parameter(ParameterSetName = 'ByCertThumbprint', Mandatory = $false)] [String][ValidateNotNullOrEmpty()]$ResourceURI = 'https://management.azure.com/' ) #Extract fields from connection (hashtable) If ($PSCmdlet.ParameterSetName -eq 'BySPConnection') { $bvalidConnectionObject = $false if ($AzureServicePrincipalConnection.ContainsKey('Applicationid') -and $AzureServicePrincipalConnection.ContainsKey('TenantId') -and $AzureServicePrincipalConnection.ContainsKey('SubscriptionId')) { if ($AzureServicePrincipalConnection.ContainsKey('CertificateThumbprint')) { $ApplicationId = $AzureServicePrincipalConnection.ApplicationId $CertThumbprint = $AzureServicePrincipalConnection.CertificateThumbprint $TenantId = $AzureServicePrincipalConnection.TenantId $bvalidConnectionObject = $true } } if (!$bvalidConnectionObject) { Write-Error "The connection object is invalid. please ensure the connection object type must be 'AzureServicePrincipal'." Exit -1 } } #Get the cert X509Certificate object If ($PSCmdlet.ParameterSetName -eq 'ByCertFile') { try { $marshal = [System.Runtime.InteropServices.Marshal] $ptr = $marshal::SecureStringToBSTR($CertFilePassword) $CertFilePlainPassword = $marshal::PtrToStringBSTR($ptr) $marshal::ZeroFreeBSTR($ptr) $Cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($CertFilePath, $CertFilePlainPassword) } Catch { Throw $_.Exception Exit -1 } } else { $CertStore = "Cert:\LocalMachine\My" $CertStorePath = Join-Path $CertStore $CertThumbprint $Cert = Get-Item $CertStorePath if (!$Cert) { Write-Error "Unable to get cert with thumbprint $CertThumbprint in cert store '$CertStore'." exit -1 } } #URI to get oAuth Access Token If (!$PSBoundParameters.ContainsKey('oAuthURI')) { $oAuthURI = "https://login.microsoftonline.com/$TenantId/oauth2/token" } #oAuth token request $ClientCert = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate]::new($ApplicationId, $Cert) $authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($oAuthURI) $Token = ($authContext.AcquireTokenAsync($ResourceUri, $ClientCert)).Result.AccessToken $Token = "Bearer $Token" $Token } New-Alias -Name Add-AzureRMServicePrincipalAccount -Value Add-AzServicePrincipalAccount |