Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destroy fails for "cannot parse as integer" #17793

Closed
acdifran opened this issue Apr 5, 2018 · 10 comments · Fixed by #17824
Closed

Destroy fails for "cannot parse as integer" #17793

acdifran opened this issue Apr 5, 2018 · 10 comments · Fixed by #17824

Comments

@acdifran
Copy link

acdifran commented Apr 5, 2018

Terraform Version

0.11.6

Terraform Configuration Files

module "vpc" {
    source = "../../terraform/modules/vpc"

    vpc_cidr               = "${var.vpc_cidr}"
    azs                    = "${var.azs}"
    public_subnets         = "${var.public_subnets}"
    private_subnets        = "${var.private_subnets}"
    private_egress_subnets = "${var.private_subnets_egress}"

    tags     = "${local.tags}"
}
resource "aws_vpc" "vpc" {
    cidr_block           = "${var.vpc_cidr}"
    enable_dns_hostnames = "${var.vpc_dns_hostnames}"
    enable_dns_support   = "${var.vpc_dns_support}"
    instance_tenancy     = "default"

    tags = "${merge(var.tags, map(
            "Name", format("%s-%s-vpc", var.tags["owner"], var.tags["environment"])
        )
    )}"
}

resource "aws_internet_gateway" "igw" {
    depends_on = [ "aws_vpc.vpc" ]

    vpc_id = "${aws_vpc.vpc.id}"

    tags = "${merge(var.tags, map(
            "Name", format("%s-%s-igw", var.tags["owner"], var.tags["environment"])
        )
    )}"
}


###############################################################################
# << DHCP Options

resource "aws_vpc_dhcp_options" "dhcp_opts" {
    depends_on = [ "aws_vpc.vpc" ]

    domain_name         = "${var.vpc_dhcp_opts_domain_name}"
    domain_name_servers = "${var.vpc_dhcp_opts_domain_name_servers}"

    tags = "${merge(var.tags, map(
           "Name", format("%s-%s-dhcp-options-set", var.tags["owner"], var.tags["environment"])
        )
    )}"

}

resource "aws_vpc_dhcp_options_association" "dhcp_assoc" {
    depends_on = [ "aws_vpc.vpc" ]

    vpc_id          = "${aws_vpc.vpc.id}"
    dhcp_options_id = "${aws_vpc_dhcp_options.dhcp_opts.id}"
}

###############################################################################
# << Route Tables

## Public route table

resource "aws_route_table" "public" {
    depends_on = [ "aws_vpc.vpc" ]

    vpc_id = "${aws_vpc.vpc.id}"

    tags = "${merge(var.tags, map(
            "Name", format("public-rt-%s-%s",
                var.tags["owner"],
                var.tags["environment"]
            )
        )
    )}"
}

## Private route tables

resource "aws_route_table" "private" {
    depends_on = [ "aws_vpc.vpc" ]

    vpc_id = "${aws_vpc.vpc.id}"

    tags = "${merge(var.tags, map(
            "Name", format("private-rt-%s-%s",
                var.tags["owner"],
                var.tags["environment"]
            )
        )
    )}"
}

resource "aws_route_table" "private_egress" {
    depends_on = [ "aws_vpc.vpc" ]

    vpc_id = "${aws_vpc.vpc.id}"

    tags = "${merge(var.tags, map(
            "Name", format("private-egress-rt-%s-%s",
                var.tags["owner"],
                var.tags["environment"]
            )
        )
    )}"
}
# Private routes will be handled on a different module

## Public default route

resource "aws_route" "igw" {
    depends_on = [ "aws_route_table.public" ]

    route_table_id         = "${aws_route_table.public.id}"
    destination_cidr_block = "0.0.0.0/0"
    gateway_id             = "${aws_internet_gateway.igw.id}"
}

###############################################################################
# << Subnets

# Get current region (defined by provider configuration)
data "aws_region" "current" {
    current = true
}

## Public subnets

resource "aws_subnet" "public" {
    count      = "${length(var.public_subnets)}"
    depends_on = [ "aws_route_table.public" ]

    vpc_id                  = "${aws_vpc.vpc.id}"
    cidr_block              = "${element(var.public_subnets, count.index)}"
    availability_zone       = "${format("%s%s",
                                    replace(data.aws_region.current.name, "/[0-9]$/", ""),
                                    element(var.azs, count.index)
                                )}"
    map_public_ip_on_launch = "true"

    tags = "${merge(var.tags, map(
            "Name", format("subnet-public-%s", element(var.azs, count.index))
        )
    )}"
}

