Authentication Options for Automated Azure PowerShell Scripts, Part 1: Service Account vs. App Registration

Automation is a fundamental requirement for good systems administration, no matter what the platform. Being able to automate tasks ensures consistency and prevents mistakes caused by forgetfulness or by simply mistyping or mis-clicking—aka “fat-finger errors.”  

In the Microsoft Azure world, PowerShell has long been the automation tool of choice for administrators coming from a Windows background. In some cases, PowerShell has been the only tool to accomplish certain tasks because the deployment of new capabilities in Azure often exceeds the pace of updating the Azure Portal. Experienced Azure administrators are likely to have a repository of useful scripts to accomplish anything from managing license assignments to automating complicated virtual machine (VM) deployments. 

When these scripts are run interactively, the administrator can enter their password directly and respond to multi-factor authentication (MFA) prompts. From an authentication perspective, running such a script isn’t any less secure than the administrator performing the actions manually. However, when the scripts are automated (e.g., run as a scheduled job), things become more complicated. 

Service Accounts

Most Azure PowerShell modules support automation by allowing the script to authenticate as a user account using a PSCredential object to pass the user ID and password. A common pattern for administrators is to create a regular Azure account as a “service account” for use with the script, assign it the requisite permissions, then authenticate as the service account in the context of the script. This approach is illustrated in the following code and in Figure 1. 

$SecureString = ConvertTo-SecureString -String "SuperSecretPasswordShh!" -AsPlainText -Force $Credential = New-Object System.Management.Automation.PSCredential "script@foobarqux.com",$SecureString Connect-AzureAD -Credential $Credential | Select-Object Account, TenantDomain Get-AzureADUser -SearchString script | Select-Object DisplayName $Credential.GetNetworkCredential().Password
Code language: PowerShell (powershell)
Figure 1 Connecting with a credential object

There are problems with this approach, however: 

  • To create the PSCredential object, the script needs access to the service account password, which should be stored in an encrypted format (unlike the clear text of the example above). Unfortunately, there is no way to require a script to store the password in an encrypted format, and an administrator may choose to use plaintext out of expediency or ignorance. A plaintext password can be retrieved with just simple file system access to the script or associated storage and introduces other vectors for the credential to leak (e.g., backups that aren’t adequately secured). 
  • Even if the password is stored in an encrypted format, it must be accessible and decryptable by the script. In the example, the password is trivially retrievable in the script directly from the PSCredential object—but there are other avenues external to the script whereby an attacker might obtain the password, depending on the storage and encryption methods used. 
  • The service account must be excluded from MFA requirements, since there is currently no way to satisfy MFA from an automated script.
    • This creates an additional administrative burden on the accounts used for script automation and puts them at risk if the credentials are leaked. This can be partially mitigated for external attack by restricting the account to access only from specific public IP addresses, but this approach again creates an additional administrative burden
  • Password-only authentication without MFA is more vulnerable to issues such as password reuse or weak passwords if good password policies aren’t implemented.
  • Administrators may be tempted to use a known service account password to bypass controls such as Privileged Identity Management (PIM) and MFA unless controls are put in place.

App Registrations and Certificates: A Better Way?

An app registration in Azure is much like an application service account in Active Directory (AD). It can be permitted to directly perform specific operations, or it can be enabled to provide delegated access to Azure resources on behalf of the user accessing the application—much as a service account in AD can leverage Kerberos Constrained Delegation (KDC) to impersonate a user to a specific resource.

When authenticating directly as an app registration, there are two supported mechanisms:

  1. Client secret
  2. Certificate

Client secrets are essentially just very strong passwords. As such, they have many of the same storage and management issues that passwords have in automation.

Certificate-based authentication uses a shared certificate model; the client and the app registration are both configured with the same certificate and consequently can use a self-signed certificate. There is no need to implement a public key infrastructure (PKI) or purchase commercial certificates, which makes the use of certificate-based authentication only marginally more complicated than client secrets. The big advantage of certificate-based authentication is that Windows has built-in certificate handling, which removes the need for the script developer to create their own credential management code.

Microsoft has added the capability to authenticate to Azure in PowerShell modules with an app registration instead of a user or service account. In most cases, only certificate authentication is supported; this is presumably because of the advantages of certificate-based authentication over client secret.

Using an app registration to authenticate to Azure with a certificate is illustrated in the following code and in Figure 2.

Connect-AzureAD -TenantId foobarqux.onmicrosoft.com -CertificateThumbprint 96A597451DD34F7421C2C668BF103EF0825034CB -ApplicationId 0372619b-44ad-4814-adf9-b5a9d767dae0 | Select-Object Account, TenantDomain Get-AzureADUser -SearchString script | Select-Object DisplayName $password = ConvertTo-SecureString -String "PFXFilePassword" -Force -AsPlainText Export-PfxCertificate -Password $password -cert Cert:\CurrentUser\My\96A597451DD34F7421C2C668BF103EF0825034CB -FilePath export.pfx
Code language: PowerShell (powershell)
Figure 2 Connecting with a certificate

Compared to the previous example of using a service account user, using an app registration offers the following advantages:

  • The connection command needs the tenant ID, the app registration ID, and the thumbprint of the certificate used to authenticate to the app registration. None of this information is considered secret and is useless without both the certificate and its private key, so there is no need to manage any confidential information in the script itself.
  • If the certificate’s private key was marked as non-exportable when the certificate was installed, then the private key can’t be simply exported using standard Windows tools. This is illustrated in the example above by attempting to use the Export-PfxCertificate command, which would include the private key. These protections on the private key can be circumvented with administrative access to the script host, but they do protect the credential from loss within the script itself.
  • An administrator can’t use the certificate credential to bypass PIM or MFA requirements for access to the Azure Portal or other web-based Azure resources.
  • App registrations don’t currently trigger conditional access policies and therefore don’t need management to exclude them from MFA requirements.

There are some disadvantages relative to the service account approach, however:

  • Because app registration sign-ins don’t trigger conditional access policies, the script can’t be constrained to known public IP addresses.
    • If the certificate and private key are leaked, they can be used freely.
    • This can be mitigated with monitoring of the app registration sign-ins to at least alert when used from an unknown IP address.
  • Not all PowerShell modules currently support using app registrations and certificates (e.g., MSOnline) for authentications.
    • This can be mitigated in some cases by using alternative modules that do support app registrations (e.g., Microsoft.Graph).

Should You Use App Registration?

For automated scripts, yes.

If your script can be converted to work with app registration authentication using certificates, that’s a far better solution for automated scripts than service accounts and passwords—even with the inability to lock it down to a known IP address. Removing the need to manage credentials in a script makes the script developer’s job easier and increases the script’s security essentially for free.

Microsoft is constantly updating the Azure-related PowerShell modules to support app registration authentication, so the number of scripts that can be converted should increase as time goes on. Many of the major modules have already been updated, including:

  • Az
  • AzureAD
  • ExchangeOnlineManagement
  • Microsoft.Graph

The process of creating an app registration and assigning a certificate and permissions may be unfamiliar to many administrators, but as Part 2 of this blog series will demonstrate, it isn’t inherently more complicated or difficult to support once you understand the concepts.

In the meantime, if you need help with automating your PowerShell scripts or managing authentication – contact the experts at Ravenswood Technology.