Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 224 additions & 0 deletions cloud-access/azure-rbac.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
---
title: 'Azure RBAC Custom Roles'
description: 'How to set up Azure custom roles for OpenOps Benchmark using RBAC'
icon: 'microsoft'
---

import JoinCommunity from '/snippets/join-community.mdx'

OpenOps provides Azure custom role definitions to create RBAC roles in your Azure subscriptions with the necessary permissions to run benchmark assessments and collect cost optimization data.

## Available Custom Roles

### OpenOps Azure Benchmark Reader

Creates the `OpenOps Azure Benchmark Reader` custom role with read-only permissions for running Azure cost optimization benchmarks. This role includes:

* **Compute resources**: Virtual machines, managed disks, snapshots, and images
* **Networking**: Network interfaces and public IP addresses
* **App Services**: Web apps, App Service Plans, and App Service Environments
* **Databases**: Azure SQL servers, databases, and elastic pools
* **Cost and billing**: Cost Management queries, consumption usage details, and billing properties
* **Monitoring**: Azure Monitor metrics
* **Optimization**: Azure Advisor recommendations and metadata

**[Download Bicep template](https://openops.s3.us-east-2.amazonaws.com/OpenOpsAzureBenchmarkReader.role-definition.bicep)** | **[Download JSON template](https://openops.s3.us-east-2.amazonaws.com/OpenOpsAzureBenchmarkReader.role-definition.json)**

**Parameters:** AssignableScopes (required)

## Installation Steps

Before creating the custom role, you must configure the `AssignableScopes` parameter to specify where this role can be assigned.

### Configure AssignableScopes

Choose the appropriate scope for your deployment:

<AccordionGroup>
<Accordion title="Single subscription">
**JSON:**
```json
"AssignableScopes": [
"/subscriptions/11111111-1111-1111-1111-111111111111"
]
```

**Bicep parameter:**
```bash
--parameters assignableScopes='["/subscriptions/11111111-1111-1111-1111-111111111111"]'
```
</Accordion>

<Accordion title="Multiple subscriptions">
Use this when you want the role available in specific subscriptions but not across the entire management group.

**JSON:**
```json
"AssignableScopes": [
"/subscriptions/11111111-1111-1111-1111-111111111111",
"/subscriptions/22222222-2222-2222-2222-222222222222",
"/subscriptions/33333333-3333-3333-3333-333333333333"
]
```

**Bicep parameter:**
```bash
--parameters assignableScopes='["/subscriptions/11111111-1111-1111-1111-111111111111","/subscriptions/22222222-2222-2222-2222-222222222222","/subscriptions/33333333-3333-3333-3333-333333333333"]'
```
</Accordion>

<Accordion title="Management group" id="management-group-scope">
Use this only if you want the custom role available across the entire management group scope.

**JSON:**
```json
"AssignableScopes": [
"/providers/Microsoft.Management/managementGroups/my-management-group"
]
```

**Bicep parameter:**
```bash
--parameters assignableScopes='["/providers/Microsoft.Management/managementGroups/my-management-group"]'
```
</Accordion>
</AccordionGroup>

### Option 1: Deploy with Bicep (Recommended)

1. Update the `assignableScopes` parameter in the command below with your subscription ID(s):

```bash
az deployment sub create \
--name openops-azure-benchmark-reader-role \
--location westus2 \
--template-file "./OpenOps Azure Benchmark Reader.role-definition.bicep" \
--parameters assignableScopes='["/subscriptions/<subscription-id>"]'
```

2. For multiple subscriptions:

```bash
az deployment sub create \
--name openops-azure-benchmark-reader-role \
--location westus2 \
--template-file "./OpenOps Azure Benchmark Reader.role-definition.bicep" \
--parameters assignableScopes='["/subscriptions/sub-1","/subscriptions/sub-2"]'
```

**Notes:**
* This is a subscription-scoped deployment. For management group scope, see the [management group configuration](#management-group-scope) above

### Option 2: Deploy with Azure CLI and JSON

1. Edit the `AssignableScopes` field in `OpenOps Azure Benchmark Reader.role-definition.json` with your subscription ID(s).

2. Create the role:

```bash
az role definition create \
--role-definition "./OpenOps Azure Benchmark Reader.role-definition.json"
```

3. To update an existing role:

```bash
az role definition update \
--role-definition "./OpenOps Azure Benchmark Reader.role-definition.json"
```

## Assign the Role to a Service Principal

After creating the custom role, assign it to the service principal that OpenOps uses to connect to Azure.

### Assign at subscription scope

```bash
az role assignment create \
--assignee-object-id "<service-principal-object-id>" \
--assignee-principal-type ServicePrincipal \
--role "OpenOps Azure Benchmark Reader" \
--scope "/subscriptions/11111111-1111-1111-1111-111111111111"
```

### Assign in multiple subscriptions

Run the assignment once per subscription:

```bash
az role assignment create \
--assignee-object-id "<service-principal-object-id>" \
--assignee-principal-type ServicePrincipal \
--role "OpenOps Azure Benchmark Reader" \
--scope "/subscriptions/11111111-1111-1111-1111-111111111111"
```

```bash
az role assignment create \
--assignee-object-id "<service-principal-object-id>" \
--assignee-principal-type ServicePrincipal \
--role "OpenOps Azure Benchmark Reader" \
--scope "/subscriptions/22222222-2222-2222-2222-222222222222"
```

### Assign at management group scope

```bash
az role assignment create \
--assignee-object-id "<service-principal-object-id>" \
--assignee-principal-type ServicePrincipal \
--role "OpenOps Azure Benchmark Reader" \
--scope "/providers/Microsoft.Management/managementGroups/my-management-group"
```

## Verification

Verify the role definition was created:

```bash
az role definition list \
--name "OpenOps Azure Benchmark Reader" \
-o json
```

Check role assignments for your service principal:

```bash
az role assignment list \
--assignee "<service-principal-object-id>" \
--all \
-o table
```

## Cost Management Access

For subscription-scope cost queries, the role includes `Microsoft.CostManagement/query/action` for the POST query API.

Azure Cost Management access commonly requires related read permissions in billing and cost surfaces. This role includes:

* `Microsoft.CostManagement/query/action`
* `Microsoft.CostManagement/*/read`
* `Microsoft.Consumption/*/read`
* `Microsoft.Billing/billingPeriods/read`
* `Microsoft.Billing/billingProperty/read`
* `Microsoft.Resources/subscriptions/read`
* `Microsoft.Resources/subscriptions/resourceGroups/read`

<Note>
If you update the role and still get `RBACAccessDenied`, wait a few minutes and retry. Azure RBAC propagation is not always immediate.

For subscriptions under EA or certain billing setups, cost visibility may also depend on billing-side settings such as view charges access.
</Note>

## Important Notes

* `AssignableScopes` is required for all Azure custom roles
* A management group is above subscriptions in the Azure hierarchy
* If you use multiple subscriptions in `AssignableScopes`, the role is limited to those subscriptions only
* Role creation requires permission to manage custom roles for every scope listed in `AssignableScopes`

## Modification

You're welcome to download and modify the role definition according to your needs. Note that some OpenOps benchmark workflows may not function properly if required permissions are removed.

<JoinCommunity />
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
targetScope = 'subscription'

@description('Scopes where this custom role can be assigned. Defaults to the current subscription.')
param assignableScopes array = [
subscription().id
]

var roleDefinitionGuid = '97fd4ee5-cfe4-4d11-a798-2d9d8a4f153f'
var roleName = 'OpenOps Azure Benchmark Reader'
var roleDescription = 'Read-only benchmark, cost, and Azure Advisor recommendation role for OpenOps Azure Benchmark.'
var actions = [
'Microsoft.Advisor/metadata/read'
'Microsoft.Advisor/recommendations/read'
'Microsoft.Billing/billingPeriods/read'
'Microsoft.Billing/billingProperty/read'
'Microsoft.Compute/disks/read'
'Microsoft.Compute/images/read'
'Microsoft.Compute/snapshots/read'
'Microsoft.Compute/virtualMachines/read'
'Microsoft.Consumption/*/read'
'Microsoft.CostManagement/*/read'
'Microsoft.CostManagement/query/action'
'Microsoft.Insights/metrics/read'
'Microsoft.Network/networkInterfaces/read'
'Microsoft.Network/publicIPAddresses/read'
'Microsoft.Resources/subscriptions/read'
'Microsoft.Resources/subscriptions/resourceGroups/read'
'Microsoft.Sql/servers/databases/read'
'Microsoft.Sql/servers/elasticPools/read'
'Microsoft.Sql/servers/read'
'Microsoft.Web/hostingEnvironments/read'
'Microsoft.Web/serverfarms/read'
'Microsoft.Web/sites/read'
]

resource customRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' = {
name: roleDefinitionGuid
properties: {
roleName: roleName
description: roleDescription
type: 'CustomRole'
permissions: [
{
actions: actions
notActions: []
dataActions: []
notDataActions: []
}
]
assignableScopes: assignableScopes
}
}

output roleDefinitionId string = roleDefinitionGuid
output roleDefinitionResourceId string = customRole.id
output roleDefinitionName string = customRole.properties.roleName
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"Name": "OpenOps Azure Benchmark Reader",
"IsCustom": true,
"Description": "Read-only benchmark, cost, and Azure Advisor recommendation role for OpenOps Azure Benchmark.",
"Actions": [
"Microsoft.Advisor/metadata/read",
"Microsoft.Advisor/recommendations/read",
"Microsoft.Billing/billingPeriods/read",
"Microsoft.Billing/billingProperty/read",
"Microsoft.Compute/disks/read",
"Microsoft.Compute/images/read",
"Microsoft.Compute/snapshots/read",
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Consumption/*/read",
"Microsoft.CostManagement/*/read",
"Microsoft.CostManagement/query/action",
"Microsoft.Insights/metrics/read",
"Microsoft.Network/networkInterfaces/read",
"Microsoft.Network/publicIPAddresses/read",
"Microsoft.Resources/subscriptions/read",
"Microsoft.Resources/subscriptions/resourceGroups/read",
"Microsoft.Sql/servers/databases/read",
"Microsoft.Sql/servers/elasticPools/read",
"Microsoft.Sql/servers/read",
"Microsoft.Web/hostingEnvironments/read",
"Microsoft.Web/serverfarms/read",
"Microsoft.Web/sites/read"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/subscriptions/<subscription-id>"
]
}
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
"pages": [
"cloud-access/supported-cloud-providers",
"cloud-access/access-levels-permissions",
"cloud-access/aws-cf-role-stack"
"cloud-access/aws-cf-role-stack",
"cloud-access/azure-rbac"
]
},
{
Expand Down
3 changes: 3 additions & 0 deletions finops-benchmark/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import JoinCommunity from '/snippets/join-community.mdx'
<Note>
For **AWS**, OpenOps must be able to assume a role that includes the **read-only permissions** defined in the Benchmark CloudFormation stack. Use the link beside this step in the wizard to open the stack details. If that role is missing or incomplete, benchmark workflows can fail. For installation steps and templates, see [AWS CloudFormation role stacks](/cloud-access/aws-cf-role-stack).
</Note>
<Note>
For **Azure**, OpenOps must be able to use a service principal with the **OpenOps Azure Benchmark Reader** custom role assigned at the appropriate scope (subscription or management group). This role includes read-only permissions for compute, networking, databases, cost management, and Azure Advisor. If the role is not assigned or lacks required permissions, benchmark workflows can fail. For installation steps and role definitions, see [Azure RBAC custom roles](/cloud-access/azure-rbac).
</Note>

4. Select **accounts or subscriptions** and **regions** to include.
![Benchmark wizard — accounts and regions](/images/benchmark-wizard-regions.png)
Expand Down