Skip to content

resource/aws_lb: fix nlb recreation when security groups are added#46695

Open
circa10a wants to merge 1 commit into
hashicorp:mainfrom
circa10a:fix-nlb-add-sg
Open

resource/aws_lb: fix nlb recreation when security groups are added#46695
circa10a wants to merge 1 commit into
hashicorp:mainfrom
circa10a:fix-nlb-add-sg

Conversation

@circa10a
Copy link
Copy Markdown

@circa10a circa10a commented Feb 27, 2026

Rollback Plan

If a change needs to be reverted, we will publish an updated version of the library.

Changes to Security Controls

Are there any changes to security controls (access controls, encryption, logging) in this pull request?

No

Description

  • Fix "Provider produced inconsistent final plan" error when adding security groups to a (NLB) that currently has none, where the security group ID is unknown at plan time (e.g. created in the same apply)
  • NLBs do not support adding security groups after creation, so the provider should force replacement. When the new security group value is unknown at plan time, the Terraform SDK represents it as an empty set, causing HasChange to return false and ForceNew to silently fail. The fix detects this case using cty value inspection and correctly triggers replacement.

Relations

Closes #43703
Closes #38647
Closes #41880

References

Output from Acceptance Testing

make testacc TESTS=TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups_unknown PKG=elbv2      
make: Verifying source code with gofmt...
==> Checking that code complies with gofmt requirements...
make: Running acceptance tests on branch: 🌿 fix-nlb-add-sg 🌿...
TF_ACC=1 go1.25.7 test ./internal/service/elbv2/... -v -count 1 -parallel 20 -run='TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups_unknown'  -timeout 360m -vet=off
2026/02/27 15:46:23 Creating Terraform AWS Provider (SDKv2-style)...
2026/02/27 15:46:23 Initializing Terraform AWS Provider (SDKv2-style)...
=== RUN   TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups_unknown
=== PAUSE TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups_unknown
=== CONT  TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups_unknown
--- PASS: TestAccELBV2LoadBalancer_NetworkLoadBalancer_updateSecurityGroups_unknown (395.57s)
PASS
ok      github.com/hashicorp/terraform-provider-aws/internal/service/elbv2      400.301s

Output from manual testing

Behavior before when adding sg to NLB

❯ terraform apply -auto-approve
╷
│ Warning: Provider development overrides are in effect
│ 
│ The following provider development overrides are set in the CLI configuration:
│  - hashicorp/aws in /Users/calemoin/Desktop/terraform-provider-aws
│ 
│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with published releases.
╵
aws_vpc.test: Refreshing state... [id=vpc-05f429f5db01d7630]
aws_subnet.test: Refreshing state... [id=subnet-07c47d5ecacccafaf]
aws_lb.test: Refreshing state... [id=arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/4220cdbe458fe561]

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

Terraform will perform the following actions:

  # aws_lb.test will be updated in-place
  ~ resource "aws_lb" "test" {
        id                                                           = "arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/4220cdbe458fe561"
        name                                                         = "nlb-sg-repro"
      ~ security_groups                                              = [] -> (known after apply)
        tags                                                         = {}
        # (19 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

  # aws_security_group.test[0] will be created
  + resource "aws_security_group" "test" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + name                   = "nlb-sg-repro"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + region                 = "us-east-1"
      + revoke_rules_on_delete = false
      + tags                   = {
          + "Name" = "nlb-sg-repro"
        }
      + tags_all               = {
          + "Name" = "nlb-sg-repro"
        }
      + vpc_id                 = "vpc-05f429f5db01d7630"
    }

Plan: 1 to add, 1 to change, 0 to destroy.
aws_security_group.test[0]: Creating...
aws_security_group.test[0]: Creation complete after 3s [id=sg-04c3b5fe5f6d5b1d6]
╷
│ Error: Provider produced inconsistent final plan
│ 
│ When expanding the plan for aws_lb.test to include new values learned so far during apply, provider "registry.terraform.io/hashicorp/aws" changed the planned action from Update to DeleteThenCreate.
│ 
│ This is a bug in the provider, which should be reported in the provider's own issue tracker.

<truncated>

After fix when adding sg to NLB

