hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
42.53k stars 9.52k forks source link

destroy --force asking for user input when variables have values #23552

Open gravelander opened 4 years ago

gravelander commented 4 years ago

When destroying infrastructure with terraform destroy --force the variable asks for input, although the input does not matter. I don't need to type in its value for it to be destroyed; any string works.

This is re: #2443 which has been locked to contributors only.

The code below is outdated/throws errors but still creates/destroys as planned. The new v0.12.17 syntax updates broke this hard. >:(

Terraform Version

Terraform v0.12.17

Terraform Configuration Files

variable "HostName" {
  type = string
  description = "Input desired hostname"
  }

provider "aws" {
  profile    = "default"
  region     = "us-east-1"
  access_key = "myaccesskeyhere"
  secret_key = "mysecretkeyhere"
}
resource "aws_instance" "example" {
  ami           = "ami-04b9e92b5572fa0d1"
  instance_type = "t2.micro"
  key_name      = "AWS-Ultra"
  security_groups = ["primary"]
  tags ={
    Name = "${var.HostName}"
  }
  provisioner "remote-exec"{
    inline = [
      "sudo hostnamectl set-hostname ${var.HostName}",
      "sudo adduser hutch --gecos '' --disabled-password",
      "usermod -aG sudo hutch",
      "sudo mkdir /home/hutch/.ssh",
      "sudo cp /home/ubuntu/.ssh/authorized_keys /home/hutch/.ssh",
      "sudo chown -R hutch:hutch /home/hutch/.ssh",
      "echo 'hutch ALL=(ALL:ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/hutch"
    ]
  }
  connection {
    type     = "ssh"
    host     = self.public_dns
    user     = "ubuntu"
    private_key = "/home/hutch/.ssh/AWS-Ultra.pem"
  }

Expected Behavior

Don't ask for input on variable, just delete the infrastructure.

Actual Behavior

var.HostName
  Input desired hostname

  Enter a value:

is printed. Input does not matter, does not require correct hostname.

Steps to Reproduce

  1. Create infrastructure with this script: terraform apply
  2. On prompt, input hostname
  3. terraform destroy --force

References

gravelander commented 4 years ago

@danieldreier per your request on #2443

danieldreier commented 4 years ago

thanks @xelarate86! I've put it on my list to reproduce. I'll follow up when I've tried.

anfedorov commented 4 years ago

Just taking beginner course in TF and ran into this. My main.tf is —

provider "aws" {
    region="us-west-1"
}

variable "inputname" {
    type = string
    description = "set the VPC name"
}

resource "aws_vpc" "myvpc" {
    cidr_block = "10.0.0.0/16"

    tags = {
        Name = var.inputname
    }
}

CLI output is —

% terraform apply --var 'inputname=asdf'

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.myvpc will be created
  + resource "aws_vpc" "myvpc" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.0.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "Name" = "asdf"
        }
    }

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_vpc.myvpc: Creating...
aws_vpc.myvpc: Creation complete after 2s [id=vpc-0b773ef14b2245944]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
% terraform destroy -force              
var.inputname
  set the VPC name

  Enter a value: qwer

aws_vpc.myvpc: Refreshing state... [id=vpc-0b773ef14b2245944]
aws_vpc.myvpc: Destroying... [id=vpc-0b773ef14b2245944]
aws_vpc.myvpc: Destruction complete after 1s

Destroy complete! Resources: 1 destroyed.

This is really surprising, as it asked for input, then ignored it and happily read the TF state instead, destroying a VPC that did not match the given input.

watcher00090 commented 3 years ago

Same issue here

patrick-mota commented 3 years ago

Same for me.

Terraform asks me for variables, I can type anything (just have to respect type string, etc.) and it works since vars are already in state.

I3urny commented 3 years ago

I also just came across this weird behaviour in Terraform v1.0.3 As @xelarate86 already mentioned, omitting the default parameter in the variable resource causes terraform to ask for a value instead of just deleting the resource.

variable "foo" {
  type = string
}

resource "local_file" "foo" {
    content = "${var.foo}"
    filename = "${path.module}/foo.bar"
}

The expected behaviour would be that destroy does not ask for a value as it destroys the existing resource defined in the .tfstate instead of asking for a value that is not used anyway. However, the actual behaviour differs:

