Handling passwords with Azure Automation service accounts

Handling passwords with Azure Automation service accounts

Q. What is the best way to handle the password for an Azure Automation credential asset?

A. When using Azure Automation it is often required to connect to Azure subscriptions which requires an Azure AD credential to be used. As a best practice this credential should be dedicated to automation in a similar fashion to service accounts in Active Directory on-premises however by default Azure AD accounts must have their passwords changed every 90 days. There are a number of options to solve this.

The first option would be to configure the password to never expire which is a technique common for on-premises service accounts however this represents a potential security risk. http://windowsitpro.com/azure/configure-azure-ad-passwords-never-expire documents this approach.

A better option is to implement a process to automatically update the Azure AD account password and the credential in a similar fashion to how Managed Service Accounts (MSAs) work in Active Directory. I initially created something that would run on-premises in preparation for a process that would actually run in Azure Automation. This process used the Azure AD cmdlets and the sign-in assistant which is shown below. Note to avoid having to enter a credential it stores the actual password in an unencrypted form which is not desirable but my intention was to make it encrypted when I moved the process to Azure Automation. Note a variable asset must exist containing the subscription name (SubName), a credential asset (MyCred) that will be updated and an unencrypted string asset to store the password (MyCredPassword).

#The automation account is a co-admin and a global admin of the AD so it can change password. Could limit
$AutomationAccountName = 'SavAutomationEastUS2'
$SubName = Get-AzureAutomationVariable -AutomationAccountName $AutomationAccountName -Name 'SubName'

$AutoCred = Get-AzureAutomationCredential -AutomationAccountName $AutomationAccountName -Name MyCred

#Generate a new password
$randomObj = New-Object System.Random
$NewPassword=""
1..16 | ForEach { $NewPassword = $NewPassword + [char]$randomObj.next(33,126) }
$NewPassword

#Create credential to connected to Azure AD
$user = $autocred.UserName
$pworig = (Get-AzureAutomationVariable -Name MyCredPassword -AutomationAccountName $AutomationAccountName).Value
$pw = ConvertTo-SecureString $pworig -AsPlainText -Force
$AzureADCredential = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $user, $pw

Connect-MsolService -Credential $AzureADCredential

Set-MsolUserPassword –UserPrincipalName [email protected] –NewPassword $NewPassword -ForceChangePassword $false

$user = $autocred.UserName
$pw = ConvertTo-SecureString $NewPassword -AsPlainText -Force
$cred = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $user, $pw

Set-AzureAutomationCredential -Value $cred -Name $AutoCred.Name -AutomationAccountName $AutoCred.AutomationAccountName

#Save password right now in case we need it. What about encrypt. Assume will decrypt
Set-AzureAutomationVariable -AutomationAccountName $AutomationAccountName -Name MyCredPassword -Value $NewPassword -Encrypted $false 

To move to Azure Automation I had to make a change because the sign-in assistant is not available which is required to use the Azure AD cmdlets. I therefore changed the code to use some cmdlets that leverage the Graph API. Additionally the code to create a random password had to be modified as the existing random password code did not work in Azure Automation. Below is the Azure Automation code I created which you can then schedule to run weekly which will create a new password. Additionally the stored password (required to call the Graph API) is stored encrypted.

workflow UpdateAutomationAccount
{
    #The automation account is a co-admin and a global admin of the AD so it can change password. Could limit
    $AutomationAccountName = 'SavAutomationEastUS2'
    # Azure AD domain to authenticate against
    $AzureADDomain = Get-AutomationVariable -Name 'AzureADDomain'
      #Get the subscription name
    $SubName = Get-AutomationVariable -Name 'SubName'
    $MyCred = Get-AutomationPSCredential -Name 'MyCred'
    $pworig = $MyCred.GetNetworkCredential().Password
      Add-AzureAccount -Credential $MyCred
      Select-AzureSubscription -SubscriptionName $SubName
      $AutoCred = Get-AzureAutomationCredential -AutomationAccountName $AutomationAccountName -Name 'MyCred'
    $AutoUser = $AutoCred.UserName
      #Generate a new password that is 16 characters with at least 2 special
    $NewPassword = InlineScript {
    #Load "System.Web" assembly in PowerShell console 
    Add-Type -AssemblyName System.Web
    #Calling GeneratePassword Method 
    [System.Web.Security.Membership]::GeneratePassword(16,2)
    }

#Get string ready for JSON which fixes certain characters
    $JSONNewPassword = ConvertTo-Json $NewPassword

#Connect using Graph API which needs modules from https://github.com/tiander/OMSsearchAPI
    $APIVersion = "1.5"
    $AppIdURI = "https://graph.windows.net"
  
    # Set up connection object to pass into Invoke-AzureADMethod
    $ADConnection = @{"Username"=$AutoUser;"AzureADDomain"=$AzureADDomain;"Password"=$pworig;"APPIdURI"=$AppIdURI;"APIVersion"=$APIVersion}
    $URI = "https://graph.windows.net/$AzureADDomain/users/$AutoUser" 
    $Body =  @"
{
  "passwordProfile": {
    "password": $JSONNewPassword,
    "forceChangePasswordNextLogin": false
  }
}
"@ 
 
    #Update the Azure AD account password
    $UserUpdate = Invoke-AzureADMethod -URI $URI -Connection $ADConnection -Body $Body -Method PATCH
      #Create updated credential object for account with new password
    $NewSecurePassword = ConvertTo-SecureString $NewPassword -AsPlainText -Force
    $cred = New-Object –TypeName System.Management.Automation.PSCredential –ArgumentList $AutoUser, $NewSecurePassword
      #Update the automation credential object with new password
    Set-AzureAutomationCredential -Value $cred -Name $AutoCred.Name -AutomationAccountName $AutoCred.AutomationAccountName    
}

To use this code you must perform the following:

  1. Import the two Graph API modules from https://github.com/tiander/OMSsearchAPI into the Automation account
  2. You need the following assets created in the automation account:
    MyCred – Credential asset which is configured with the current automation account username and password and is the account that have its password updated
    SubName – String asset containing name of the Azure subscription
    AzureADDomain – String asset containing name of Azure AD instance to which the account belongs to
  3. The automation account will likely need to be a co-admin to perform its duties from runbooks and have the permission to change passwords in Azure AD
  4. Paste the above code into a runbook and change the automation account name at the start of the script. It can also be downloaded from the TechNet Gallery here which is where I'll keep it updated.
  5. Test the runbook by running as a test
  6. Schedule the runbook to run weekly

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish