Authenticate to Azure Keyvault using Certificate and get secret - azure

I am looking for an example of using a certificate to authenticate to the keyvault, and then get a secret -- all in PowerShell (already have operational C#).
Have an app in AD for accessing Keyvault.

First, make sure your AD App(service principal) has the correct permission in your keyvault -> Access policies, in your case, it should be Get and List secret permissions.
Then get values for signing in and try the command as below.
Connect-AzAccount -CertificateThumbprint "<certificate Thumbprint>" -ApplicationId "<AD App applicationid(clientid)>" -Tenant "<tenant id>" -ServicePrincipal
Get-AzKeyVaultSecret -VaultName "<keyvault name>" -Name "<secret name>" -Version "<secret version>" | ConvertTo-Json

Related

azure App registration using powershell does not create a service principal

when we create an azure ad app registration from the azure portal the service principal is automatically created, and given a contributors role, how do we achieve the same using PowerShell ?
I tried running the following the command the app is created but no service principal is created, and there are no parameters for configuring a service principle
New-AzADApplication -DisplayName "NewApplication" -HomePage "http://www.microsoft.com" -IdentifierUris "http://NewApplication"
what i am looking for is to create the following using powershell, is this possible ?
AzureADAppregistration + ServicePrincipal
Create and get the client secret
Thanks
Try this:
#set your secret here.
$secretTextValue = "abcdefg1234567890"
$secret = ConvertTo-SecureString -String $secretTextValue -AsPlainText
$app = New-AzADApplication -DisplayName "NewApplication" -HomePage "http://www.microsoft.com" -IdentifierUris "http://NewApplication"
New-AzADAppCredential -ObjectId $app.ObjectId -Password $secret -EndDate (Get-Date).AddMonths(6)
#azure will assign contributor role of current subscription to this SP
New-AzADServicePrincipal -ApplicationId $app.ApplicationId
Result:

Adding key vault access permissions while preserving existing ones

I have an Azure PowerShell task in my pipeline, in which I need to import a certificate to a key vault. Before doing that, I need to assign Import certificate permission to the current service principal. However, this service principal might already have existing certificate permissions (e.g. Get, List) from other tasks in this or other pipelines. If I use Set-AzKeyVaultAccessPolicy, it will remove these other permissions. Is there a way of preserving these permissions, and just adding some new ones?
$spId = (Get-AzContext).Account.Id;
Set-AzKeyVaultAccessPolicy -VaultName $kv -ServicePrincipalName $spId -PermissionsToCertificates Import
Import-AzKeyVaultCertificate -VaultName $kv …
There is no direct way to add the new permission, your option is to get the old permissions as a list, add the new permission to it, then set all the permissions again.
The sample works for me:
$spId = (Get-AzContext).Account.Id
$objectid = (Get-AzADServicePrincipal -ApplicationId $spId).Id
$kv = Get-AzKeyVault -ResourceGroupName <group-name> -VaultName joykeyvault
$cerpermission = ($kv.AccessPolicies | Where-Object {$_.ObjectId -eq $objectid}).PermissionsToCertificates
$cerpermission += "Import"
Set-AzKeyVaultAccessPolicy -VaultName joykeyvault -ObjectId $objectid -BypassObjectIdValidation -PermissionsToCertificates $cerpermission
Note: The parameters in the last line is important, if your service principal used in the devops service connection does not have the permission to list service principals in your AAD tenant, please use -ObjectId $objectid -BypassObjectIdValidation instead of -ServicePrincipalName $spId, otherwise you will get an error.

How to get thumbprint of the cert associated with a service principal in AzureAD when the sp is created independently without App and Cred

I have a service principal that I've creating using below powershell.
$sp3 = New-AzureRmADServicePrincipal `
-DisplayName "<service-principal-name>" `
-CertValue $certValue3 `
-EndDate ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($cert3.Certificate.GetExpirationDateString(), [System.TimeZoneInfo]::Local.Id, 'GMT Standard Time'))
where certValue3 is Base64String RawCertData. This service principal works fine and I am able to get a token when using the cert.
Once service principal is created in Azure AD, how do I see thumbprint of the certificate associated with the service principal using Powershell?
I've tried this, but I get Forbidden when I try to execute Get-AzureADApplicationKeyCredential
I also checked the manifest in Azure Portal under the service principal that gets created under Azure Active Directory → App Registrations → <service-principal-name> → Manifest, but the keyCredentials node is empty
"keyCredentials": [],
Please note that when I create an application using New-AzureRmADApplication followed by credential New-AzureRmADAppCredential and then New-AzureRmADServicePrincipal, then I see the keyCredentials with customKeyIdentifier set to the certificate thumbprint. Sample script below -
$adapp = New-AzureRmADApplication -DisplayName "<application-name>" `
-HomePage "<home-page-url>" `
-IdentifierUris "<identifier-url>" `
-CertValue $certValue `
-StartDate ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($cert.Certificate.GetEffectiveDateString(), [System.TimeZoneInfo]::Local.Id, 'GMT Standard Time')) `
-EndDate ([System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($cert.Certificate.GetExpirationDateString(), [System.TimeZoneInfo]::Local.Id, 'GMT Standard Time'))
New-AzureRmADAppCredential -ApplicationId $adapp.ApplicationId -CertValue $certValue2
$sp2 = New-AzureRmADServicePrincipal -ApplicationId $adapp.ApplicationId -DisplayName "<application-name>"
How to get thumbprint of the certificate associated with a service principal in Azure AD using powershell when the service principal is created independently without AzureRmADApplication and AzureRmADAppCredential?
According to my test, we can use the following Azure AD Graph API to get the key credentials of the sp. The customKeyIdentifier in KeyCredential is the thumbprint of the certificate
GET https://graph.windows.net/<your teanant id>/servicePrincipals/<your sp object id>/keyCredentials?api-version=1.6
For example
Create sp and get thumbprint
$tenantId ="<tenant id>"
#use the goabl admin account to login
Connect-AzureRmAccount -Tenant $tenantId
$certificateObject = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$certificateObject.Import("E:\Cert\examplecert.pfx","Password0123!", [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet)
Write-Host "the thumbrint of cert"
$certificateObject.Thumbprint
$keyValue = [System.Convert]::ToBase64String($certificateObject.GetRawCertData())
$sp =New-AzureRmADServicePrincipal -DisplayName "jimtestsample" -CertValue $keyValue -EndDate $endDate
$context=Get-AzureRmContext
$token=$context.TokenCache.ReadItems() |Where-Object { ($_.TenantId -eq $tenantId) -and ($_.Resource -eq "https://graph.windows.net/") }
$accesstoken=$token.AccessToken
$url = "https://graph.windows.net/$tenantId/servicePrincipals/"+$sp.Id+"/keyCredentials?api-version=1.6"
$keyCreds = Invoke-RestMethod -Uri $url -Method Get -Headers #{"Authorization" = "Bearer $accesstoken"}
Write-Host "--------------------------------------------"
$keyCreds.value | Select-Object customKeyIdentifier
I test your command, it should work. When using New-AzADServicePrincipal to create the service principal, it will create an AD App(i.e. App Registration) for you automatically, and the certificate will also appear in the Certificates & secrets of your AD App.
In my sample, I use the new Az module, for the old AzureRm module which you used, it should also work(Not completely sure, I recommend you to use the new Az module, because the AzureRm module has been deprecated and will not be updated). And make sure you are looking into the correct AD App in the portal, because the DisplayName of the AD App could be repeated.
$cert=New-SelfSignedCertificate -Subject "CN=TodoListDaemonWithCert" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -KeySpec Signature
$bin = $cert.RawData
$base64Value = [System.Convert]::ToBase64String($bin)
New-AzADServicePrincipal -DisplayName joy134 -CertValue $base64Value
Check in the portal:
Then you can use this way you have tried, to fix the Forbidden error, your account should at least be the Owner of the AD App, or if your account has an admin role in the tenant e.g. Application administrator, Groups administrator, it will also work.
$CustomKeyIdentifier = (Get-AzureADApplicationKeyCredential -ObjectId "<object-id>").CustomKeyIdentifier
$Thumbprint = [System.Convert]::ToBase64String($CustomKeyIdentifier)
Besides, you should note the different command combinations will lead to different results, see this link. So when you test it, I recommend you to use different values of the parameters.

Programmatically authenticate into AAD with MFA via powershell

I have a PowerShell script that logs into Azure subscription with the command Connect-AzAccount using user's credentials.
The code is the following:
$userPassword='password'
$userName="username"
$tenantId="########-####-####-####-############"
$subscriptionId="########-####-####-####-############"
$azureSecpassword = $userPassword | ConvertTo-SecureString -asPlainText -Force
$azureCredential = New-Object System.Management.Automation.PSCredential($userName, $azureSecpassword)
Connect-AzAccount -Credential $azureCredential -Tenant $tenantId -SubscriptionId $subscriptionId
The code above works without any user interaction.
Few days ago the customer enabled the multi-factor authentication for the users.
How can I keep a fully automated login process (without user interactions) with the multi-factor authentication?
Best Regards.
This is a common question. Unfortunately, the answer is No. If the account is MFA-enabled, you could just login with an interactive way.
In such a case, we choose to use the service principal to login with non-interactive in general.
$azureAplicationId ="Azure AD Application Id"
$azureTenantId= "Your Tenant Id"
$azurePassword = ConvertTo-SecureString "client secret" -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($azureAplicationId , $azurePassword)
Connect-AzAccount -Credential $psCred -TenantId $azureTenantId -ServicePrincipal
Reference - Sign in with a service principal.
If you must log in as a user, there might be 2 optional approaches.
1. If you will run the script locally or in a specific PC
You can Persist Azure user credentials. You can enable auto save, or manually save the context to a file, and then use it in another PS session.
If you enabled auto save, then you can directly get the context as following:
Get-AzContext
# If you have more than one contexts, you can choose one by specifing the name
Get-AzContext -Name 'CSP Azure (e5b0****-****-****-****-5e5f****4c68) - jack#h****a.onmicrosoft.com'
If you want to manually do it, here is the sample:
# Interactively log for one time
Connect-AzAccount
# Save the context
Save-AzContext -Path D:\ctx.dat
And in another PS session, you can:
Import-AzContext -Path D:\ctx.dat
2. Use refresh token to acquire token, and connect to Azure
You can get the refresh token from the auto saved Azure context (usually at C:\Users\<UserName>\.Azure\TokenCache.dat).
Open the dat file with notepad, and you will get the refresh token:
Then you can get a new token in PowerShell with that refresh token, and connect to Azure:
Clear-AzContext
$tenantId = "e4c9ab4e-****-****-****-230b****57fb"
$subscriptionId = "e5b0fcfa-****-****-****-5e5f****4c68"
$refreshToken = 'AQABAAAAAAAP0****a lot of characters here*****0A9FWoB8mvDtoWRJHBVO7GJzodLKYmNIAA'
$url = "https://login.microsoftonline.com/" + $tenantId + "/oauth2/token"
$body = "grant_type=refresh_token&refresh_token=" + $refreshToken
$response = Invoke-RestMethod $url -Method POST -Body $body
$AccessToken = $response.access_token
Connect-AzAccount -AccountId "the user id, jack#h****a.onmicrosoft.com" -AccessToken $AccessToken -Tenant $tenantId -SubscriptionId $subscriptionId
How can I keep a fully automated login process (without user interactions) with the multi-factor authentication?
You can't do this with a user account--that's the whole point of multi-factor authentication.
Instead, Azure AD supports authenticating with a service principal (instead of a user principal, like you're doing currently), and Azure supports granting access to Azure resources to service principals.
MFA requirements (and other conditional access policies) do not apply to service principals (often referred to as an Azure AD "app"), and service principals support more secure methods of authentication for automation scenarios (e.g. public/private key pairs).
So, what you should do:
Ensure the machine running this script is secure. Anyone with access to the machine has the same amount of access as the script.
Create an application identity and associate credentials with it.
Note: It is strongly recommend you use certificate-based authentication for your service principal, instead of password-based. It is a very insecure practice to have any kind of secret stored in a PowerShell script!
Grant the service principal the minimum level of access to Azure resources, to allow it to complete the required task.
Update your script to use the app's identity (service principal) instead of the user's identity. It's even simpler than using a user account:
$tenantId = "########-####-####-####-############"
$subscriptionId = "########-####-####-####-############"
$appId = "########-####-####-####-############"
$thumbprint= "##############"
Connect-AzAccount -ServicePrincipal -TenantId $tenantId -ApplicationId $appId -CertificateThumbprint $thumbprint
Note: If this script is running on a VM in Azure, you should forget step 2, and simply enable a managed identity and use that.

Calling the connect cmdlets in a script

I am trying to write some powershell script that has to combine both Az cmdlets and AzureRM to accomplish some of the stuff I want to do.
What truly happens though when I call all in the same script:
Connect-AzAccount
Connect-AzureAD
Connect-AzureRMAccount
Initially, I make a call to Get-Credential and save that in a variable.
Then I use those credentials to populate -Credential in the Connect-AzAccount
Then because of Multi-Factor Authentication, I have to then make a call to Connect-AzureAD, which prompts a popup that allows the user to enter Email, Password and Code from MFA text to phone.
Later in the script, there are some cmdlets that are in the RM version, and so I call Connect-AzureRMAccount with the previous Credentials from above.
$credentials = Get-Credential
$azureCredentials = New-Object System.Management.Automation.PSCredential ($credentials.UserName, $credentials.Password)
Connect-AzAccount -Credential $azureCredentials -Tenant $tenantID -SubscriptionId $subscriptionID
Connect-AzureAD -Tenant $tenantID
Connect-AzureRMAccount -Credential $azureCredentials -Tenant $tenantID -SubscriptionId $subscriptionID
What is actually happening in terms of Authentication during this entire script where several different Connect cmdlets are called.
Due to some reason, there is a specific cmdlet
$AppRegistration = New-AzureADApplication -DisplayName $appName -HomePage $AppURI -IdentifierUris $AppURI -ReplyUrls $AppURI -PasswordCredentials $psadCredential
where I get an error in Powershell telling me that I need to call Connect-AzureAD again, even though it was already called once during the script. Does it time out with the MFA?
How can I avoid having to get the user to sign in several times after running the script?
I don't think this is a correct option, if you want to avoid MFA, the workaround is to create a service principal(AD App), grant the permissions for it, then you can login with the service principal without MFA.
You could follow the steps below.
1.Create an Azure Active Directory application, then Upload a certificate and Get values for signing in.
2.Navigate to the Azure Active Directory in the portal -> Roles and administrators -> click Application administrator -> Add assignment -> search by your AD App name(service principal name) -> select it -> Select.
Note: In your case, you want to use the command New-AzureADApplication, so you need to give the Application administrator directory role to your AD App(service principal), if you want to do other things need more permissions, you may need to give a role like Global administrator, it depends on you.
3.Then you could use the command below to login with Az module and AzureAD module.
Connect-AzAccount -CertificateThumbprint "F1D9FE13A8FBxxxx1C8B07D1666" -ApplicationId "aa60b5df-xxxxxx8ae8e0cc2e4" -Tenant "bb58915cxxxxxxb97ed6c65" -ServicePrincipal
Connect-AzureAD -CertificateThumbprint "F1D9FE13A8FBxxxx1C8B07D1666" -ApplicationId "aa60b5df-xxxxxx8ae8e0cc2e4" -Tenant "bb58915cxxxxxxb97ed6c65"
New-AzureADApplication -DisplayName "newapp" -IdentifierUris "http://mynewapp11.contoso.com"

Resources