Create Azure AD ClientCredentials Key from PowerShell - powershell

In the Azure Portal I can create an Application, Key and Permissions to the Graph API.
I can get a Token using:
AuthenticationContext ac = new AuthenticationContext("https://login.windows.net/graphDir1.onmicrosoft.com");
ClientCredential cc = new ClientCredential("b3b1fc59-84b8-4400-a715-ea8a7e40f4fe", "FStnXT1QON84B5o38aEmFdlNhEnYtzJ91Gg/JH/Jxiw=");
AuthenticationResult authResult = ac.AcquireToken("https://graph.windows.net", cc);
Using the Azure Active Directory Module for Windows PowerShell I can create a new Symmetric Key.
New-MsolServicePrincipalCredential -AppPrincipalId ??? -Type Symmetric
Using the key returned from this in the code above returns the error:
AdalServiceException: AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.
This used to work with a previous version of ADAL using instead of ClientCredential, SymmetricKeyCredential but that class no longer exists.
Is there a way to generate a key from PowerShell that works with the code above?

Please try using Password as the key type:
New-MsolServicePrincipalCredential -AppPrincipalId $appId `
-Type Password `
-StartDate ([DateTime]::Now.AddMinutes(-5)) `
-EndDate ([DateTime]::Now.AddMonths(1)) `
-Value "$newPassword"
Hope this helps

Related

Execute an App registration without AzureAD

For a professional project, a chunk of the pipeline must be able to create an application (the first App registration, so I only have a global Admin) automatically within Azure AD. So far I used AzureAD which works well with Powershell 5.6 on Windows.
I now must be able to run the code with Ubuntu 20.04 and its Powershell 7.2. Unfortunately for me, AzureAD module is only supported on non-core Windows PowerShell, therefore it does not work on core PS6 or PS7. A very simplified piece of code is the following:
# Connection infos
$tenantId = "abcdef12345-1234-1234-124-abcdef12346789"
$account = "my_admin#domain.com" # Is cloud Admin by default
$password = ConvertTo-SecureString "MyPassword" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential -ArgumentList ($account, $password)
Connect-AzureAD -Credential $psCred -Tenant $tenantId
# Create app
$appName = "MyApp"
New-App -appName $appName -tenant_id $tenantId
I am stuck and my question is the following: how could I run such an operation with Powershell 7.2 considering AzureAD is not usable? I did check Connect-MgGraph for the connection part only (https://github.com/microsoftgraph/msgraph-sdk-powershell) but the clientId is an infos that I don't have -and want to create-.
Thanks in advance
You can use DeviceLogin as explained in this article to obtain an oAuth access token for you Global Administrator account in PowerShell (independent of the version) but this first step needs a human interaction.
After obtaining the token, you can use it to make Graph API calls with your Global Administrator permissions to create an application.
Once you create your first application, you must attribute required permissions and use it to automate the process (obtain token programmatically using API calls) for application creation in PowerShell.
You could use Resource Owner Password Credentials (ROPC) to authenticate, however Microsoft actively discourages it in their documentation due to the security implications of sending a password over the wire.
If the security issues present with this method of authentication are still tolerated within your acceptance criteria, you would still need a ClientID. Luckily, AzureAD has a well-known ClientID that you can use to authenticate. This ID is 1950a258-227b-4e31-a9cf-717495945fc2
The below Powershell code should get you started. I've basically translated the HTTP request within Microsoft's documentation into a splatted Invoke-RestMethod command.
$LoginWithROPCParameters = #{
URI = "https://login.microsoftonline.com/contoso.onmicrosoft.com/oauth2/v2.0/token"
Method = "POST"
Body = #{
client_id = "1950a258-227b-4e31-a9cf-717495945fc2"
scope = "user.read openid profile offline_access"
username = "username#contoso.onmicrosoft.com"
password = "hunter2"
grant_type = "password"
}
}
Invoke-RestMethod #LoginWithROPCParameters

