Azure Citadel
  • Blogs

  • ARM
  • Azure Arc
    • Overview
    • Azure Arc-enabled Servers
      • Prereqs
      • Scenario
      • Hack Overview
      • Azure Landing Zone
      • Arc Pilot resource group
      • Azure Monitoring Agent
      • Additional policy assignments
      • Access your on prem VMs
      • Create onboarding scripts
      • Onboarding using scripts
      • Inventory
      • Monitoring
      • SSH
      • Windows Admin Center
      • Governance
      • Custom Script Extension
      • Key Vault Extension
      • Managed Identity
    • Azure Arc-enabled Kubernetes
      • Prereqs
      • Background
      • Deploy Cluster
      • Connect to Arc
      • Enable GitOps
      • Deploy Application
      • Enable Azure AD
      • Enforce Policy
      • Enable Monitoring
      • Enable Azure Defender
      • Enable Data Services
      • Enable Application Delivery
    • Useful Links
  • Azure CLI
    • Install
    • Get started
    • JMESPATH queries
    • Integrate with Bash
  • Azure Landing Zones
    • Prereqs
    • Day 1
      • Azure Baristas
      • Day 1 Challenge
    • Day 2
      • Example
      • Day 2 Challenge
    • Day 3
      • Day 3 Challenge
    • Useful Links
  • Azure Policy
    • Azure Policy Basics
      • Policy Basics in the Azure Portal
      • Creating Policy via the CLI
      • Deploy If Not Exists
      • Management Groups and Initiatives
    • Creating Custom Policies
      • Customer scenario
      • Policy Aliases
      • Determine the logic
      • Create the custom policy
      • Define, assign and test
  • Azure Stack HCI
    • Overview
    • Useful Links
    • Updates from Microsoft Ignite 2022
  • Marketplace
    • Introduction
      • Terminology
      • Offer Types
    • Partner Center
    • Offer Type
    • Publish a VM Offer HOL
      • Getting Started
      • Create VM Image
      • Test VM Image
      • VM Offer with SIG
      • VM Offer with SAS
      • Publish Offer
    • Other VM Resources
    • Publish a Solution Template HOL
      • Getting Started
      • Create ARM Template
      • Validate ARM Template
      • Create UI Definition
      • Package Assets
      • Publish Offer
    • Publish a Managed App HOL
      • Getting Started
      • Create ARM Template
      • Validate ARM Template
      • Create UI Definition
      • Package Assets
      • Publish Offer
    • Managed Apps with AKS HOL
    • Other Managed App Resources
    • SaaS Offer HOLs
    • SaaS Offer Video Series
      • Video 1 - SaaS Offer Overview
      • Video 2 - Purchasing a SaaS Offer
      • Video 3 - Purchasing a Private SaaS Plan
      • Video 4 - Publishing a SaaS Offer
      • Video 5 - Publishing a Private SaaS Plan
      • Video 6 - SaaS Offer Technical Overview
      • Video 7 - Azure AD Application Registrations
      • Video 8 - Using the SaaS Offer REST Fulfillment API
      • Video 9 - The SaaS Client Library for .NET
      • Video 10 - Building a Simple SaaS Landing Page in .NET
      • Video 11 - Building a Simple SaaS Publisher Portal in .NET
      • Video 12 - SaaS Webhook Overview
      • Video 13 - Implementing a Simple SaaS Webhook in .NET
      • Video 14 - Securing a Simple SaaS Webhook in .NET
      • Video 15 - SaaS Metered Billing Overview
      • Video 16 - The SaaS Metered Billing API with REST
  • Microsoft Fabric
    • Theory
    • Prereqs
    • Fabric Capacity
    • Set up a Remote State
    • Create a repo from a GitHub template
    • Configure an app reg for development
    • Initial Terraform workflow
    • Expanding your config
    • Configure a workload identity
    • GitHub Actions for Microsoft Fabric
    • GitLab pipeline for Microsoft Fabric
  • Packer & Ansible
    • Packer
    • Ansible
    • Dynamic Inventories
    • Playbooks & Roles
    • Custom Roles
    • Shared Image Gallery
  • Partner
    • Lighthouse and Partner Admin Link
      • Microsoft Cloud Partner Program
      • Combining Lighthouse and PAL
      • Minimal Lighthouse definition
      • Using service principals
      • Privileged Identity Management
    • Useful Links
  • REST API
    • REST API theory
    • Using az rest
  • Setup
  • Terraform
    • Fundamentals
      • Initialise
      • Format
      • Validate
      • Plan
      • Apply
      • Adding resources
      • Locals and outputs
      • Managing state
      • Importing resources
      • Destroy
    • Working Environments for Terraform
      • Cloud Shell
      • macOS
      • Windows with PowerShell
      • Windows with Ubuntu in WSL2
    • Using AzAPI
      • Using the REST API
      • azapi_resource
      • Removing azapi_resource
      • azapi_update_resource
      • Data sources and outputs
      • Removing azapi_update_resource
  • Virtual Machines
    • Azure Bastion with native tools & AAD
    • Managed Identities

  • About
  • Archive
  1. Home
  2. Terraform
  3. Fundamentals
  4. Destroy

