Using Azure Resource Locks with Terraform

Azure Resource Locks can be used to protect your resources against accidental deletion. Their creation can be built into your Terraform workflow so they are deployed at build time and your resources are protected right from the start.

Code

The following Terraform will create an Azure Resource Group protected by a “CanNotDelete” Resource Lock. Any resources in this resource group (and the resource group itself) cannot be removed without first removing the lock.

 1# Create a Delete Lock on the Resource group
 2resource "azurerm_management_lock" "rglock" {
 3  name       = "resourcegrouplock"
 4  scope      = azurerm_resource_group.rsg.id
 5  lock_level = "CanNotDelete"
 6  notes      = "Important Stuff. Do Not Delete"
 7}
 8
 9# Configure the Azure provider
10terraform {
11  required_providers {
12    azurerm = {
13      source  = "hashicorp/azurerm"
14      version = ">= 3.7.0, < 4.0.0"
15    }
16  }
17  required_version = ">= 1.6.0"
18
19}
20provider "azurerm" {
21  skip_provider_registration = "true"
22  features {}
23}
24
25# Create a resource group
26resource "azurerm_resource_group" "rsg" {
27  location = "eastus"
28  name     = "resourcelocksterraformtest"
29}

Note that in the code sample the block for the Resource Lock is at the top, which is unconventional but makes this example clearer to read.

When invoking terraform destroy to remove the deployed resources, the provider is intelligent enough to understand the dependency tree and that the lock needs removing before the other resources it applies to can be tidied up.

Permissions

You can create an Azure resource lock with Terraform, but as with any other method you need the correct permissions to do so, and the built-in “Contributor” role is not enough. Remember that when working with code, you don’t magically gain extra permissions that you wouldn’t have in the Azure Portal.

Without this permission terraform apply throws the following error (your exact resource names may differ if you haven’t used the template above)

1Error: creating Scoped Lock (Scope: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcelocksterraformtest"
2│ Lock Name: "resourcegrouplock"): unexpected status 403 (403 Forbidden)
3with error: AuthorizationFailed: The client 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
4with object id 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' does not have 
5authorization to perform action 'Microsoft.Authorization/locks/write' 
6over scope '/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/resourcelocksterraformtest/providers/Microsoft.Authorization/locks/resourcegrouplock'
7or the scope is invalid. If access was recently granted, please refresh
8your credentials.

The Microsoft documentation at learn.microsoft.com tells us that:

you need access to Microsoft.Authorization/* or Microsoft.Authorization/locks/* actions. Users assigned to the Owner and the User Access Administrator roles have the required access.

We can create a custom role with the Read/ Write/ Delete permissions provided for Microsoft.Authorization/locks/* and assign it to the User/ Service Principal running the Terraform. This is what that looks like in the portal- if you have permissions to do that in Terraform then you probably have permissions to work with the locks already!

A Custom Role can be created from the RBAC blade.

And the permissions to assign to the role are those under Microsoft.Authorization/locks
The new role should be assigned to the user/service principal being used for Terraform, on this subscription (or another suitable scope).

If you want to grant your pipeline the rights to create, but not remove, resource locks then a custom role with the Read and Write permissions, but not Delete can be created. With this “Lock Creator” role the assigned user can run terraform apply to deploy the above template, but terraform destroy will fail with the message:

1Error: deleting Scoped Lock