Azure AD - Cannot set Application Registration Key Credential with Type "Sign"

I'm trying to set a custom signing key for an Azure AD Application Registration. However, I get a confusing error message and cannot complete the request.
I tried to set the credential using multiple strategies:
PowerShell New-AzureADApplicationKeyCredential command
Microsoft Graph API
Manipulating the Application Registrations Manifest directly in Azure Portal
Microsoft Graph returns a simple "Bad Request", whereas PowerShell and Azure Portal are more specific in their responses:
"The value for the property "usage" in one of your credentials is invalid. Acceptable values are Sign, Verify."
The interesting thing about this error is that I am specifying the usage as "Sign".
PowerShell code snippet:
$appObjectID = $appRegistration.ObjectId
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("<path-to-certificate>")
$bin = $cer.GetRawCertData()
$base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)
New-AzureADApplicationKeyCredential `
-ObjectId $appObjectID `
-CustomKeyIdentifier $base64Thumbprint `
-Type AsymmetricX509Cert `
-Usage Sign `
-Value $base64Value `
-StartDate $cer.GetEffectiveDateString() `
-EndDate $cer.NotAfter.ToString()
Error message:
Code: Request_BadRequest
Message: The value for the property "usage" in one of your credentials is invalid. Acceptable values are Sign, Verify.
RequestId: <id>
DateTimeStamp: <timestamp>
Details: PropertyName - keyCredentials.keyId, PropertyErrorCode - InvalidKeyUsage
HttpStatusCode: BadRequest
HttpStatusDescription: Bad Request
HttpResponseStatus: Completed`
This is based on the documentation: MS Docs: New-AzureADApplicationKeyCredential. However, I think there is a mistake in this documentation, since they use a randomly generated GUID as input for the parameter ObjectID, which should be the ObjectID of the Application Registration I want to add the new key credential to. So I replaced this keyId with the ObjectId of my Application Registration. (If I directly use the code from MS Docs, I get a "Request_ResourceNotFound" error because the command can't find the Application Registration with this random GUID in Azure AD.)
Things I have tried:
Change -Usage Sign to -Usage "Sign"
Adding a "Verify" credential to the App (works as expected) with this command
When I try to directly modify the Manifest in Azure Portal, I basically get the same error message:
Failed to update <app-name> application. Error detail: The value for the property "usage" in one of your credentials is invalid. Acceptable values are Sign, Verify.
Screenshot from error in Azure Portal
Is there maybe a issue that some parameters cannot be used this way when setting a "Sign" credential?
Thanks in advance for any help and regards!
Thanks to Ash (see his comment to my initial question) I found the solution in this article. I followed the tutorial and could set the "Sign" Key Credential using Graph API after also including a "Verify" Key Credential and a corresponding Password Credential into the request body.

Missing cluster cert causes Add-AzServiceFabricClusterCertificate to fail: Object reference not set to an instance of an object

I'm fairly new to Service Fabric, so I'm not sure if this is an issue with the cmdlet or if this is a miss on my part. I am using Az.ServiceFabric module version 2.0.2 and the Az module version 3.8.0.
I am trying to use the Add-AzServiceFabricClusterCertificate cmdlet to add a secondary certificate that I've already created in my Azure KeyVault to my cluster. When I run the cmdlet, it fails with this error (running with Debug gave me more stack detail):
DEBUG: AzureQoSEvent: CommandName - Add-AzServiceFabricClusterCertificate; IsSuccess - False; Duration -
00:00:07.3059582;; Exception - System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Azure.Commands.ServiceFabric.Commands.ServiceFabricClusterCmdlet.GetClusterType(Cluster
clusterResource)
at Microsoft.Azure.Commands.ServiceFabric.Commands.AddAzureRmServiceFabricClusterCertificate.ExecuteCmdlet()
at Microsoft.WindowsAzure.Commands.Utilities.Common.AzurePSCmdlet.ProcessRecord();
Looking at the code for this cmdlet, I noticed that it's probably failing because the cluster resource that gets passed into GetClusterType does not have its Certificate member, so it fails when it tries to check the Certificate.Thumbprint and Certificate.ThumbprintSecondary:
internal ClusterType GetClusterType(Cluster clusterResource)
{
if (string.IsNullOrWhiteSpace(clusterResource.Certificate.Thumbprint) &&
string.IsNullOrWhiteSpace(clusterResource.Certificate.ThumbprintSecondary))
{
return ClusterType.Unsecure;
}
else
{
return ClusterType.Secure;
}
}
The cluster that gets passed into GetClusterType is retrieved in the same manner as in the Get-AzServiceFabricCluster cmdlet, so when I run that cmdlet for the cluster that I'm trying to add the certificate to, I noticed that my Certificate field is empty in the response. I'm guessing that's what's causing the NullRef exception. Here's that relevant snippet:
AzureActiveDirectory :
TenantId : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ClusterApplication : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
ClientApplication : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Certificate :
CertificateCommonNames : Microsoft.Azure.Management.ServiceFabric.Models.ServerCertificateCommonNames
ClientCertificateCommonNames :
ClientCertificateThumbprints :
I'm wondering if it's expected that the Certificate field would be empty when I'm using the Get-AzServiceFabricCluster cmdlet and if that is indeed the cause of my Add-AzServiceFabricClusterCertificate cmdlet failing. When I look at the cluster's Security blade in Azure Portal, I do see the Primary Cluster Certificate with which I originally created the cluster, and this is the cert that I use when deploying and doing other cluster operations. However, I did notice that the cert thumbprint field is empty when viewing the certificate from the portal. I would expect to see this certificate when using Get-AzServiceFabricCluster, but it comes up empty. Is this certificate missing from my Get-AzServiceFabricCluster cmdlet possible to fix through the portal or with another cmdlet?
It looks like your cluster is configured to find certificates by common name, rather than thumbprint. I'm guessing this based on the fact your portal doesn't show a thumbprint against the certificate, in addition to the snippet you have posted.
If this is the case, there's no need to update your cluster configuration with a new certificate when the old certificate has expired - Instead you need to install the certificate only into your VMSS vault. Once you add the new certificate to the VMSS, Service Fabric will automatically use the later expiring certificate.
You must always ensure you have at least one valid certificate installed on your VMSS with the common name configured in your cluster.
PS to upload certificate KV and install onto VMSS:
$subscriptionId = "sub-id"
$vmssResourceGroupName = "vmss-rg-name"
$vmssName = "vmss-name"
$vaultName = "kv-name"
$primaryCertName = "kv-cert-name"
$certFilePath = "...\.pfx"
$certPassword = ConvertTo-SecureString -String "password" -AsPlainText -Force
# Sign in to your Azure account and select your subscription
Login-AzAccount -SubscriptionId $subscriptionId
# Update primary certificate within the Key Vault
$primary = Import-AzKeyVaultCertificate `
-VaultName $vaultName `
-Name $primaryCertName `
-FilePath $certFilePath `
-Password $certPassword
$certConfig = New-AzVmssVaultCertificateConfig -CertificateUrl $primary.SecretId -CertificateStore "My"
# Get VM scale set
$vmss = Get-AzVmss -ResourceGroupName $vmssResourceGroupName -VMScaleSetName $vmssName
# Add new certificate version
$vmss.VirtualMachineProfile.OsProfile.Secrets[0].VaultCertificates.Add($certConfig)
# Update the VM scale set
Update-AzVmss -ResourceGroupName $vmssResourceGroupName -Verbose `
-Name $vmssName -VirtualMachineScaleSet $vmss
For more info, I wrote a blog post on switching from thumbprint to common name.
The official docs are also a good reference.

Generate a JWT with an X.509 certificate signature in Powershell

I'm trying to get an API access token from Active Directory using certificate authentication as shown here:
Access token request with a certificate
The request needs the client_assertion property, which is a JWT created from the certificate with the format and specs mentioned here:
Assertion format
Is there a way of generating this token in Powershell that is non-interactive as this is part of a release pipeline?
Edit: To make it a little more clear, this C# code using the Microsoft.IdentityModel.Clients.ActiveDirectory library is what I'm trying to do in Powershell, specifically the second line:
AuthenticationContext authContext = new AuthenticationContext(authority);
IClientAssertionCertificate assertion = new ClientAssertionCertificate(clientId, certificate);
authenticationResult = await authContext.AcquireTokenAsync(resource, assertion);
Sample code (just tested this, this works for me when I register an app as a web app).
<#
Sample to connect to Graph using a certificate to authenticate
Prerequisite : ADAL (Microsoft.IdentityModel.Clients.ActiveDirectory.dll)
#>
# Load the ADAL Assembly
Add-Type -Path "E:\Assemblies\Microsoft.IdentityModel.Clients.ActiveDirectory.4.3.0\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
# Settings for the application
$AppID = '<ID OF THE WEB APP>'
$TenantDomain = '<TENANT>'
$LoginUri = 'https://login.microsoftonline.com/'
$Resource = 'https://graph.microsoft.com'
$Certificate = Get-Item 'Cert:\CurrentUser\My\<CERTIFICATE THUMBPRINT>' # This points to my own certificate
# Auth Authority Uri
$Authority = "$LoginUri/$TenantDomain"
# Create the authenticationContext
$Context = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($Authority)
# create the CAC
$CAC = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate]::new($AppID,$Certificate)
# Get the token
$TokenResponse = $Context.AcquireTokenAsync($Resource,$CAC)
Start-Sleep -Seconds 1 # Sleep for 1 second...
# Token should be present
$TokenResult = $TokenResponse.Result
As I know, you could not use PowerShell to create the JWT.
You could use jwt.io to edit each part (header, payload), and then jwt.io will automatically encode it into a JWT for the client_assertion.

Using Powershell to get Azure AD Token (jwt)

I am trying to get a jwt token from AAD using Powershell using Username/Password authentication.
I am writing a powershell script that will to call an API using a bearer token. What I have works if I copy & paste the token from an SPA that uses the API. I am looking for a way to retrieve the token from my powershell.
This looks really promising: https://github.com/Azure-Samples/active-directory-dotnet-native-headless/blob/master/TodoListClient/Program.cs
I feel like I'm smacking my head against a wall trying to create a 'UserPasswordCredential' object. Any clues to how I can do this would be super-helpful.
I have Add-Type-ed:
- Microsoft.IdentityModel.Clients.ActiveDirectory.dll
- Microsoft.IdentityModel.Clients.ActiveDirectory.platform.dll (adds nothing?)
- Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll
- Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll
The docs page for 'UserPasswordCredential' :
https://learn.microsoft.com/en-us/dotnet/api/microsoft.identitymodel.clients.activedirectory.userpasswordcredential
It should be in one of the first two dlls
This, under 'Constraints & Limitations', makes me think it may not actually be possible from powershell:
http://www.cloudidentity.com/blog/2014/07/08/using-adal-net-to-authenticate-users-via-usernamepassword/
Looking at the code below, the first acquire token succeeds, the second fails - possibly/probably because $cred is a UserCredential not a UserPasswordCredential.
Is is possible to do this with powershell?
Finally, on a totally different track, how do I find the values for redirectUri and resourceAppIdURI that my application needs? When I look in the AAD console, and browser to my Enterprise Application, I can find the AppId (which I can use as $clientId).
I'm not sure the redirectUri is strictly necessary for me as all I really want is the token, but I can have a good guess at what it should be.
When I try to call the first AquireToken method (without $cred) using my app details, it fails with this message:
Exception calling "AcquireToken" with "4" argument(s): "AADSTS50001: The application named https://myappwithapi/Login was not found in the tenant named me.onmicrosoft.com.
Is it possible for me to find the require value for resourceAppIdURI by looking in my azure portal?
'https://myappwithapi/Login' is from my azure portal > enterprise apps > [app' > properties > HomepageUrl
code:
#setup
$TenantName = "mme.onmicrosoft.com"
$clientId = "1950a258-227b-4e31-a9cf-717495945fc2" # Microsoft
$clientId = "03faf8db-..........................." #
$username = "me#me.onmicrosoft.com"
$password = Read-Host -AsSecureString -Prompt "Enter Password"
# add dlls
$adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
$adalforms = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll"
$adalplatform = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.platform.dll"
[System.Reflection.Assembly]::LoadFrom($adal) | Out-Null
[System.Reflection.Assembly]::LoadFrom($adalforms) | Out-Null
[System.Reflection.Assembly]::LoadFrom($adalplatform) | Out-Null
#prep request
$redirectUri = "urn:ietf:wg:oauth:2.0:oob" # Microsoft
$resourceAppIdURI = "https://graph.windows.net"
$authority = "https://login.windows.net/$TenantName"
$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
# Get Token prompting for creds
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "Always")
$authResult
# Get the cred
$cred = New-Object -TypeName 'Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential' -ArgumentList $username, $password
#$cred = New-Object -TypeName 'Microsoft.IdentityModel.Clients.ActiveDirectory.UserPassCredential' -ArgumentList $username, $password
$authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $cred)
$authResult
This post has more the one question in it.
Your base use case 'Using Powershell to get Azure AD Token (jwt)' is a common one and there are several samples and pre-built examples to leverage. For example:
https://github.com/pcgeek86/AzureADToken
A PowerShell module that allows you to get a JSON Web Token (JWT) from Azure Active Directory (AAD).
https://gallery.technet.microsoft.com/Get-Azure-AD-Bearer-Token-37f3be03
This script acquires a bearer token that can be used to authenticate to the Azure Resource Manager API with tools such as Postman. It uses the Active Directory Authentication Library that is installed with the Azure SDK.
See if those two resources resolves your use base line use case.
As for this...
"Is it possible for me to find the require value for resourceAppIdURI by looking in my azure portal?"
You can do this via a remote PowerShell logon to AzureAD. Install the AAD PowerShell module.
https://learn.microsoft.com/en-us/powershell/azure/overview?view=azurermps-5.1.1
https://msdn.microsoft.com/en-us/library/dn135248(v=nav.70).aspx
Download and install MSOL. Sign in with the MSOL
https://www.microsoft.com/en-US/download/details.aspx?id=39267
The Microsoft Online Services Sign-In Assistant provides end user sign-in capabilities
and use the built-in cmdlets to pull your information from your organization settings, and or hit the MSGraph API and query.
https://learn.microsoft.com/en-us/powershell/azure/active-directory/install-adv2?view=azureadps-2.0
You can use the Azure Active Directory PowerShell Module Version for Graph for Azure AD administrative tasks
As for this one:
"how do I find the values for redirectUri and resourceAppIdURI that my application needs?"
This is in your app registration section of your portal. The developer team provide the redir uri not Azure. It's part of the registration process all else is generated by Azure App Reg process.
The app registration process is here and of course you are someone else had to register this app in AzureAD, and thus can retrieve it at any time.:
https://blogs.msdn.microsoft.com/onenotedev/2015/04/30/register-your-application-in-azure-ad
Any registered apps and their details can be retrieved using...
Get-AzureADApplication
Get-AzureADApplication | Select -Property *
(Get-AzureADApplication).ReplyUrls
Get-AzureADApplication | Select -Property AppID, DisplayName,ReplyUrls
https://learn.microsoft.com/en-us/powershell/module/azuread/get-azureadapplication?view=azureadps-2.0