DEV Community

Gabriel de Biasi
Gabriel de Biasi

Posted on

Avoid configuration drift on your terraform state when using aws_security_group

Terraform is a fantastic tool for provisioning and maintaining infrastructure state, both on-premises and in cloud environments. However, sometimes we go through some situations where we have an expectation that terraform works one way, but terraform ends up working in another way.

this is fine

That's what happened to me when using the aws_security_group. This resource allows you to create a Security Group within a VPC, which in practice can be applied to other resources, such as EC2 instances, databases, and Kubernetes clusters, to deny connections to specific ports and allow only some specific ports (or none).

My use case was to create a Security Group for an EC2 instance that would work as a simple job processing worker. In summary, because it works in pull-based, this instance would not need to have any ingress (incoming) rules, only one egress (outgoing) rule for any IP.

So, I did it like this:

resource "aws_security_group" "sg" {
  name   = "my-amazing-security-group"
  vpc_id = aws_vpc.main.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
Enter fullscreen mode Exit fullscreen mode

When running the terraform apply command, I got the following output:

gabriel@machine:~/terraform$ terraform 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_security_group.sg will be created
  + resource "aws_security_group" "sg" {
      + arn                    = (known after apply)
      + description            = "Managed by Terraform"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = (known after apply)
      + name                   = "my-amazing-security-group"
      + name_prefix            = (known after apply)
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + tags_all               = (known after apply)
      + vpc_id                 = "vpc-0101010101"
    }

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

aws_security_group.sg: Creating...
aws_security_group.sg: Creation complete after 2s [id=sg-0101010101010101]
Enter fullscreen mode Exit fullscreen mode

Excellent! Terraform created the security group and I was able to use it on the EC2 instance I created. However, I realized I had made a mistake while provisioning the EC2 instance and needed to connect to this specific instance using ssh.

made a mistake

To enable this access, I needed to create a new ingress rule on the security group. So, I went directly to the AWS dashboard, and edited the security group manually, allowing connections on port 22 for just my IP Address.

hehehe

After connecting to the instance and solving the problem, I thought to myself:

I'm gonna run terraform apply again, as terraform will notice the extra rule in the security group and create a plan to remove this rule, right?

So when I ran the command I got the following output:

gabriel@machine:~/terraform$ terraform apply
aws_security_group.sg: Refreshing state... [id=sg-0101010101010101]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

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

confused

I was confused. Did terraform not notice the new rule in the security group? I went to the AWS dashboard and saw that the ingress rule was still present! So what?

Then I dig a little further and I understood that the aws_security_group does not maintain the state of the rules, if this is not explicit. That is, we need to make it explicit in the resource definition that we don't want any ingress rules. Correcting our code, it would look like this:

resource "aws_security_group" "sg" {
  name   = "my-amazing-security-group"
  vpc_id = data.aws_vpc.main.id

  ingress = []    # no ingress rules allowed

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
Enter fullscreen mode Exit fullscreen mode

Note that the ingress attribute takes an empty list. This makes it explicit to terraform that we don't want any ingress rules for this security group. Then, running the terraform apply command again, we can finally have the guaranteed state:

gabriel@machine:~/terraform$ terraform apply
aws_security_group.sg: Refreshing state... [id=sg-0110522c7ec4870d0]

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_security_group.sg will be updated in-place
  ~ resource "aws_security_group" "sg" {
        id                     = "sg-0110522c7ec4870d0"
      ~ ingress                = [
          - {
              - cidr_blocks      = [
                  - "169.169.169.169/32",
                ]
              - description      = "My IP"
              - from_port        = 22
              - ipv6_cidr_blocks = []
              - prefix_list_ids  = []
              - protocol         = "tcp"
              - security_groups  = []
              - self             = false
              - to_port          = 22
            },
        ]
        name                   = "my-amazing-security-group"
        tags                   = {}
        # (7 unchanged attributes hidden)
    }

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

aws_security_group.sg: Modifying... [id=sg-0110522c7ec4870d0]
aws_security_group.sg: Modifications complete after 0s [id=sg-0110522c7ec4870d0]

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

ufa

Now, terraform guarantees that this security group does not gain new rules through the AWS dashboard and maintains this state for us!

Top comments (1)

Collapse
 
eribeirojr profile image
eribeirojr

Charming! :)