Automatically delete resources that are older than 30 days utilizing Azure Automation

Have you ever wondered how to keep your Azure test environment clean and avoid unnecessary costs? If you have resources that are created and used for testing purposes, you may want to delete them automatically after a certain period using Azure Automation. This way, you can free up space and resources for new tests and save money on your Azure bill.

This can be achieved by using Azure Policy and Azure Automation. The idea is to use Azure Policy to add a CreatedDate tag to every new resource that is created in a specific subscription. The tags value will store the date and time of when the resource was created. Then, we will use a PowerShell script on Azure Runbook to check the tag and delete resources that are older than 30 days. This script will run periodically on a schedule or demand.

By using this approach, you can ensure that your test environment is always clean and up to date. You can also customize the script and the policy to suit your needs. For example, you can change the retention period or exclude certain resources from deletion.

In the following sections, I will explain how to create and apply an Azure Policy to automatically create a CreatedDate tag, how to write and run a PowerShell script that will use the tag date and delete any resource older than 30 days, and how to add this script to Azure Runbook so that it can run on a schedule.

 

Requirements

  • An admin account with permission to create a custom policy and an automation account
  • An automation account to use Azure runbooks

Azure policy:

Azure Policy is a service that allows you to create and assign policies to your Azure resources. Policies can enforce rules, evaluate compliance, and perform actions on your resources. In this  scenario, we will utilize the following custom policy:

 

				
					{ 
  "mode": "All", 
  "policyRule": { 
    "if": { 
      "allOf": [ 
        { 
          "field": "tags['CreatedDate']", 
          "exists": false 
        } 
      ] 
    }, 
    "then": { 
      "effect": "modify", 
      "details": { 
        "operations": [ 
          { 
            "operation": "add", 
            "field": "tags['CreatedDate']", 
            "value": "[concat(substring(utcNow(), 5, 2), '/', substring(utcNow(), 8, 2), '/', substring(utcNow(), 0, 4), '-', substring(utcNow(), 11, 8))]" 
          } 
        ], 
        "roleDefinitionIds": [ 
          "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608" 
        ] 
      } 
    } 
  }, 
  "parameters": {} 
} 
				
			

 

The above policy will add a tag named CreatedDate with the current date and time in UTC as its value to any resource that does not have this tag already. This policy requires the DevTest Labs User role to execute.

The name of the tag and the time zone can also be altered. For example, if you want the tag to be in CEST you can change the value to:

 

				
					"[formatDateTime(convertTimeZone(utcNow(), 'UTC', 'Central Europe Standard Time'), 'dd/MM/yyyy-HH:mm:ss')]" 
				
			

 

Once you have the custom policy ready head to the Azure portal and sign in with an admin account.

  1. Search for and select Policy from the search box.
  2. Select Definitions on the left side of the Azure Policy page.
  3. Select + Policy definition from the top of the Policy – Definitions page. This will open a new page where you can create your custom policy definition.
  4. On the Basics tab, enter a name and description for your policy definition. You can also choose a category and metadata for your policy definition.
  5. On the Rules tab, enter the JSON code for your policy definition.
  6. On the Review + Create tab, review your policy definition and select Create to save it.
  7. Go back to the Policy – Definitions page and select your newly created policy definition.
  8. Select Assign from the top of the Policy Definition page. And assign the policy to the scope you desire.

 

Once the policy is assigned and a resource is created the resources should automatically have a CreatedDate tag like the one below:

 

PowerShell

Now that the policy is in place, let’s use PowerShell to create a code that uses the CreatedDate tag to check and delete resources older than 30 days. The code looks like this:

 

				
					# Define the subscription id and the tag name 
$subId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx" 
$tagName = "CreatedDate" 

# Authenticate using the system-assigned managed identity 
Connect-AzAccount -Identity 

# Select the subscription 
Select-AzSubscription -SubscriptionId $subId 

# Get all the resource groups in the subscription 
$rgs = Get-AzResourceGroup 

