Terraform Removed Block

lykins - Dec 7 '23 - - Dev Community

Decided to test out the new state block which looks to be released in v1.7.

Stemmed from this request in the hashi forums.
https://discuss.hashicorp.com/t/request-for-testing-removed-block/60511

For now, you can find some documentation and usage here:
https://github.com/hashicorp/terraform/pull/34339/commits/9af31fb5ce8fe983fc212390659215cf6501c85d

At this point it looks like a holy trinity of state blocks will be finished - import, moved, and now removed.

Image description

I conducted a simple test, and similar to import and move, additional feature support has been added.

Also looks like there will be an additional lifecycle argument which support this -- in this case it was destroy = false. This argument is required per docs.

The lifecycle block is required. The destroy argument determines whether Terraform will attempt to destroy the objects managed by the module or not. A value of false means that Terraform will remove the resources from state without destroying them.

Creating then removing local_file from state

Created a simple module which only creates one local file resource.

For this release, I downloaded the latest beta version.

https://releases.hashicorp.com/terraform/1.7.0-beta1/

My initial setup:

terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "2.4.0"
    }
  }
}

resource "local_file" "file" {
  filename = "${path.module}/destination"
  source   = "${path.module}/source"
} 
Enter fullscreen mode Exit fullscreen mode

Ran an init + apply:

