hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.83k stars 9.17k forks source link

[Bug]: `aws_lightsail_instance_public_ports` will always be replaced when `port_info.protocol` is set to `all` #36091

Open mdawar opened 8 months ago

mdawar commented 8 months ago

Terraform Core Version

v1.7.4

AWS Provider Version

v5.39.1

Affected Resource(s)

aws_lightsail_instance_public_ports

Expected Behavior

Running terraform apply should not force replacement of this resource.

Actual Behavior

Replacement of this resource is triggered because of the difference between the port_info.protocol value of all and -1 as stored in the state.

Relevant Error/Panic Output Snippet

No response

Terraform Configuration Files

terraform {
  required_version = "~> 1.7.4"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.39.1"
    }
  }
}

variable "aws_region" {
  description = "AWS region"
  type        = string
  default     = "us-east-1"
}

variable "aws_access_key" {
  description = "AWS access key ID"
  type        = string
}

variable "aws_secret_key" {
  description = "AWS secret access key"
  type        = string
}

provider "aws" {
  region     = var.aws_region
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key

  default_tags {
    tags = {
      Terraform   = "true"
      Environment = "dev"
    }
  }
}

data "aws_availability_zones" "available" {
  state = "available"

  filter {
    name   = "opt-in-status"
    values = ["opt-in-not-required"]
  }
}

resource "aws_lightsail_instance" "demo" {
  name              = "demo"
  availability_zone = data.aws_availability_zones.available.names[0]
  blueprint_id      = "amazon_linux_2"
  bundle_id         = "nano_1_0"
}

resource "aws_lightsail_instance_public_ports" "main" {
  instance_name = aws_lightsail_instance.demo.name

  port_info {
    protocol  = "all" # Saved in the state as "-1", causing resource re-creation.
    from_port = 0
    to_port   = 65535
  }
}

Steps to Reproduce

  1. Apply this configuration using terraform apply.
  2. Run terraform apply again.

The second apply will try to replace this resource because of the port_info.protocol value of all.

$ terraform apply
data.aws_availability_zones.available: Reading...                                                                                                                                             
data.aws_availability_zones.available: Read complete after 1s [id=us-east-1]                                                                                                                  
aws_lightsail_instance.demo: Refreshing state... [id=demo]
aws_lightsail_instance_public_ports.main: Refreshing state... [id=demo-435209064]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_lightsail_instance_public_ports.main must be replaced
-/+ resource "aws_lightsail_instance_public_ports" "main" {
      ~ id            = "demo-435209064" -> (known after apply)
        # (1 unchanged attribute hidden)

      - port_info { # forces replacement
          - cidr_list_aliases = [] -> null
          - cidrs             = [
              - "0.0.0.0/0",
            ] -> null
          - from_port         = 0 -> null
          - ipv6_cidrs        = [
              - "::/0",
            ] -> null
          - protocol          = "-1" -> null
          - to_port           = 65535 -> null
        }
      + port_info { # forces replacement
          + cidr_list_aliases = (known after apply)
          + cidrs             = (known after apply)
          + from_port         = 0
          + ipv6_cidrs        = (known after apply)
          + protocol          = "all"
          + to_port           = 65535
        }
    }

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

Debug Output

No response

Panic Output

No response

Important Factoids

This value of -1 is handled for aws_vpc_security_group_ingress_rule and aws_vpc_security_group_egress_rule resources, see ProtocolForValue function, also the value -1 is accepted as a value for the ip_protocol argument of these resources.

References

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lightsail_instance_public_ports#protocol https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule#ip_protocol https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule#ip_protocol

Would you like to implement a fix?

None

github-actions[bot] commented 8 months ago

Community Note

Voting for Prioritization

Volunteering to Work on This Issue

justinretzolk commented 8 months ago

This looks quite similar to #35526. Can you take a look at the suggestion here and let us know if that helps at all?

Also of note, in case that doesn't fix it, located this comment on another potentially related issue #30286.

mdawar commented 8 months ago

@justinretzolk I've checked that issue, and no I don't think it's related, that issue is about icmpv6 as the protocol value, this issue is about handling the -1 value being equivalent to all just like it's handled for aws_vpc_security_group_ingress_rule and aws_vpc_security_group_egress_rule.

As you can see in the ProtocolForValue function.

func ProtocolForValue(v string) string {
    // special case -1
    protocol := strings.ToLower(v)
    if protocol == "-1" || protocol == "all" {
        return "-1"
    }
        // ...
}

Currently a replacement is forced because of this difference, if you just keep applying the config it will keep replacing the resource.