❯ terraform apply -auto-approve
╷
│ Warning: Provider development overrides are in effect
│ 
│ The following provider development overrides are set in the CLI configuration:
│  - hashicorp/aws in /Users/calemoin/Desktop/terraform-provider-aws
│ 
│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with published releases.
╵
aws_vpc.test: Refreshing state... [id=vpc-04b98ffb1fd1bf282]
aws_subnet.test: Refreshing state... [id=subnet-04525bc4639189a6b]
aws_lb.test: Refreshing state... [id=arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/9a2e7ccd260ec705]

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

Terraform will perform the following actions:

  # aws_lb.test must be replaced
-/+ resource "aws_lb" "test" {
      ~ arn                                                          = "arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/9a2e7ccd260ec705" -> (known after apply)
      ~ arn_suffix                                                   = "net/nlb-sg-repro/9a2e7ccd260ec705" -> (known after apply)
      ~ dns_name                                                     = "nlb-sg-repro-9a2e7ccd260ec705.elb.us-east-1.amazonaws.com" -> (known after apply)
      + enforce_security_group_inbound_rules_on_private_link_traffic = (known after apply)
      ~ id                                                           = "arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/9a2e7ccd260ec705" -> (known after apply)
      ~ ip_address_type                                              = "ipv4" -> (known after apply)
        name                                                         = "nlb-sg-repro"
      + name_prefix                                                  = (known after apply)
      ~ secondary_ips_auto_assigned_per_subnet                       = 0 -> (known after apply)
      ~ security_groups                                              = [] -> (known after apply) # forces replacement
      - tags                                                         = {} -> null
      ~ tags_all                                                     = {} -> (known after apply)
      ~ vpc_id                                                       = "vpc-04b98ffb1fd1bf282" -> (known after apply)
      ~ zone_id                                                      = "REDACTED" -> (known after apply)
        # (9 unchanged attributes hidden)

      - access_logs {
          - enabled = false -> null
            # (2 unchanged attributes hidden)
        }

      ~ subnet_mapping (known after apply)
      - subnet_mapping {
          - subnet_id            = "subnet-04525bc4639189a6b" -> null
            # (4 unchanged attributes hidden)
        }
    }

  # aws_security_group.test[0] will be created
  + resource "aws_security_group" "test" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
                # (1 unchanged attribute hidden)
            },
        ]
      + name                   = "nlb-sg-repro"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + region                 = "us-east-1"
      + revoke_rules_on_delete = false
      + tags                   = {
          + "Name" = "nlb-sg-repro"
        }
      + tags_all               = {
          + "Name" = "nlb-sg-repro"
        }
      + vpc_id                 = "vpc-04b98ffb1fd1bf282"
    }

Plan: 2 to add, 0 to change, 1 to destroy.
aws_lb.test: Destroying... [id=arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/9a2e7ccd260ec705]
aws_lb.test: Still destroying... [id=arn:aws:elasticloadbalancing:us-east-1:...ncer/net/nlb-sg-repro/9a2e7ccd260ec705, 00m10s elapsed]
aws_lb.test: Still destroying... [id=arn:aws:elasticloadbalancing:us-east-1:...ncer/net/nlb-sg-repro/9a2e7ccd260ec705, 00m20s elapsed]
aws_lb.test: Still destroying... [id=arn:aws:elasticloadbalancing:us-east-1:...ncer/net/nlb-sg-repro/9a2e7ccd260ec705, 00m30s elapsed]
aws_lb.test: Still destroying... [id=arn:aws:elasticloadbalancing:us-east-1:...ncer/net/nlb-sg-repro/9a2e7ccd260ec705, 00m40s elapsed]
aws_lb.test: Destruction complete after 42s
aws_security_group.test[0]: Creating...
aws_security_group.test[0]: Creation complete after 3s [id=sg-0b49ef34db42fe926]
aws_lb.test: Creating...
aws_lb.test: Still creating... [00m10s elapsed]
aws_lb.test: Still creating... [00m20s elapsed]
aws_lb.test: Still creating... [00m30s elapsed]
aws_lb.test: Still creating... [00m40s elapsed]
aws_lb.test: Still creating... [00m50s elapsed]
aws_lb.test: Still creating... [01m00s elapsed]
aws_lb.test: Still creating... [01m10s elapsed]
aws_lb.test: Still creating... [01m20s elapsed]
aws_lb.test: Still creating... [01m30s elapsed]
aws_lb.test: Still creating... [01m40s elapsed]
aws_lb.test: Still creating... [01m50s elapsed]
aws_lb.test: Still creating... [02m00s elapsed]
aws_lb.test: Still creating... [02m10s elapsed]
aws_lb.test: Still creating... [02m20s elapsed]
aws_lb.test: Still creating... [02m30s elapsed]
aws_lb.test: Creation complete after 2m34s [id=arn:aws:elasticloadbalancing:us-east-1:<account id>:loadbalancer/net/nlb-sg-repro/5ba4276a6657c234]