Table of Contents

  • Overview
  • Starting point
  • Commenting and renaming
  • terraform destroy
  • Summary

Destroy

Short lab to tear down the environment.

Overview

In this lab you will

  • selectively destroy a resource with commenting
  • destroy the whole environment with terraform destroy
  • check the state file

Starting point

Your files should look similar to this:

  • provider.tf

    terraform {
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~>3.1"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    
      storage_use_azuread = true
    }
    
  • variables.tf

    variable "resource_group_name" {
      description = "Name for the resource group"
      type        = string
      default     = "terraform-basics"
    }
    
    variable "location" {
      description = "Azure region"
      type        = string
      default     = "West Europe"
    }
    
    variable "container_group_name" {
      description = "Name of the container group"
      type        = string
      default     = "terraform-basics"
    }
    
  • main.tf

    locals {
      uniq = substr(sha1(azurerm_resource_group.basics.id), 0, 8)
    }
    
    resource "azurerm_resource_group" "basics" {
      name     = var.resource_group_name
      location = var.location
    
      lifecycle {
        ignore_changes = [
          tags,
        ]
      }
    }
    
    resource "azurerm_container_group" "basics" {
      name                = var.container_group_name
      location            = azurerm_resource_group.basics.location
      resource_group_name = azurerm_resource_group.basics.name
      ip_address_type     = "Public"
      dns_name_label      = "${var.container_group_name}-${local.uniq}"
      os_type             = "Linux"
    
      container {
        name   = "inspectorgadget"
        image  = "jelledruyts/inspectorgadget:latest"
        cpu    = "0.5"
        memory = "1.0"
    
        ports {
          port     = 80
          protocol = "TCP"
        }
      }
    }
    
    resource "azurerm_storage_account" "import_example" {
      name                     = "richeney27182818"
      resource_group_name      = azurerm_resource_group.basics.name
      location                 = azurerm_resource_group.basics.location
      account_tier             = "Standard"
      account_replication_type = "LRS"
    
      allow_nested_items_to_be_public = false
      is_hns_enabled                  = true
      nfsv3_enabled                   = true
      public_network_access_enabled   = false
    }
    

    ⚠️ Your storage account name will be different.

  • outputs.tf

    output "ip_address" {
      value = azurerm_container_group.basics.ip_address
    }
    
    output "fqdn" {
      value = "http://${azurerm_container_group.basics.fqdn}"
    }
    
  • terraform.tfvars

    location = "UK South"
    

    You may have set a different value for location.

Commenting and renaming