# Loop through each resource group and get the resources in it 
foreach ($rg in $rgs) { 
  $resources = Get-AzResource -ResourceGroupName $rg.ResourceGroupName 

 # Loop through each resource and check the tag value 
  foreach ($resource in $resources) { 

 # Get the tag value for the current resource 
    $tagValue = $resource.Tags[$tagName] 

 # If the tag value is not null or empty, parse it as a date 
    if (![string]::IsNullOrEmpty($tagValue)) { 

 # Parse the tag value as a date in mm/dd/yyyy format 
      $createdDate = [datetime]::ParseExact($tagValue, "MM/dd/yyyy-HH:mm:ss", $null) 

 # Calculate the difference between the current date and the created date in days 
      $daysOld = (Get-Date) - $createdDate | Select-Object -ExpandProperty Days 

 # If the difference is greater than or equal to 30, delete the resource 
      if ($daysOld -ge 30) { 
        Write-Host "Deleting resource $($resource.Name) with tag value $tagValue" 
        Remove-AzResource -ResourceId $resource.ResourceId -Force 
      } 
    } 
  } 
} 
				
			

 

Be sure to change the Subscription ID (highlighted in green) to your subscription. Below is a brief explanation of how the code works:

  1. We start by defining two variables, this being the $subID and $tagname
  2. Then it looks through the resource groups inside the defined subscription and checks all the resources inside for the CreatedDate tag
  3. if the tag exists It gets the tag value for the current resource by accessing the Tags property
  4. It checks if the tag value is not null or empty by using the [string]::IsNullOrEmpty()
  5. It calculates the difference between the current date and the created date. If the difference is greater than or equal to 30 It deletes the resource by using the Remove-AzResource cmdlet

 

To test the script, you can create a test resource and change its tag date. The script should delete any resource older than 30 days. You can also adjust the date to be shorter or longer than 30 days.

 

Azure Runbook

Automation is a powerful way to improve the efficiency, reliability, and consistency of your cloud operation. Through Azure automation, we can make frequent time-consuming tasks quick and with little to no management needed.

To automatically delete resources that are older than 30 days we will take the previous script and add it to a runbook, for this to run properly a minor modification will be needed.

For the connection we will utilize the system-assigned managed identity of your Azure Automation account for this we will add the Connect-AzAccount -Identity command into the script, this will allow runbook to easily access other Azure resources without storing any credentials in the code.

Below is the code that you can copy and paste only needing to alter the subscription ID that is highlighted in yellow:

 

				
					# Define the subscription id and the tag name 
$subId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx" 
$tagName = "CreatedDate" 

# Authenticate using the system-assigned managed identity 
Connect-AzAccount -Identity 

# Select the subscription 
Select-AzSubscription -SubscriptionId $subId 

# Get all the resource groups in the subscription 
$rgs = Get-AzResourceGroup 

# Loop through each resource group and get the resources in it 
foreach ($rg in $rgs) { 
  $resources = Get-AzResource -ResourceGroupName $rg.ResourceGroupName 

 # Loop through each resource and check the tag value 
  foreach ($resource in $resources) { 

 # Get the tag value for the current resource 
    $tagValue = $resource.Tags[$tagName] 

 # If the tag value is not null or empty, parse it as a date 
    if (![string]::IsNullOrEmpty($tagValue)) { 

 # Parse the tag value as a date in mm/dd/yyyy format 
      $createdDate = [datetime]::ParseExact($tagValue, "MM/dd/yyyy-HH:mm:ss", $null) 

 # Calculate the difference between the current date and the created date in days 
      $daysOld = (Get-Date) - $createdDate | Select-Object -ExpandProperty Days 

 # If the difference is greater than or equal to 30, delete the resource 
      if ($daysOld -ge 30) { 
        Write-Host "Deleting resource $($resource.Name) with tag value $tagValue" 
        Remove-AzResource -ResourceId $resource.ResourceId -Force 
      } 
    } 
  } 
} 
				
			

 

To link the runbook to a schedule, select the runbook and then select Link to a schedule in the overview blade.

Here you can choose the time and frequency for the runbook to delete the resources. Select a time when the resources are not in use and set the recurrence to Recurring like the image below:

After creating and running the script in your environment, it will check the tag values daily or at your preferred frequency. It will then calculate if the resource is older than 30 days and remove it if needed.

 

Conclusion

In this blog, you learned how to manage your Azure resources more efficiently by using tags and automation. You set up a policy that automatically adds a CreatedDate tag to all resources in a specific scope. You also learned how to use PowerShell to manually delete old resources based on their tags.

Finally, you automated this process by creating and running a script with Azure Automation. This way, you can save time and money by keeping your Azure environment clean and organized.

If you want to know more about this, contact our team of Azure experts.

Back to top

What's your Challenge?

We’re not great fans of templates for proposals as our experience taught us that there is no such thing as two clients with exactly the same needs that can be addressed equally.

We want to understand how we can help your business to grow and present you with a customized solution.

Challenge us to do so by getting in touch below.