Implantando e conectando-se a uma Instância EC2 usando AWS System Manager e Terraform.
Tags: terraform, aws, security, architecture
Published: 2023-12-06 19:04:16

Neste artigo, te ensinarei a criar uma instância na AWS, e se conectar na máquina sem a necessidade da porta 22 utilizando o AWS System Manager – Session Manage, juntamente com o IaC da Hashicorp, Terraform.

## AWS System Manager

O AWS Systems Manager é um serviço de gerenciamento de infraestrutura que oferece uma variedade de recursos para ajudar a automatizar suas tarefas com mais segurança.
Session Manager

Um desses recursos se chama **Session Manager**, ele permite que você acesse suas instâncias EC2 e outros recursos gerenciados pela AWS de forma segura e sem a necessidade de abrir portas diretamente em sua instância.

Com o Session Manager, você pode acessar via CLI em suas máquinas EC2 e executar comandos sem precisar se preocupar com a exposição de portas SSH, tornando mais segura sua conexão com a instância.

Para configurar o Session Manager e habilitar a comunicação segura via CLI, é necessário seguir alguns passos:

- Instale o AWS CLI e o AWS SSM Plugin de acordo com a documentação. [Documentação AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) / [Documentação AWS SSM Plugin](https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html).
- Configure o Security Group para permitir tráfego HTTPS na porta de saída.
- Verifique a instalação do agente do SSM nas instâncias EC2. [Documentação SSM Agent.](https://docs.aws.amazon.com/pt_br/systems-manager/latest/userguide/ssm-agent.html)
- Habilite a função IAM para que as instâncias EC2 acessem o AWS Systems Manager (Session Manager).

## Terraform

Usaremos os módulos oficiais da AWS encontrados no Terraform Registry, com a exceção de que vamos criar nosso próprio módulo para o Session Manager.

#### VPC

Na nossa rede VPC, usaremos IPs de Classe B com uma máscara /24. Embora tenhamos criado 3 sub-redes, utilizaremos apenas uma para nossa EC2 e outra para o NAT Gateway.

```terraform
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"

  name = "VPC-LAB-SSM"
//CIDR for VPC
  cidr = "171.31.0.0/16"

//Subnets & AZS
  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  private_subnets = ["171.31.11.0/24", "171.31.12.0/24", "171.31.13.0/24"]
  public_subnets  = ["171.31.101.0/24", "171.31.102.0/24", "171.31.103.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = true


//Security Group Egress
  default_security_group_egress = [
    {
      from_port   = 443
      to_port     = 443
      protocol    = "tcp"
      cidr_blocks = "0.0.0.0/0"
    }
  ]

  tags = {
    Terraform   = "true"
    Environment = "LAB-SSM"
  }
}
```

Para utilizar o Session Manager, é necessário apenas abrir a porta 443 na configuração de saída do nosso Firewall Stateful. Neste laboratório, não faremos configurações de entrada, apenas na saída.

#### Session Manager

Este será um módulo simples, desenvolvido por nós. Será necessário fornecer o nome da policy e um nome para o perfil da instância.

```terraform
module "session_manager" {
source = "./aws-sessionManager"

  name         = "EC2SSMRole_TF"
  policy_arn   = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  name_profile = "Instance_SSM_Profile"
}
```

Vamos aproveitar a função JSONENCODE para converter nossos dados em formato JSON e aplicá-los à configuração da aws_iam_role. 

```terraform
//Role EC2
resource "aws_iam_role" "ec2_role" {
  name = var.name

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Principal = {
          Service = "ec2.amazonaws.com"
        }
        Action = "sts:AssumeRole"
      }
    ]
  })
}

//Attachment AmazonSSMManagedInstanceCore on IAM Role
resource "aws_iam_role_policy_attachment" "ec2_role_policy" {
  policy_arn = var.policy_arn
  role       = aws_iam_role.ec2_role.name
}

//Instance Profile with EC2 associate
resource "aws_iam_instance_profile" "ec2_instance_profile" {
  name = var.name_profile

  role = aws_iam_role.ec2_role.name
}
```

A seguir, vinculamos a policy "AmazonSSMManagedInstanceCore" a essa role e criamos um perfil de instância que a referencia. 

#### Outputs

Primeiramente será necessário fazer o output no modulo filho do Session Manager para ser referenciado no modulo raiz do terraform.

```terraform
output "instance_profile_name" {
  value = aws_iam_instance_profile.ec2_instance_profile.name
} 
```
Vamos definir os outputs que serão utilizados na configuração da EC2

- ID do Security Group.
- ID da Subnet.
- Nome do perfil da instância.
- ID da instância, que é necessária para usar o Session Manager.

//ID Security Group.
output "default_security_group_id" { 
  value = module.vpc.default_security_group_id
}

```terraform
//ID Subnet.
output "private_subnets_id" {
  value = module.vpc.private_subnets
}

//ID instância
output "instance_id" {
  value = module.ec2_instance.id
}

//Name Instance Profile.
output "instance_profile_name" {
  value = module.session_manager.instance_profile_name
}
```

#### Instancia EC2 

Retornando aos módulos no Terraform Registry, estamos prontos para criar nossa instância EC2. Deixamos esta etapa por último para garantir que tenhamos todos outputs à nossa disposição.

```terraform
module "ec2_instance" {
  source = "terraform-aws-modules/ec2-instance/aws"
  version = "5.5.0"

  name   = "EC2-LAB-SSM"

  instance_type          = "t2.micro"

  //ID Security Group
  vpc_security_group_ids = [module.vpc.default_security_group_id]

  //ID Security Group
  subnet_id              = module.vpc.private_subnets[1]

  //Name Instance Profile
  iam_instance_profile   = module.session_manager.instance_profile_name

  tags = {
    Terraform   = "true"
    Environment = "LAB-SSM"
  }
}
```

É importante mencionar que não fizemos referência a nenhuma AMI específica, permitindo que o módulo utilize o AMI padrão, que é o Amazon Linux 2, e que de acordo com a documentação já vem com o SSM Agent instalado.

Para esta instância, estamos usando o tipo t2.micro, que se enquadra na camada gratuita (Free Tier). É importante observar que nos campos Security Group, Subnet ID e Instance Profile, não estamos inserindo valores diretamente, mas sim fazendo referência aos valores do output que esta em nosso modulo raiz. Isso proporciona uma infraestrutura totalmente dinâmica.

Por ser uma única instância, é necessário especificar qual subnet será utilizada. Neste caso, optamos pela [1], que corresponde a "171.31.12.0/24".
Deploy da sua infraestrutura

Abra o terminal dentro da pasta que contém seus arquivos TF, e execute o comando "terraform init" para baixar os plugins do provedor da AWS e dos módulos necessários.
```
terraform init
```
Utilize "terraform fmt --recursive" para formatar seu código Terraform, mantendo boas práticas sugeridas pela Hashicorp.
```
terraform fmt --recursive
```
Realize uma validação da sintaxe do seu código Terraform usando "terraform validate".
```
terraform validate
```
Implante sua infraestrutura com segurança usando o comando "terraform apply".
```
terraform apply
```
Este comando exibirá a estrutura da sua infraestrutura. Em seguida, solicitará sua confirmação. Digite "yes" para continuar com a implementação. Aguarde alguns minutos até a conclusão da criação do nosso laboratório.

Após a finalizar a implementação, o Terraform vai te gerar um output semelhante a este: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyw1wdw51jqna0wqthiv.png)

#### AWS CLI & PLUGIN SSM

O próximo passo é fazer uma conexão direta com a máquina a partir do nosso terminal utilizando o AWS CLI juntamente com o PLUGIN SSM, eliminando a necessidade de usar o console da AWS.

Após configurar o AWS CLI e instalar o AWS SSM PLUGIN, vá ao seu terminal e insira o seguinte comando:
```
 aws ssm start-session --target  ID_DA_SUA_INSTÂNCIA

//exemplo:
 aws ssm start-session --target  i-0cc3d167144121764
```
Lembrando que você precisa utilizar o valor do "instance_id" que será gerado no output.

O "--target" realizará uma busca na AWS para localizar o ID da sua instância correspondente. Nota-se que o valor utilizado no --target é o mesmo valor que esta no output "instance_id".

Depois disso, a interface do seu terminal será essa:
Interface do Terminal após se conectar na instância

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7etju6qu93z0n6vvxck.png)

E pronto, você terá uma conexão direta com sua instância, sem a necessidade da porta 22, sem a necessidade de uma chave de segurança (Key Pair), apenas utilizando os recursos do AWS Systems Manager - Session Manager em conjunto com o Terraform.

## Terraform destroy

Depois de testar seu laboratório, não se esqueça de executar o comando "terraform destroy -auto-approve" para finalizar a infraestrutura que você criou pelo Terraform, assim evitando esquecer algum recurso ativo na AWS.
```
 terraform destroy -auto-approve
```
**REPOSITÓRIO DO LABORATÓRIO**

Repositório do nosso laboratório no GitHub:
https://github.com/paulodisfarce/terraform-aws-infra-ssm

Versão Terraform: v1.5.7