$ terraform apply -auto-approve
var.foo
  Enter a value: foo

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.foo will be created
  + resource "local_file" "foo" {
      + content              = "foo"
      + directory_permission = "0777"
      + file_permission      = "0777"
      + filename             = "./foo.bar"
      + id                   = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
local_file.foo: Creating...
local_file.foo: Creation complete after 0s [id=0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
% terraform destroy -auto-approve
var.foo
  Enter a value: WhyIsSettingThisValueNecessary?

local_file.foo: Refreshing state... [id=0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33]

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:

  # local_file.foo will be destroyed
  - resource "local_file" "foo" {
      - content              = "foo" -> null
      - directory_permission = "0777" -> null
      - file_permission      = "0777" -> null
      - filename             = "./foo.bar" -> null
      - id                   = "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33" -> null
    }

Plan: 0 to add, 0 to change, 1 to destroy.
local_file.foo: Destroying... [id=0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33]
local_file.foo: Destruction complete after 0s

Destroy complete! Resources: 1 destroyed.

As expected the resource is deleted, however, a variable value had to be set during terraform destroy. Setting the value is unnecessary as it is not applied before deletion anyway and the goal was to destroy the existing resources in their current state. hence why the input prompt comes as a bit of a surprise.

In actual projects it might not be desired to set default values for certain variables. Similarly, a .tfvars file is not always necessary.

Making the user define a variable value during the deletion process which has effectively no use seems cumbersome.

@danieldreier is there any chance this behaviour will be changed any time soon?

BrandonRodriguezC commented 3 years ago

I have the same issue but i have to type the correct value to destroy the infrastructure on Terraform v1.0.5 (on linux_amd64 + provider registry.terraform.io/hashicorp/aws v3.56.0) . Here's a part of the Terraform main.tf file:

resource "aws_security_group" "SG_Ansible" {
  name        = "Ansible-brandonrodriguezc"
  description = "Ansible target apps"
  vpc_id      = data.aws_vpc.psl_vpc.id

  ingress {
    description      = "From ansible master to this new host"
    from_port        = 22
    to_port          = 22
    protocol         = "tcp"
    cidr_blocks      = ["${var.ip}/32"]
    ipv6_cidr_blocks = ["::/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

And a piece of the variable.tf file:

variable "ip"{
    description = "Ip of the ansible master to add it to the security group"
    type = string
}

When i try to destroy it, it asks for the ip variable and the input is incorrect it stops the destroy process:

[root@b7c1c904a293 TerraformTest]# terraform destroy
var.ip
  Ip of the current host to add it to the security group

  Enter a value: yes

module.Ansible-target.aws_security_group.SG_Ansible: Refreshing state... [id=sg-02b383a605894cb26]
╷
│ Error: "yes/32" is not a valid CIDR block: invalid CIDR address: yes/32
│
│   with module.Ansible-target.aws_security_group.SG_Ansible,
│   on .terraform/modules/Ansible-target/ASG-SG-LC/main.tf line 5, in resource "aws_security_group" "SG_Ansible":
│    5: resource "aws_security_group" "SG_Ansible" {
│
╵
╷
│ Error: "yes/32" is not a valid CIDR block: invalid CIDR address: yes/32
│
│   with module.Ansible-target.aws_security_group.SG_Ansible,
│   on .terraform/modules/Ansible-target/ASG-SG-LC/main.tf line 5, in resource "aws_security_group" "SG_Ansible":
│    5: resource "aws_security_group" "SG_Ansible" {
│
╵
[root@b7c1c904a293 TerraformTest]#

But if the input is correct it continues with the destroy process:

[root@b7c1c904a293 TerraformTest]# terraform destroy
var.ip
  Ip of the current host to add it to the security group

  Enter a value: 34.139.48.058            

module.Ansible-target.aws_security_group.SG_Ansible: Refreshing state... [id=sg-02b383a605894cb26]
module.Ansible-target.aws_launch_configuration.Ansible-brandonrodriguezc: Refreshing state... [id=terraform-20210827140741740900000001]
module.Ansible-target.aws_autoscaling_group.ASG-Ansible: Refreshing state... [id=ASG_Ansible_brandonrodriguezc]

The expected behavior is to destroy the infrastructure without asking the variables used in the apply process

asharsidd commented 2 years ago

I have come across the same issue and it is March 2022 so it means this has not been resolved. It just does not make any sense for Terraform to ask us all the variables again when we are asking it to 'destroy'. I have a Azure VM template with five variables input and now I want to destroy the VM but it is asking me all the variables again and will not take 'Enter' or any other value.

`[ash@rhel8 azurevm]$ terraform destroy var.resource_group_name Resource Group name

Enter a value: ashredhatt

var.user_name Enter your username:

Enter a value:

var.user_password Enter your Password:

Enter a value:

var.vm_name Enter your VM name:

Enter a value:

........ ╷ │ Error: "name" must not be empty │ │ with azurerm_windows_virtual_machine.rg, │ on main.tf line 60, in resource "azurerm_windows_virtual_machine" "rg": │ 60: name = var.vm_name │ ╵ ╷ │ Error: expected "admin_username" to not be an empty string, got │ │ with azurerm_windows_virtual_machine.rg, │ on main.tf line 64, in resource "azurerm_windows_virtual_machine" "rg": │ 64: admin_username = var.user_name │ ╵ ╷ │ Error: Public Ip Address: (Name "_pip" / Resource Group "ashredhatt") was not found │ │ with data.azurerm_public_ip.rg, │ on main.tf line 83, in data "azurerm_public_ip" "rg": │ 83: data "azurerm_public_ip" "rg" { │ ╵ [ash@rhel8 azurevm]$ `

ksalman commented 2 years ago

I am also running into this issue.

OneCricketeer commented 2 years ago

But if the input is correct it continues with the destroy process

is asking me all the variables again and will not take 'Enter' or any other value

Both of those examples of throwing errors are implemented as ValidateFunc within the respective providers for those properties and not directly related to the issue of being prompted at all.

You can see why it prompts for input from the help docs (it actually runs apply)

Usage: terraform [global options] destroy [options]

  Destroy Terraform-managed infrastructure.

  This command is a convenience alias for:
      terraform apply -destroy

Also note: #27681

metalboz commented 2 years ago

Same issue for me.

The best solution would be to be able to pass the plan as a destroy variable, something like this...

terraform plan -out test.plan

var.HostName
  Input desired hostname

  Enter a value: host1
..........  

terraform destroy test.plan
LaurentDumont commented 2 years ago

Just hit the same behavior.

It's a bit weird to ask considering that the variable value doesn't matter.

victorrgez commented 1 year ago

Also having this issue. Variables' values should not be asked for since they do not matter at the time of destroying

tmassoth commented 1 year ago

still facing this issue with TF version 1.3.6

luis-fnogueira commented 1 year ago

still facing this issue with TF version 1.3.7

daxpate commented 1 year ago

still facing this issue with TF version 1.3.7

rubber-ant commented 1 year ago

still facing this issue with TF version 1.3.9

saymolet commented 1 year ago

still facing this issue with TF version 1.4.2

georgeollis commented 1 year ago

Still an issue. Will this ever be resolved?

shfletcher commented 1 year ago

Still an issue with TF version 1.4.6

aiah7894 commented 1 year ago

Still seems like an ongoing issue. I have defined a variable to take user input for a CIDR block as follows:

resource "aws_security_group" "test_sg" {
    name = "test_sg"
    description = "This allows SSH, HTTP and MySql"
    vpc_id = "${aws_vpc.test_vpc.id}"

    ingress {
        description = "SSH"
        from_port = 22
        to_port = 22
        protocol = "tcp"
        cidr_blocks = ["${var.ip_address}/32"]
    }
}

However, when trying to destroy, if I enter a blank value in the IP address input, the destruction fails:

var.ip_address
  Your ip address for adding to the security group

  Enter a value: 

var.owner
  Enter a value: 

var.project
  Enter a value: 

aws_vpc.test_vpc: Refreshing state... [id=vpc-078041a919c110583]
aws_internet_gateway.test_ig: Refreshing state... [id=igw-058c7d4e3352757aa]
╷
│ Error: "/32" is not a valid CIDR block: invalid CIDR address: /32
│ 
│   with aws_security_group.test_sg,
│   on sg.tf line 3, in resource "aws_security_group" "test_sg":
│    3: resource "aws_security_group" "test_sg" {
│ 

It will proceed if the correct IP address is mentioned. TF version 1.4.6

dss010101 commented 1 year ago

still an issue

rmsys commented 1 year ago

Still an issue with Terraform v1.5.6

crw commented 1 year ago

Thanks for the update! This is likely to remain an issue until this specific issue is closed, no need to post these updates (until this issue is closed).

Please do use the upvote mechanism (click or add the 👍 emoji to the original post) to indicate your support for this issue. This helps avoid notification spam for issues with high numbers of participants while enabling the maintainers to prioritize issues. Thanks again for the feedback!

DrGerbil commented 1 year ago

To work around this, I add all of my input variables as output variables, with the names prepended by TFVAR. Then I can run eval $(terraform output | sed -nr '/TF_VAR_/ s/(TF_VAR_.*) = (.*)/export \1=\2/p') to set all the same input variables for the destroy. It's annoying that Terraform doesn't just retain this information automatically, though.

ArtemVakhitov commented 2 weeks ago

Just run into this. As a hackish workaround, I use local-exec provisioners to save the inputs in an .auto.tfvars file at the apply stage and then auto-load and delete the file at the destroy stage.

resource "null_resource" "manage_inputs" {

  provisioner "local-exec" {
    command = <<-EOT
        printf "instances = %s\ncores = %s\ngigabytes = %s\n" "${var.instances}" "${var.cores}" "${var.gigabytes}" > vms.auto.tfvars
    EOT
    when = create
  }

  provisioner "local-exec" {
    command = "rm -f vms.auto.tfvars"
    when = destroy
  }

}