Before we clean up the environment, take a look at how commenting blocks and renaming files can change the behaviour of the CLI commands.

  1. Comment out the container instance

    You can comment individual lines by prepending with either # or //.

    You can comment multiple lines by surrounding the block with /* and */, as shown below.

    /*
    resource "azurerm_container_group" "basics" {
      name                = var.container_group_name
      location            = azurerm_resource_group.basics.location
      resource_group_name = azurerm_resource_group.basics.name
      ip_address_type     = "public"
      dns_name_label      = "${var.container_group_name}-${local.uniq}"
      os_type             = "Linux"
    
      container {
        name   = "inspectorgadget"
        image  = "jelledruyts/inspectorgadget:latest"
        cpu    = "0.5"
        memory = "1.0"
    
        ports {
          port     = 80
          protocol = "TCP"
        }
      }
    }
    */
    

    Users of vscode can also highlight multiple lines and use the CTRL+K,CTRL+C chord to comment, and CTRL+K,CTRL+U to uncomment.

  2. terraform plan

    terraform plan
    

    You should see errors based on the outputs.

  3. Rename the outputs.tf file

    When Terraform runs its commands it is looking at all files in the current directory that match *.tf. You can rename file suffixes and it will ignore those files.

    Rename the outputs files so that it is completely ignored in the diff.

    mv outputs.tf outputs.tf.ignore
    
  4. terraform plan

    terraform plan
    
    azurerm_resource_group.basics: Refreshing state... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics]
    azurerm_container_group.basics: Refreshing state... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics/providers/Microsoft.ContainerInstance/containerGroups/terraform-basics]
    azurerm_storage_account.import_example: Refreshing state... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics/providers/Microsoft.Storage/storageAccounts/richeney27182818]
    
    Terraform used the selected providers to generate the following execution
    plan. Resource actions are indicated with the following symbols:
      - destroy
    
    Terraform will perform the following actions:
    
      # azurerm_container_group.basics will be destroyed
      # (because azurerm_container_group.basics is not in configuration)
      - resource "azurerm_container_group" "basics" {
          - dns_name_label      = "terraform-basics-c3818179" -> null
          - exposed_port        = [
              - {
                  - port     = 80
                  - protocol = "TCP"
                },
            ] -> null
          - fqdn                = "terraform-basics-c3818179.uksouth.azurecontainer.io" -> null
          - id                  = "/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics/providers/Microsoft.ContainerInstance/containerGroups/terraform-basics" -> null
          - ip_address          = "20.108.193.216" -> null
          - ip_address_type     = "Public" -> null
          - location            = "uksouth" -> null
          - name                = "terraform-basics" -> null
          - os_type             = "Linux" -> null
          - resource_group_name = "terraform-basics" -> null
          - restart_policy      = "Always" -> null
          - tags                = {} -> null
    
          - container {
              - commands                     = [] -> null
              - cpu                          = 0.5 -> null
              - environment_variables        = {} -> null
              - image                        = "jelledruyts/inspectorgadget:latest" -> null
              - memory                       = 1 -> null
              - name                         = "inspectorgadget" -> null
              - secure_environment_variables = (sensitive value)
    
              - ports {
                  - port     = 80 -> null
                  - protocol = "TCP" -> null
                }
            }
        }
    
    Plan: 0 to add, 0 to change, 1 to destroy.
    
    Changes to Outputs:
      - fqdn       = "http://terraform-basics-c3818179.uksouth.azurecontainer.io" -> null
      - ip_address = "20.108.193.216" -> null
    
    ─────────────────────────────────────────────────────────────────────────────
    
    Note: You didn't use the -out option to save this plan, so Terraform can't
    guarantee to take exactly these actions if you run "terraform apply" now.
    

    The resource is no longer in the config and so Terraform plans to remove it.

    Some of you will be familiar with ARM templates or Bicep and the standard incremental mode, which only ever contributes resources idempotently. If you were to remove resources from the resources array in an ARM template then those resources would remain in the resource group and would have to be manually deleted.

    The Terraform behaviour here is closer to the less commonly used complete mode in ARM / Bicep.

  5. Apply the change

    terraform apply
    

    Approve the change. The container group will be deleted.

terraform destroy

