AAD Service Principal Certificate Authentication

Did you ever wanted to automate everything in Azure? Using Azure Automation or using Remote PowerShell – pretty much anything automated in Azure, you should NOT be using a stock standard user account. Why? Because you can have all sorts of problems, for instance the password can expire and then it breaks everything and everything stops working. It’s a bit like on-prem days where you would use specific service accounts, each service account setup for a specific purpose, much easier to manage and it’s best practice.

In Azure it’s no difference, you use a service principal and grant this service principal access to where ever in Azure with contributor or owner privileges.

The below script (run as admin) walks you through setting up an AAD application, creating the service principal, creating a self signed certificate then uploading the certificate to Azure.

There’s a section further below in the script to create all the Azure automation variables and sets up the certificate so you can use the Service Principal with Azure Automation. Remember, you need the certificate installed on the machine in which you want to connect automatically to Azure with using certificate based authentication.

Then further below is an example Azure Automation block of code clearly labled “Azure Automation PowerShell Workflow Runbook example” which you add to your own Azure Automation account as a starting point.

# Written with Azure PowerShell module v6.1.0 available from https://github.com/Azure/azure-powershell/releases/tag/v6.1.0-May2018
# Recently tested fully with a later version of the Azure PowerShell module – Install-Module AzureRM -RequiredVersion 6.1.0
######################
############## Step 0: Change Variables, and logon
######################
# Define Azure Automation Assets
$AutomationAppIDName = 'AutomationAppId' # Feel free to change to suit you
$AutomationTenantIDName = 'AutomationTenantId' # Feel free to change to suit you
$AutomationCertificateName = 'AutomationCertificate' # Feel free to change to suit you
$AutomationSubscriptionIDName = 'AutomationSubscriptionId' # Feel free to change to suit you
# Login to Azure manually
LoginAzureRmAccount
### Choose Subscription
$subscription = Get-AzureRmSubscription | Out-GridView Title "Select the Azure subscription that you want to use …" PassThru
Select-AzureRmSubscription SubscriptionId $subscription.id
######################
############## Step 1: Create certificate for Azure AD Service Principal
######################
# Define certificate start and end dates
$currentDate = Get-Date
$endDate = $currentDate.AddYears(1)
$notAfter = $endDate.AddYears(1)
# Generate new self-signed certificate from "Run as Administrator" PowerShell session
$certName = Read-Host Prompt "Enter FQDN Subject Name for certificate"
$certStore = "Cert:\LocalMachine\My"
$certThumbprint = (New-SelfSignedCertificate DnsName $certName CertStoreLocation $CertStore KeyExportPolicy Exportable Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" NotAfter $notAfter).Thumbprint
######################
############## Step 2: Export Certificate to PFX file
######################
# Export password-protected pfx file
$pfxPassword = Read-Host Prompt "Enter password to protect exported certificate:" AsSecureString
$pfxFilepath = "$($env:TEMP)\$(Get-Date Format yyyyMMdd)pfxfile.json"
Export-PfxCertificate Cert "$($certStore)\$($certThumbprint)" FilePath $pfxFilepath Password $pfxPassword
######################
############## Step 3: Create Key Credential Object
######################
# Create Key Credential Object
$cert = New-Object TypeName System.Security.Cryptography.X509Certificates.X509Certificate ArgumentList @($pfxFilepath, $pfxPassword)
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())
$keyId = [guid]::NewGuid()
Get-Module AzureRM.Resources
Import-Module Name AzureRM.Resources
# $keyCredential = New-Object -TypeName Microsoft.Azure.Commands.Resources.Models.ActiveDirectory.PSADKeyCredential
$keyCredential = New-Object TypeName Microsoft.Azure.Graph.RBAC.Version1_6.ActiveDirectory.PSADKeyCredential
<#
If you get an error:
New-Object : Cannot find type [Microsoft.Azure.Commands.Resources.Models.ActiveDirectory.PSADKeyCredential]: verify that the assembly containing this type is loaded.
Install this module version v4.2.1 specifically, run this: – Install-Module AzureRM -RequiredVersion 4.2.1
#>
# Define properties of Key Credential object
$keyCredential.StartDate = $currentDate
$keyCredential.EndDate = $endDate
$keyCredential.KeyId = $keyId
$keyCredential.CertValue = $keyValue
#$keyCredential | fl *
######################
############## Step 4: Create Azure AD Service Principal
######################
# Define Azure AD Application Properties
$adAppName = Read-Host Prompt "Enter unique Azure AD App name"
$adAppHomePage = Read-Host Prompt "Enter unique Azure AD App Homepage URI" # Any URL, http://microsoft.com if you like :)
$adAppIdentifierUri = Read-Host Prompt "Enter unique Azure AD App Identifier URI" # Can be the same as the first URI
# Remove Azure AD Application
#Remove-AzureRmADApplication -DisplayName $adAppName -Force
# Create new Azure AD Application
$adApp = New-AzureRmADApplication DisplayName $adAppName HomePage $adAppHomePage IdentifierUris $adAppIdentifierUri KeyCredentials $keyCredential
Write-Output "New Azure AD App Id: $($adApp.ApplicationId)"
# Create Azure AD Service Principal
New-AzureRmADServicePrincipal ApplicationId $adApp.ApplicationId
######################
############## Step 5: Assign Role-Based Access Control (RBAC) Permissions to the Service Principal
######################
# Sleep and wait for the new service principal to propergate through AAD
Start-Sleep Seconds 30
# Assign Owner permissions to the Service Principal for the selected subscription
New-AzureRmRoleAssignment RoleDefinitionName Owner ServicePrincipalName $adApp.ApplicationId.Guid
######################
############## Step 6: Test authenticating as Service Principal
######################
# Set Azure AD Tenant ID
$tenantId = (Get-AzureRmContext).Tenant.Id
# Test authenticating as Service Principal to Azure
LoginAzureRmAccount ServicePrincipal TenantId $tenantId ApplicationId $adApp.ApplicationId CertificateThumbprint $certThumbprint
### Choose Subscription
$subscription = Get-AzureRmSubscription | Out-GridView Title "Select the Azure subscription that you want to use …" PassThru
Select-AzureRmSubscription SubscriptionId $subscription.id
######################
############## Step 7: Define Azure Automation Assets
######################
# Select existing Azure Automation account
$automationAccount = Get-AzureRmAutomationAccount | Out-GridView Title "Select an existing Azure Automation account …" PassThru
# Create Azure Automation Asset for Azure AD App ID
New-AzureRmAutomationVariable Name $AutomationAppIDName Value $adApp.ApplicationId AutomationAccountName $automationAccount.AutomationAccountName `
ResourceGroupName $automationAccount.ResourceGroupName Encrypted:$false
# Create Azure Automation Asset for Azure AD Tenant ID
New-AzureRmAutomationVariable Name $AutomationTenantIDName Value $tenantId AutomationAccountName $automationAccount.AutomationAccountName `
ResourceGroupName $automationAccount.ResourceGroupName Encrypted:$false
# Create Azure Automation Asset for Certificate
New-AzureRmAutomationCertificate Name $AutomationCertificateName Path $pfxFilepath Password $pfxPassword `
AutomationAccountName $automationAccount.AutomationAccountName ResourceGroupName $automationAccount.ResourceGroupName
# Create Azure Automation Asset for Azure Subscription ID
New-AzureRmAutomationVariable Name $AutomationSubscriptionIDName Value $subscription.Id AutomationAccountName $automationAccount.AutomationAccountName `
ResourceGroupName $automationAccount.ResourceGroupName Encrypted:$false
##########################################################################################
##########################################################################################
############## ######################
############## Azure Automation PowerShell Workflow Runbook example ######################
############## ######################
##########################################################################################
##########################################################################################
workflow StartAzurePlaylistVMs
{
# Get Azure Automation Assets
$adAppId = Get-AutomationVariable Name 'AutomationAppId'
Write-Output "Azure AD Tenant Id: $adAppId"
$tenantId = Get-AutomationVariable Name 'AutomationTenantId'
Write-Output "Azure AD Tenant Id: $tenantId"
$subscriptionId = Get-AutomationVariable Name 'AutomationSubscriptionId'
Write-Output "Azure Subscription Id: $subscriptionId"
$cert = Get-AutomationCertificate Name 'AutomationCertificate'
$certThumbprint = ($cert.Thumbprint).ToString()
Write-Output "Service Principal Certificate Thumbprint: $certThumbprint"
# Install Service Principal Certificate
Write-Output "Install Service Principal certificate…"
if ((Test-Path "Cert:\CurrentUser\My\$($certThumbprint)") -eq $false) {
InlineScript {
$certStore = new-object System.Security.Cryptography.X509Certificates.X509Store("My", "CurrentUser")
$certStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
$certStore.Add($Using:cert)
$certStore.Close()
}
}
# Login to Azure
Write-Output "Login to Azure as Service Principal…"
LoginAzureRmAccount ServicePrincipal TenantId $tenantId ApplicationId $adAppId CertificateThumbprint $certThumbprint
# Select Azure Subscription
Write-Output "Select Azure subscription…"
Select-AzureRmSubscription SubscriptionId $subscriptionId TenantId $tenantId
$VMs = Get-AzureRmVM | where {$_.Name -match 'playlist'}
ForEach Parallel ($VM in $VMs)
{
Start-AzureRmVM Name $VM.Name ResourceGroupName $VM.ResourceGroupName
}
}

Below is how you would logon to Azure using PowerShell and certificate based authentication – as long as you have the certificate installed locally on the machine in which you are connection from, so you can successfully find the correct thumbprint.

### Log into Azure using certificate authentication
$AzureADAppID = '49fy7934-dc71-4bdd-8804-h9742fyh93y'
$certThumbprint = (Get-ChildItem Cert:\LocalMachine\My | where {$_.Subject -match 'PowerShell_SP'}).ThumbPrint
$TenantId = '3494h480-6a11-78k0-a439-49hh8f49d0'
LoginAzureRmAccount ServicePrincipal TenantId $tenantId ApplicationId $AzureADAppId CertificateThumbprint $certThumbprint
$Subscription = (Get-AzureRmSubscription | Out-GridView Title "Choose a Source & Target Subscription …" PassThru)
Select-AzureRmSubscription SubscriptionId $Subscription.Id

See my other post to see how to setup an AAD Service Principal using password authentication instead.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s