Nuking all Azure Resource Groups under all Azure subscriptions

The code

You can copy the script below as a one-liner:

The Terraform code is also here:

Overview

In Microsoft Azure, Resource Groups are logical containers that are directly linked to their resources. If you want to delete your resources, just right click on the resource group poof! The resources are gone.

This is a breath of fresh air compared to the asset inventory, visibility, and cleanup nightmare in AWS. But it comes with its own security challenges.

Background

To understand how this exploit/mischief works, you’ll need to understand:

  1. Azure Resource Management Hierarchy: How Resource Groups and Subscriptions are tied to resources
  2. Azure Role Assignments: How privileged identities can have access across Azure subscriptions

Resource Management Hierarchy

Azure resource management hierarchy works like this:

Azure management groups, subscriptions, and resource groups hierarchy
Azure management groups, subscriptions, and resource groups hierarchy
  • Azure Tenant (not shown): This is similar to an AWS Organization. It is at a higher level than Management groups.
  • Management Groups: similar to AWS OUs. Hierarchy of grouping accounts).
  • Subscriptions: A subscription is a similar construct to an AWS Account. It can have multiple resource groups inside it.
  • Resource Groups: As mentioned before, these are containers that are directly linked to their resources. If you delete a resource group, you delete its resources too.

Azure Role Assignments

In AWS, we have the concept of “Cross Account Trust.” Essentially, you modify RoleA's Trust Policy in AccountA so that it allows RoleB from AccountB to “Assume” that role via sts:AssumeRole. When you make the sts:AssumeRole call, it returns a set of temporary credentials that you can then use against the target account.

Unfortunately, this becomes a complicated web/mesh/mess of trust.

AWS Cross-account Trust via sts:AssumeRole
AWS Cross-account Trust via sts:AssumeRole

Identities exist in one AWS account but do not exist in another. But at least sts:AssumeRole generates credentials that are scoped to a single AWS Account!

In Azure, all identities exist at the Active Directory level. Their permissions per subscription are based on role assignments. This is much cleaner, in my opinion. Rather than a a mesh/web/mess, it is a one-direction series of role assignments.

Azure Role Assignments
Azure Role Assignments

Azure Role Assignments can be scoped at these levels:

  1. Tenant level: This has the same effect across all management groups, all subscriptions, and all resource groups.
  2. Management group level: This has the same effect across all subscriptions, and all resource groups that are in this management group.
  3. Subscription level: This has the same effect across all resource groups that are within that subscription
  4. Resource group level: Only applies to that resource group.

Where can I nuke things

So, how does this information affect where you can apply this nuke-azure script?

Well, the most likely point of entry is CI/CD pipelines - specifically those that provision infrastructure.

Here’s a great example. You have a CI/CD pipeline to Azure that leverages Terraform. You have different pipeline jobs for different Azure environments. You originally intend for each of those jobs to be scoped to a specific subscription. But all of those jobs leverage ✨God-Mode Tenant-level credentials✨. In this case, it would be possible for a developer who has the ability to run Terraform on their own Azure subscription to run Terraform against other Azure subscriptions. That could cause this scenario:

CI/CD Pipeline scenario

Nuking Azure with Terraform’s null_resource and local-exec provisioner

Here’s the Terraform code:

The bash code does this:

  1. Gets a list of all Azure subscriptions
  2. For each subscription, list the resource groups
  3. For each resource group, delete the resource group
  4. Profit

You can test the above code by doing the following:

  • Create an Azure Test tenant. Don’t do this in production.
  • Create a subscription within that tenant
  • Create a few resource groups in that subscription
  • Then log in to Azure on command line using az login
  • Insert the Terraform mentioned above into a file called main.tf. Then, from within that directory, run these commands to apply the Terraform:
terraform init
terraform plan
terraform apply --auto-approve

Example output is below.

Example output of destroying infrastructure with Terraform
Example output of destroying infrastructure with Terraform

Conclusion

Infrastructure Provisioning and scheduling pipelines are some of the highest impact attack vectors in any organization. It is crucial to lock these down in a reasonable way that doesn’t stifle innovation but still prevents mass destruction. My recommendation in this case would be to limit the scope of the Infrastructure Provisioning Service Principals so that they are on a per-subscription level.

Kinnaird McQuade
Kinnaird McQuade
Lead Cloud Security Engineer

Always remove the french language pack: sudo rm -fr ./*

Previous