resource "aws_subnet" "private" {
    count      = "${length(var.private_subnets)}"
    depends_on = [ "aws_route_table.private" ]

    vpc_id                  = "${aws_vpc.vpc.id}"
    cidr_block              = "${element(var.private_subnets, count.index)}"
    availability_zone       = "${format("%s%s",
                                    replace(data.aws_region.current.name, "/[0-9]$/", ""),
                                    element(var.azs, count.index)
                                )}"
    map_public_ip_on_launch = "false"

    tags = "${merge(var.tags, map(
            "Name", format("subnet-private-%s", element(var.azs, count.index))
        )
    )}"
}

resource "aws_subnet" "private_egress" {
    count      = "${length(var.private_egress_subnets)}"
    depends_on = [ "aws_route_table.private_egress" ]

    vpc_id                  = "${aws_vpc.vpc.id}"
    cidr_block              = "${element(var.private_egress_subnets, count.index)}"
    availability_zone       = "${format("%s%s",
                                    replace(data.aws_region.current.name, "/[0-9]$/", ""),
                                    element(var.azs, count.index)
                                )}"
    map_public_ip_on_launch = "false"

    tags = "${merge(var.tags, map(
            "Name", format("subnet-private-egress-%s",
                element(var.azs, count.index)
            )
        )
    )}"
}

###############################################################################
# << VPC endpoints

## S3

# Obtain vpc endpoint service name
data "aws_vpc_endpoint_service" "s3" {
	service = "s3"
}

resource "aws_vpc_endpoint" "s3" {
	vpc_id       = "${aws_vpc.vpc.id}"
	service_name = "${data.aws_vpc_endpoint_service.s3.service_name}"
}

resource "aws_vpc_endpoint_route_table_association" "s3_public" {
    depends_on = [ "aws_route_table.public" ]

    vpc_endpoint_id = "${aws_vpc_endpoint.s3.id}"
    route_table_id  = "${aws_route_table.public.id}"
}

resource "aws_vpc_endpoint_route_table_association" "s3_private" {
    depends_on = [ "aws_route_table.private" ]

    vpc_endpoint_id = "${aws_vpc_endpoint.s3.id}"
    route_table_id  = "${aws_route_table.private.id}"
}

resource "aws_vpc_endpoint_route_table_association" "s3_private_egress" {
    depends_on = [ "aws_route_table.private_egress" ]

    vpc_endpoint_id = "${aws_vpc_endpoint.s3.id}"
    route_table_id  = "${aws_route_table.private_egress.id}"
}

###############################################################################
# << Route Tables Association

resource "aws_route_table_association" "public" {
    count = "${length(var.public_subnets)}"

    subnet_id      = "${element(aws_subnet.public.*.id, count.index)}"
    route_table_id = "${aws_route_table.public.id}"
}

resource "aws_route_table_association" "private" {
    count = "${length(var.private_subnets)}"

    subnet_id      = "${element(aws_subnet.private.*.id, count.index)}"
    route_table_id = "${aws_route_table.private.id}"
}

resource "aws_route_table_association" "private_egress" {
    count = "${length(var.private_egress_subnets)}"

    subnet_id      = "${element(aws_subnet.private_egress.*.id, count.index)}"
    route_table_id = "${aws_route_table.private_egress.id}"
}

Debug Output

https://gist.github.com/acdifran/0bc2bdfde113b7be39d7e945c12c7f67

Crash Output

Expected Behavior

Plan with the -destroy should destroy all the resources that were created with that same plan.

Actual Behavior

Plan with the -destroy option complains about not being able to parse certain variables:

  • module.vpc.output.private_subnet_ids: cannot parse "${length(var.private_subnets)}" as an integer
  • module.vpc.output.private_egress_subnet_ids: cannot parse "${length(var.private_egress_subnets)}" as an integer
  • module.vpc.output.public_subnet_ids: cannot parse "${length(var.public_subnets)}" as an integer

Steps to Reproduce

  1. terraform init
  2. terraform plan
  3. terraform apply
  4. terraform plan -destroy
  5. terraform apply

Additional Context

I was not having this issue until upgrading to 0.11.6 today. I upgraded to that version to fix this issue: #17425, but that seems to have caused this new one. I've confirmed I don't have this issue on 0.11.0

References

@jbardin
Copy link
Member