Apply complete! Resources: 2 added, 0 changed, 1 destroyed.

@circa10a circa10a requested a review from a team as a code owner February 27, 2026 23:55
@github-actions
Copy link
Copy Markdown
Contributor

Community Guidelines

This comment is added to every new Pull Request to provide quick reference to how the Terraform AWS Provider is maintained. Please review the information below, and thank you for contributing to the community that keeps the provider thriving! 🚀

Voting for Prioritization

  • Please vote on this Pull Request by adding a 👍 reaction to the original post to help the community and maintainers prioritize it.
  • Please see our prioritization guide for additional information on how the maintainers handle prioritization.
  • Please do not leave +1 or other comments that do not add relevant new information or questions; they generate extra noise for others following the Pull Request and do not help prioritize the request.

Pull Request Authors

  • Review the contribution guide relating to the type of change you are making to ensure all of the necessary steps have been taken.
  • Whether or not the branch has been rebased will not impact prioritization, but doing so is always a welcome surprise.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 27, 2026

⚠️ Our automation has detected the following potential issues with your pull request


❌ Changelog Entry Required (Click to expand)

The proposed change requires a changelog entry. Please see the Changelog Process section of the contributing guide for information on the changelog generation process.

Tip: This check is not triggered for draft pull requests, since the pull request number is not known until the pull request is opened and is required to create a changelog entry. Opening a pull request first as a draft, adding the requisite changelog entry file, and then marking the pull request as ready for review will prevent future warnings.

@github-actions github-actions Bot added needs-triage Waiting for first response or review from a maintainer. documentation Introduces or discusses updates to documentation. tests PRs: expanded test coverage. Issues: expanded coverage, enhancements to test infrastructure. service/elbv2 Issues and PRs that pertain to the elbv2 service. provider Pertains to the provider itself, rather than any interaction with AWS. size/M Managed by automation to categorize the size of a PR. labels Feb 27, 2026
@dosubot dosubot Bot added the bug Addresses a defect in current functionality. label Feb 27, 2026
Signed-off-by: circa10a <caleblemoine@gmail.com>
@circa10a
Copy link
Copy Markdown
Author

circa10a commented Feb 27, 2026

CHANGELOG.md entry since the bot told me to remove it as it's added by maintainers.

* resource/aws_lb: Fix `Provider produced inconsistent final plan` error when adding security groups to a Network Load Balancer that has no security groups ([#43703](https://github.com/hashicorp/terraform-provider-aws/issues/43703))

@justinretzolk justinretzolk removed the needs-triage Waiting for first response or review from a maintainer. label Mar 2, 2026
@circa10a
Copy link
Copy Markdown
Author

@justinretzolk @ewbankkit apologies for the direct ping. I just wanted to follow up on this PR to determine expectations around if this will be reviewed/merged in the near future or not?

@ewbankkit ewbankkit self-assigned this May 19, 2026
@github-actions github-actions Bot added the prioritized Part of the maintainer teams immediate focus. To be addressed within the current quarter. label May 19, 2026
@ewbankkit ewbankkit added this to the v6.47.0 milestone May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Addresses a defect in current functionality. documentation Introduces or discusses updates to documentation. prioritized Part of the maintainer teams immediate focus. To be addressed within the current quarter. provider Pertains to the provider itself, rather than any interaction with AWS. service/elbv2 Issues and PRs that pertain to the elbv2 service. size/M Managed by automation to categorize the size of a PR. tests PRs: expanded test coverage. Issues: expanded coverage, enhancements to test infrastructure.

Projects

None yet

3 participants