local_file.file: Refreshing state... [id=5291767cb169477c0a03c53439ec85c8dc3aaf46]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # local_file.file will be created
  + resource "local_file" "file" {
      + content_base64sha256 = (known after apply)
      + content_base64sha512 = (known after apply)
      + content_md5          = (known after apply)
      + content_sha1         = (known after apply)
      + content_sha256       = (known after apply)
      + content_sha512       = (known after apply)
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./destination"
      + id                   = (known after apply)
      + source               = "./source"
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

local_file.file: Creating...
local_file.file: Creation complete after 0s [id=5291767cb169477c0a03c53439ec85c8dc3aaf46]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Enter fullscreen mode Exit fullscreen mode

Updated the file to remove the local_file resource and added removed block. I'm guessing lifecycle + destroy is a new argument.

terraform {
  required_providers {
    local = {
      source  = "hashicorp/local"
      version = "2.4.0"
    }
  }
}

# resource "local_file" "file" {
#   filename = "${path.module}/destination"
#   source   = "${path.module}/source"
# }

removed {
  from = local_file.file
  lifecycle {
    destroy = false
  }
}
Enter fullscreen mode Exit fullscreen mode

Ran an apply and received the expected feedback.

local_file.file: Refreshing state... [id=5291767cb169477c0a03c53439ec85c8dc3aaf46]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:

Terraform will perform the following actions:

 # local_file.file will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
 . resource "local_file" "file" {
      - content_base64sha256 = "N+FgMd5soTK/NPAhWpf9OwVevfnr6DOVhD8CTislQ5s=" -> null
      - content_base64sha512 = "/uaHo4s7YXkWFCLPOobf2izKhUbZtBKmlE3fnEfjM5kFzN4MAC4e8IK+Yl6QlbuqBPnpiI36dGCgtCX/7lVBCw==" -> null
      - content_md5          = "f352cac61c815fe9d44770e65345367d" -> null
      - content_sha1         = "5291767cb169477c0a03c53439ec85c8dc3aaf46" -> null
      - content_sha256       = "37e16031de6ca132bf34f0215a97fd3b055ebdf9ebe83395843f024e2b25439b" -> null
      - content_sha512       = "fee687a38b3b6179161422cf3a86dfda2cca8546d9b412a6944ddf9c47e3339905ccde0c002e1ef082be625e9095bbaa04f9e9888dfa7460a0b425ffee55410b" -> null
      - directory_permission = "0777" -> null
      - file_permission      = "0777" -> null
      - filename             = "./destination" -> null
      - id                   = "5291767cb169477c0a03c53439ec85c8dc3aaf46" -> null
      - source               = "./source" -> null
    }

Plan: 0 to add, 0 to change, 0 to destroy.

 Warning: Some objects will no longer be managed by Terraform
 
 If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
  - local_file.file
 
 After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.


Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Enter fullscreen mode Exit fullscreen mode

Let us try something a bit different.

In this case, I will be creating an ECR resource via the AWS provider, then deleting that resource from state and importing the resource to the AWS Cloud Control provider.

Why? Cause my friend @drewmullen wanted me to try this.

Set up my configuration. Commented out the AWSCC resource for now.

terraform {
  required_providers {
    awscc = {
      source  = "hashicorp/awscc"
      version = "0.66.0"
    }
    aws = {
      source  = "hashicorp/aws"
      version = "5.29.0"
    }
  }
}


resource "aws_ecr_repository" "this" {
  name                 = "delete-test"
  image_tag_mutability = "MUTABLE"

  image_scanning_configuration {
    scan_on_push = true
  }
}

# resource "awscc_ecr_repository" "this" {
#   repository_name      = "delete-test"
#   image_tag_mutability = "MUTABLE"
#   image_scanning_configuration = {
#     scan_on_push = true
#   }
# }

Enter fullscreen mode Exit fullscreen mode

Ran an apply.

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_ecr_repository.this will be created
  + resource "aws_ecr_repository" "this" {
      + arn                  = (known after apply)
      + id                   = (known after apply)
      + image_tag_mutability = "MUTABLE"
      + name                 = "delete-test"
      + registry_id          = (known after apply)
      + repository_url       = (known after apply)
      + tags_all             = (known after apply)

      + image_scanning_configuration {
          + scan_on_push = true
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_ecr_repository.this: Creating...
aws_ecr_repository.this: Creation complete after 1s [id=delete-test]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Enter fullscreen mode Exit fullscreen mode

Ok, resource is now created. For the next step, I will add a removed block and add an import block. I will also comment out (remove) the previous AWS ECR resource from code.

terraform {
  required_providers {
    awscc = {
      source  = "hashicorp/awscc"
      version = "0.66.0"
    }
    aws = {
      source  = "hashicorp/aws"
      version = "5.29.0"
    }
  }
}

# resource "aws_ecr_repository" "this" {
#   name                 = "delete-test"
#   image_tag_mutability = "MUTABLE"

#   image_scanning_configuration {
#     scan_on_push = true
#   }
# }

removed {
  from = aws_ecr_repository.this
  lifecycle {
    destroy = false
  }
}

import {
  id = "delete-test"
  to = awscc_ecr_repository.this
}

resource "awscc_ecr_repository" "this" {
  repository_name      = "delete-test"
  image_tag_mutability = "MUTABLE"
  image_scanning_configuration = {
    scan_on_push = true
  }
}
Enter fullscreen mode Exit fullscreen mode

After updating, received the following results:

awscc_ecr_repository.this: Preparing import... [id=delete-test]
aws_ecr_repository.this: Refreshing state... [id=delete-test]
awscc_ecr_repository.this: Refreshing state... [id=delete-test]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

 # aws_ecr_repository.this will no longer be managed by Terraform, but will not be destroyed
 # (destroy = false is set in the configuration)
 . resource "aws_ecr_repository" "this" {
      - arn                  = "arn:aws:ecr:us-east-1:634211456147:repository/delete-test" -> null
      - id                   = "delete-test" -> null
      - image_tag_mutability = "MUTABLE" -> null
      - name                 = "delete-test" -> null
      - registry_id          = "634211456147" -> null
      - repository_url       = "634211456147.dkr.ecr.us-east-1.amazonaws.com/delete-test" -> null
      - tags                 = {} -> null
      - tags_all             = {} -> null

      - encryption_configuration {
          - encryption_type = "AES256" -> null
        }

      - image_scanning_configuration {
          - scan_on_push = true -> null
        }
    }

  # awscc_ecr_repository.this will be updated in-place
  # (imported from "delete-test")
  ~ resource "awscc_ecr_repository" "this" {
        arn                          = "arn:aws:ecr:us-east-1:634211456147:repository/delete-test"
      + empty_on_delete              = (known after apply)
        encryption_configuration     = {
            encryption_type = "AES256"
        }
        id                           = "delete-test"
        image_scanning_configuration = {
            scan_on_push = true
        }
        image_tag_mutability         = "MUTABLE"
      + lifecycle_policy             = (known after apply)
        repository_name              = "delete-test"
      + repository_policy_text       = (known after apply)
        repository_uri               = "634211456147.dkr.ecr.us-east-1.amazonaws.com/delete-test"
      + tags                         = (known after apply)
    }

Plan: 1 to import, 0 to add, 1 to change, 0 to destroy.

 Warning: Some objects will no longer be managed by Terraform
 
 If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:
  - aws_ecr_repository.this
 
 After applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again.


Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

awscc_ecr_repository.this: Importing... [id=delete-test]
awscc_ecr_repository.this: Import complete [id=delete-test]
Enter fullscreen mode Exit fullscreen mode

Wrapping up

Nice feature that was added, especially for beginning terraformers. I've never enjoyed dealing with state, but this provides a much more controlled option.

I assume features such as for_each and other meta arguments are still not available, I haven't dug into the terraform code to confirm what is and isn't available. I think just recently import now accepts for_each and provider arguments.

Thanks to @danquack and @drewmullen for the review and ideas!

. . . . . . . . .