Evite configuration drift no seu estado de terraform ao usar aws_security_group
Tags: aws, terraform, sre, devops
Published: 2022-03-08 11:02:37

O [terraform](https://www.terraform.io/intro) é uma ferramenta fantástica para provisionar e manter o estado de uma infraestrutura, tanto on-premises quanto nos ambientes de cloud. Entretanto, algumas vezes passamos por algumas situações que nós temos uma expectativa de que o terraform funcione de uma maneira, porém o terraform acaba funcionando de outra forma.

Foi o que aconteceu comigo ao utilizar o recurso [aws_security_group](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group). Este recurso permite criar um [Security Group](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html) dentro de uma VPC, que na prática pode ser aplicado a outros recursos, como instâncias EC2, banco de dados, clusters Kubernetes, para fechar portas de conexão e liberar apenas algumas portas específicas (ou nenhuma).

Meu caso de uso foi criar um Security Group para uma instância EC2 que iria trabalhar como um _worker_, ou seja, essa instância iria obter os jobs através de uma fonte externa, realizar o processamento e enviar o resultado. Em resumo, por trabalhar em **pull-based**, essa instância não precisaria ter nenhuma regra de ingresso (entrada), apenas uma regra de egresso (saída) para qualquer IP.


Logo, fiz o recurso da seguinte maneira:
```
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"]
  }
}
```


Ao executar o comando **terraform apply**, obtive a seguinte saída:

```
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]
```
Ótimo! O terraform criou o security group e eu pude utilizar na instância EC2 que criei. Entretanto, percebi que tinha cometido um erro ao provisionar a instância EC2 e eu precisava me conectar nessa instância usando **ssh**.

![made a mistake](https://media.giphy.com/media/QBpnlzndcEeIS57v15/giphy.gif)

Para possibilitar o acesso, eu precisava criar a regra de ingresso no security group. Logo, fui diretamente no dashboard da AWS, editei o security group manualmente liberando a porta 22 para apenas o meu IP.

![hehehe](https://media.giphy.com/media/xThtaxbIPLT9XqWcDK/giphy.gif)

Após me conectar na instância e resolver o problema, pensei: “_Vou simplesmente executar terraform apply, pois o terraform irá perceber a regra extra no security group e criar um plano para remover esta regra_”. Então, ao executar o comando, obtive a seguinte saída:

```
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.
```

![confuso](https://media.giphy.com/media/nTfdeBvfgzV26zjoFP/giphy.gif)

Na hora fiquei confuso. O terraform não percebeu a regra nova no security group? Fui no dashboard da AWS e vi que a regra de ingresso ainda estava presente! E agora?

Pesquisando melhor, entendi que o recurso aws_security_group não mantém o estado das regras, se isso não for **explícito**. Ou seja, precisamos deixar explícito na definição do recurso que não queremos nenhuma regra de ingresso. Corrigindo nosso código, ficaria assim:

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

  ingress = []    # explicitamente nenhuma regra de ingresso

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
```
Perceba que o atributo **ingress** recebe uma lista vazia. Isto deixa explícito que não queremos nenhuma regra de ingresso para este security group. Logo, executando o comando terraform apply novamente, finalmente podemos ter o estado garantido:

```
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.
```

![ufa](https://media.giphy.com/media/nXxOjZrbnbRxS/giphy.gif)

Ufa! Agora sim o terraform nos garante que este security group não ganhe novas regras através do dashboard da AWS e mantenha este estado pra gente.