Import AzureAD app role assignments into Terraform state
Tags: azuread, terraform, snowflake, dataplatform
Published: 2023-12-30 18:07:35

This article shows how to import [AzureAD app role assignments](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/app_role_assignment) into the Terraform state. With app role assignments, AzureAD users, groups, or service principals are assigned a role in an application. [Source](https://learn.microsoft.com/en-us/graph/api/resources/approleassignment?view=graph-rest-1.0).

In the use case I'm writing about, AzureAD groups are assigned a role "User" in the AzureAD enterprise application for Snowflake, allowing members of those [AzureAD groups to single sign-on to Snowflake](https://community.snowflake.com/s/article/HOW-TO-Setup-SSO-with-Azure-AD-and-the-Snowflake-New-URL-Format-or-Privatelink).

## In short

- [The problem](#the-problem)
- [Get Microsoft Graph API token](#get-microsoft-graph-api-token)
- [List existing app role assignments using the Graph API](#list-existing-app-role-assignments-using-the-graph-api)
    - [Handling multiple assignments](#multiple-assignments)
- [Import existing app role assignments into the Terraform state](#import-existing-app-role-assignments-into-the-terraform-state)
- [Conclusion](#conclusion)

## Context

Creating and managing a data platform involves managing its infrastructure and user access among other things.  In the data platform my team is managing, we use Snowflake for analytical data warehousing and user access is managed from [AzureAD](https://learn.microsoft.com/en-us/entra/identity/saas-apps/snowflake-tutorial) (Microsoft Entra).

This setup allows us to use existing AzureAD users and groups and grant them access to roles inside of Snowflake. In short, adding a group named "ECOM_SALES" will allow any member of that group to log in to Snowflake and use a [functional role](https://docs.snowflake.com/en/user-guide/security-access-control-considerations#aligning-object-access-with-business-functions) named `ECOM_SALES`.

Below is a screenshot of the Snowflake enterprise application showing multiple groups assigned the role "User":

![Snowflake enterprise application showing added groups](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b97b7a45wyocgm098sne.png)

Being strong believers in infrastructure-as-code, my team manages the entire platform with Terraform. However, during the very early days, some changes were done manually, such as adding AzureAD groups to the enterprise application for Snowflake single sign-on.

Access management is an important part of the overall platform security and as such should be managed through code. Manual configuration can be overlooked during migrations, it does not follow the [four-eyes principle](https://www.unido.org/overview/member-states/change-management/faq/what-four-eyes-principle) which can cause outages and does not provide a stable foundation for a data platform.

## The problem

With the understanding of why we want to manage all AzureAD app role assignments with Terraform, let's see how those added manually can be imported and managed with Terraform.

[The docs](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/app_role_assignment) state that app role assignments are imported with:

```
terraform import \
  azuread_app_role_assignment.example \
  00000000-0000-0000-0000-000000000000/appRoleAssignment/aaBBcDDeFG6h5JKLMN2PQrrssTTUUvWWxxxxxyyyzzz
```

where:

- `00000000-0000-0000-0000-000000000000` is the object ID of the service principal object associated with your AzureAD enterprise application for Snowflake.
- `aaBBcDDeFG6h5JKLMN2PQrrssTTUUvWWxxxxxyyyzzz` is the app role assignment ID.

I had absolutely no idea where to find the real value of `aaBBcDDeFG6h5JKLMN2PQrrssTTUUvWWxxxxxyyyzzz` in the UI.

Since Terraform providers communicate with 3rd party APIs (AWS, AzureAD etc...) I figured I could do the same.

## Get Microsoft Graph API token

Microsoft's [Graph API](https://learn.microsoft.com/en-us/graph/use-the-api) allows programmatic access to the Microsoft Cloud service resources.

To use it, you must authenticate and obtain a [JWT](https://jwt.io/):

```
TOKEN=$(curl -d grant_type=client_credentials \
  -d client_id=$CLIENT_ID \
  -d client_secret=$CLIENT_SECRET \
  -d scope=https://graph.microsoft.com/.default \
  -d resource=https://graph.microsoft.com  \
  https://login.microsoftonline.com/$TENANT_ID/oauth2/token  |
  jq -j .access_token)
```

Of course, export `CLIENT_ID`, `CLIENT_SECRET` and `TENANT_ID` first.

## List existing app role assignments using the Graph API

Having the JWT in the `TOKEN` variable, we can list the existing app role assignment for group `ECOM_SALES` in the AzureAD enterprise application for Snowflake with ID `1ab2c3de-f456-7890-fghi-j12345k67lm8`:

```
ASSIGNMENT_ID=$(curl -sX GET \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  https://graph.microsoft.com/v1.0/servicePrincipals/1ab2c3de-f456-7890-fghi-j12345k67lm8/appRoleAssignedTo |
  jq '.value[] | select(.principalDisplayName =="ECOM_SALES").id')
```

The API returns:

```
{
    "@odata.context": "LINK_HERE",
    "@odata.nextLink": "LINK_HERE",
    "value": [
        {
            "id": "ZMW3ujNOGUuENMLa2k6-mVWFENfBHbVDlwQJg1ui848",
            "deletedDateTime": null,
            "appRoleId": "33c0d484-efdc-4e65-b6fc-470f4bcb4f46",
            "createdDateTime": "2023-02-17T08:48:17.1803369Z",
            "principalDisplayName": "ECOM_SALES",
            "principalId": "bab7c564-4e33-4b19-8434-c2dada4ebe99",
            "principalType": "Group",
            "resourceDisplayName": "Snowflake AAD",
            "resourceId": "1ab2c3de-f456-7890-fghi-j12345k67lm8"
        }
    ]
}
```

and our variable `ASSIGNMENT_ID` has the value `ZMW3ujNOGUuENMLa2k6-mVWFENfBHbVDlwQJg1ui848`.

With this value we now have all the elements necessary to import this app role assignment into the Terraform state.

### Multiple assignments

In a situation where you have multiple AzureAD app role assignments for groups such as `ECOM_SALES`, `ECOM_FINANCE` and `ECOM_LEGAL`, you can use the following `jq` expression to list them all:

```
.value[] | select(.principalDisplayName | startswith("ECOM_"))
```

## Import existing app role assignments into the Terraform state

Having the app role assignment ID in a variable named `ASSIGNMENT_ID` and the AzureAD enterprise application for Snowflake having ID `1ab2c3de-f456-7890-fghi-j12345k67lm8`, we can:

```
terraform import \
  azuread_app_role_assignment.example \
  1ab2c3de-f456-7890-fghi-j12345k67lm8/appRoleAssignment/$ASSIGNMENT_ID
```

## Conclusion

This article provided a quick solution for importing Azure AD app role assignments into Terraform, addressing the challenge of manual interventions in infrastructure management. Leveraging Microsoft's Graph API and Terraform, we demonstrated a streamlined process to list and import app role assignments.