We’ll finish with a command that you will use rarely in production. The terraform destroy command will update state, show the current resources and remove any defined in your files.

  1. Destroy the environment

    terraform destroy
    

    Example output:

    azurerm_resource_group.basics: Refreshing state... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics]
    azurerm_storage_account.import_example: Refreshing state... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics/providers/Microsoft.Storage/storageAccounts/richeney27182818]
    
    Terraform used the selected providers to generate the following execution
    plan. Resource actions are indicated with the following symbols:
      - destroy
    
    Terraform will perform the following actions:
    
      # azurerm_resource_group.basics will be destroyed
      - resource "azurerm_resource_group" "basics" {
          - id       = "/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics" -> null
          - location = "uksouth" -> null
          - name     = "terraform-basics" -> null
          - tags     = {
              - "source" = "terraform"
            } -> null
        }
    
      # azurerm_storage_account.import_example will be destroyed
      - resource "azurerm_storage_account" "import_example" {
          - access_tier                       = "Hot" -> null
          - account_kind                      = "StorageV2" -> null
          - account_replication_type          = "LRS" -> null
          - account_tier                      = "Standard" -> null
          - allow_nested_items_to_be_public   = true -> null
          - enable_https_traffic_only         = true -> null
          - id                                = "/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics/providers/Microsoft.Storage/storageAccounts/richeney27182818" -> null
          - infrastructure_encryption_enabled = false -> null
          - is_hns_enabled                    = true -> null
          - location                          = "uksouth" -> null
          - min_tls_version                   = "TLS1_2" -> null
          - name                              = "richeney27182818" -> null
          - nfsv3_enabled                     = true -> null
          - primary_access_key                = (sensitive value)
          - primary_blob_connection_string    = (sensitive value)
          - primary_blob_endpoint             = "https://richeney27182818.blob.core.windows.net/" -> null
          - primary_blob_host                 = "richeney27182818.blob.core.windows.net" -> null
          - primary_connection_string         = (sensitive value)
          - primary_dfs_endpoint              = "https://richeney27182818.dfs.core.windows.net/" -> null
          - primary_dfs_host                  = "richeney27182818.dfs.core.windows.net" -> null
          - primary_file_endpoint             = "https://richeney27182818.file.core.windows.net/" -> null
          - primary_file_host                 = "richeney27182818.file.core.windows.net" -> null
          - primary_location                  = "uksouth" -> null
          - primary_queue_endpoint            = "https://richeney27182818.queue.core.windows.net/" -> null
          - primary_queue_host                = "richeney27182818.queue.core.windows.net" -> null
          - primary_table_endpoint            = "https://richeney27182818.table.core.windows.net/" -> null
          - primary_table_host                = "richeney27182818.table.core.windows.net" -> null
          - primary_web_endpoint              = "https://richeney27182818.z33.web.core.windows.net/" -> null
          - primary_web_host                  = "richeney27182818.z33.web.core.windows.net" -> null
          - queue_encryption_key_type         = "Service" -> null
          - resource_group_name               = "terraform-basics" -> null
          - secondary_access_key              = (sensitive value)
          - secondary_connection_string       = (sensitive value)
          - shared_access_key_enabled         = true -> null
          - table_encryption_key_type         = "Service" -> null
          - tags                              = {} -> null
    
          - blob_properties {
              - change_feed_enabled      = false -> null
              - last_access_time_enabled = false -> null
              - versioning_enabled       = false -> null
    
              - delete_retention_policy {
                  - days = 7 -> null
                }
            }
    
          - network_rules {
              - bypass                     = [
                  - "AzureServices",
                ] -> null
              - default_action             = "Deny" -> null
              - ip_rules                   = [] -> null
              - virtual_network_subnet_ids = [] -> null
            }
    
          - queue_properties {
    
              - hour_metrics {
                  - enabled               = true -> null
                  - include_apis          = true -> null
                  - retention_policy_days = 7 -> null
                  - version               = "1.0" -> null
                }
    
              - logging {
                  - delete                = false -> null
                  - read                  = false -> null
                  - retention_policy_days = 0 -> null
                  - version               = "1.0" -> null
                  - write                 = false -> null
                }
    
              - minute_metrics {
                  - enabled               = false -> null
                  - include_apis          = false -> null
                  - retention_policy_days = 0 -> null
                  - version               = "1.0" -> null
                }
            }
    
          - share_properties {
    
              - retention_policy {
                  - days = 7 -> null
                }
            }
    
          - timeouts {}
        }
    
    Plan: 0 to add, 0 to change, 2 to destroy.
    
    Do you really want to destroy all resources?
      Terraform will destroy all your managed infrastructure, as shown above.
      There is no undo. Only 'yes' will be accepted to confirm.
    
      Enter a value: yes
    
    
    azurerm_storage_account.import_example: Destroying... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics/providers/Microsoft.Storage/storageAccounts/richeney27182818]
    azurerm_storage_account.import_example: Destruction complete after 2s
    azurerm_resource_group.basics: Destroying... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-06b2123a68cc/resourceGroups/terraform-basics]
    azurerm_resource_group.basics: Still destroying... [id=/subscriptions/2ca40be1-7e80-4f2b-92f7-...3a68cc/resourceGroups/terraform-basics, 10s elapsed]
    azurerm_resource_group.basics: Destruction complete after 15s
    
    
    Destroy complete! Resources: 2 destroyed.
    
    

Summary

Done! 😊

You have learnt how to initialise Terraform, install providers, format and validate HCL files, how to add resources and plan and apply your configs. You have also worked with simple expressions, locals and outputs, manipulated the state file - including an import - and then managed the destroy phase.

Importing resources Destroy Next