jbardin commented Apr 5, 2018

Hi @acdifran,

Thanks for reporting this. I have a hunch that this is an issue around the separate plan, which isn't often used in conjunction with a full destroy.

Could you verify if this works work when using terraform destroy directly?

@acdifran
Copy link
Author

acdifran commented Apr 5, 2018

Hi @jbardin

Thanks for looking into this. I just confirmed that I do NOT have this issue when using the destroy command directly. It seems to be related to the plan command only.

@jbardin
Copy link
Member

jbardin commented Apr 9, 2018

Hi @acdifran,

I haven't yet managed to create a minimal config that can replicate this issue.
The errors here are from the outputs in the vpc module. Would it be possible the include that config, and anything else that might reference those outputs?

@acdifran
Copy link
Author

Hi @jbardin

Here are the outputs of the vpc module and the outputs of the vpc template:

output "vpc_id" {
    value = "${aws_vpc.vpc.id}"
}

output "vpce_id" {
    value = "${aws_vpc_endpoint.s3.id}"
}

output "vpc_igw" {
    value = "${aws_internet_gateway.igw.id}"
}

output "public_subnet_ids" {
    value = [ "${aws_subnet.public.*.id}" ]
}

output "private_subnet_ids" {
    value = [ "${aws_subnet.private.*.id}" ]
}

output "private_egress_subnet_ids" {
    value = [ "${aws_subnet.private_egress.*.id}" ]
}

output "route_tables_public" {
    value = "${aws_route_table.public.*.id}"
}

output "route_tables_private" {
    value = "${aws_route_table.private.*.id}"
}

output "route_tables_private_egress" {
    value = "${aws_route_table.private_egress.*.id}"
}
output "vpc_id" {
  value = "${module.vpc.vpc_id}"
}

output "vpce_id" {
  value = "${module.vpc.vpce_id}"
}

output "vpc_cidr" {
  value = "${var.vpc_cidr}"
}

output "public_subnet_ids" {
  value = [ "${module.vpc.public_subnet_ids}" ]
}

output "private_subnet_ids" {
  value = [ "${module.vpc.private_subnet_ids}" ]
}

output "private_egress_subnet_ids" {
  value = [ "${module.vpc.private_egress_subnet_ids}" ]
}

output "route_tables_public" {
    value = "${module.vpc.route_tables_public}"
}

output "route_tables_private" {
    value = "${module.vpc.route_tables_private}"
}

output "route_tables_private_egress" {
    value = "${module.vpc.route_tables_private_egress}"
}

@jbardin
Copy link
Member

jbardin commented Apr 10, 2018

Thanks @acdifran, that's what I needed.

This is triggered when an output referring to a list of resources using the splat (*) operator, which in turn used an interpolated count value.

If you still want to use the separate plan/apply steps, you can work around the issue by setting TF_WARN_OUTPUT_ERRORS=1 during the apply.

The minimal case here is

variable "list" {
  default = ["1", "2"]
}

resource "null_resource" "a" {
  count = "${length(var.list)}"
}

output "out" {
  value = "${null_resource.a.*.id}"
}
$ tf apply -auto-approve
$ tf plan -destroy -out destroy.plan
$ tf apply destroy.plan

resulting in:
* output.out: cannot parse "${length(var.list)}" as an integer

vincentbernat added a commit to vincentbernat/nixpkgs that referenced this issue May 20, 2018
This is a small fix for hashicorp/terraform#17793. There is no need to
rebuild the providers.
@c-carpenter
Copy link
Contributor

I've run into this problem on 0.11.7 but under slightly different conditions.
I am doing the same length combined with splat *, but instead of plan separation causing the error its because I am trying to delete a specific state.

terraform-mfa destroy --target=module.some_module -auto-approve

@mengesb
Copy link
Contributor

mengesb commented Aug 2, 2018

I'm hitting this on 0.11.7 as well. I'm running into many places where I have ternary controls in the counts, or usage through locals. This most affects me when I do partial destroys of the plan -- the delete succeeds, then I go to plan or apply after to recreate those destroyed items and I run into that message a lot.

I have no outputs - and I also have no modules or depth to the plan -- it's all flat.

@jhg03a
Copy link

jhg03a commented Jan 26, 2019

I also run into this problem on 11.8 when applying a plan which has had portions tainted.

@jcarlson
Copy link

I see this issue still in 0.11.11.

@ghost
Copy link

ghost commented Jul 25, 2019

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Jul 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants