Secure Authentication with PowerShell & Service Principals: MS Graph, Azure AD, Azure & Exchange Online

By | October 30, 2020

In this blog I will share how you can securely connect to many services using what is known as a “Service Principal”. We will be connecting to the following services with one script (only 12 lines of code) and one service principal with no authentication prompts:

  • Azure AD (AzureAD module)
  • Exchange Online (ExchangeOnlineManagement V2.0 Module)
  • Azure (Az module)
  • Microsoft Graph (Microsoft.Graph.Intune module)

You will need to install the modules listed above before the script will work for you. Access to create an Azure Keyvault is preferred. I will use one to get my application secrets within the script. This blog will consist of the following:

  • Registering an Application in Azure Active Directory
  • Assigning the appropriate permissions for that app (API, RBAC)
  • Generate a certificate on a machine and upload it to the app for authentication
  • Add a secret to the application
  • Create a Key vault and upload the secret
  • Grant the service principal access to read the secrets
  • The details you need to copy will be highlighted along the way
  • Make the script work for you

Registering an Application in Azure Active Directory

Step one is to register the application. This will be known as the service principal.

  1. Go to https://aad.portal.azure.com/
  2. Click App Registrations
  3. Click +New Registration
  4. Enter a friendly name for you application (my example will us PowerShell [Certificate Connections] as the name
  5. Click Register
  6. You have registered your application.

Assign the Permissions to the Service Principal

Permissions are key when using service principals to authenticate into your environment. This section will take about adding Microsoft API permissions as well as role based permissions. I want app to be used for things like Azure, Graph and more. Throughout other sections, we may add more permissions for things like Azure Keyvault.

Add API Permissions

  1. Locate you application from AAD>App Registrations>Your App
  2. Click API Permissions
  3. Add MS Graph API permissions. Click +Add a Permission
    1. Click Microsoft Graph
    2. Click Application Permissions
    3. Enter DeviceManagementServiceConfig.ReadWrite.All
    4. Check the box from the dropdown and click Add Permissions
  4. Add Exchange Online API Permissions. Click +Add Permission
    1. Click Exchange
    2. Click Application Permissions
    3. Enter Exchange.ManageAsApp
    4. Check the box from the dropdown and click Add Permissions
  5. Under App>API Permissions click Grant Admin Consent for tenant then Yes.
  6. Go to the Overview blade of your app and copy the Application (client) ID to a notepad. You will need this later.

Add Role Based Permissions

  1. For the Exchange Administrator Role.
    1. Go to AAD>Roles and Administrators
    2. Search and click Exchange Administrator
    3. Click +Add Assignments
    4. Search and select your service principal.
    5. Click Add
  2. For the Azure subscription access
    1. Go to https://portal.azure.com
    2. Browse to Subscriptions
    3. Select your desired subscription and click Access control (IAM)
    4. Click Role Assignments
    5. Click +Add>Add Role Assignment
    6. Select the role. In my example, I am using Contributor access
    7. Search and select your service principal
    8. Click Save

Generate Certificate for Application

When connecting to some modules, we will use certificates as the authentication method. First, we will generate the certificate, then upload it.

Generate the Cert

To generate the certificate, change the $subject parameter to match your desired certificate subject name.

$subject = 'PowershellConnection'
$cert = New-SelfSignedCertificate -CertStoreLocation "cert:\CurrentUser\My" `
  -Subject "$subject" `
  -KeySpec KeyExchange
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())

Export the Certificate

  1. Click Win+R
  2. Enter mmc and press enter
  3. Click Files>Add Remote Snapin>Certificates>Add>My user account>Finish>OK
  4. Go to Personal>Certificates
  5. Right click and select All Tasks>Export
  6. Export the certificate without the private key

Import the Certificate

  1. Locate your App in the AAD portal and go to Certificates and Secrets
  2. Click Upload Certificate
  3. Browse your exported certificate and import it

Grab the Thumbprint

While you have mmc open, double click your certificate and go to Details. Scroll down to the bottom and click on Thumbprint. Copy the thumbprint to a notepad (beside your Application ID you copied earlier) as you will need it later.

Add a Secret to the Application

For some services we will use certificate authentication, for others we will use secrets. As I do not want to have my secret in plain text in the script, I will upload it to Azure Keyvault instead. This section will cover creating the secret.

  1. Go to AAD>Your App>Certificates and Secrets
  2. Click +New Client Secret
  3. Enter a name and expiry for the secret. Then click Add
  4. Copy the secret to a notepad. It will disappear when you click of the screen (Now you should have Application ID, Certificate Thumbprint, and Application Secret in your notepad)

Create Azure Keyvault, Upload the Secret, Create the Access Policy

In this section we will cover creating the key vault and ensuring our service principal can access the secrets we have in here. In the example script, we will connect to Azure using certificate authentication. When connected to Azure, we will query Keyvault for the Application secret. I will then use that application secret to authenticate to MSGraph with client secret authention.

Create the Key Vault and Add the Access Policy

  1. Go to https://portal.azure.com/
  2. Search Key Vaults and click +Add
  3. Fill out the Basics blade. Copy the Key vault name to a notepad. (Now you should have Application ID, Certificate Thumbprint, Application Secret, and Key vault name in your notepad)
  4. Under the Access Policy blade, click +Add Access Policy
  5. Under Permissions check Get and List for the permissions
  6. Under service principal, click None selected and add your service principal. Then click Add.
  7. Continue to create your Keyvault.

NOTE: If you are using an existing Key vault, browse to it and select Access Policies. Add the access policy as per step 4,5,6 above. From the properties blade, copy your Key vault name to a notepad.

Upload the Secret to Key Vault

Copy your client secret from your notepad to your clipboard.

  1. Under Your Key vault>Secrets click +Generate/Import
  2. Leave Upload Options as Manual
  3. In the Name Field, give it a friendly name. Then copy it to the notepad. (Now you should have Application ID, Certificate Thumbprint, Application Secret, Key vault name, and Key vault secret name in your notepad)
  4. Optionally fill out the rest and click create.

Making the Script Work for You

Ok, great. You made it through the boring bits. Now to customize the script with your variables and be on your way to connect to different services and automate everything, right?

The details you had from your notepad should be used for the variables.

  • Application ID ($AppId)
  • Certificate Thumbprint ($CertThumbprint)
  • Key vault name ($KeyVaultName)
  • Key vault secret name ($KeyVaultSecret)
  • $MSTenant should be your Microsoft tenant. E.g. ‘dectur.com’
#Set Variables
$AppId              = ''
$CertThumbprint     = ''
$KeyVaultName       = '' 
$KeyVaultSecret     = ''
$MsTenant           = ''
$AuthUrl            = "https://login.microsoftonline.com/$MsTenant"

#Connect to Microsoft Azure (Az) 
Connect-AzAccount -ApplicationId $AppId -CertificateThumbprint $CertThumbprint -Tenant $MsTenant -ServicePrincipal

#Connect to Exchange Online
Connect-ExchangeOnline -AppId $AppId -CertificateThumbprint $CertThumbprint -Organization $MsTenant

#Connect to Azure AD
Connect-AzureAD -ApplicationId $AppId -CertificateThumbprint $CertThumbprint -TenantDomain $MsTenant

#Get the client secret from vault
$newSecret      = (Get-AzKeyVaultSecret -VaultName $KeyVaultName -Name $KeyVaultSecret).SecretValueText

#Update the MS Graph environment, then connect using the client secret
Update-MSGraphEnvironment -AppId $appID -AuthUrl $AuthUrl
Connect-MSGraph -ClientSecret $newSecret