Formação AWS

← Todos os artigos

EC2 com Terraform, AWS CLI e console: o mesmo servidor três vezes

CarreiraPor Henrylle Maia · 15 out 2026 · 9 min de leitura

Quer começar no Terraform, mas sem saber como o que você já clica no console vira código? Aqui a mesma EC2 sai de três jeitos: pelo console, por um script da AWS CLI e por um main.tf do Terraform. Com os três lado a lado, dá para ver o que muda ao passar para infraestrutura como código.

O cenário: uma máquina de trabalho, três caminhos

A instância é uma máquina de trabalho na nuvem. Ela precisa de três coisas: um Security Group, uma role de instance profile e um User Data que deixe a máquina pronta quando ligar, com Docker, Git e outros itens já instalados.

Eu me conecto nela por SSM, um modelo mais seguro do que SSH. Por isso não uso par de chaves, e o Security Group só precisa de regra de saída.

Lancei três instâncias, uma por caminho: bia-manual, bia-cli e bia-terraform. Se você nunca lançou uma EC2 pelo console, comece por Lançar EC2 no novo console da AWS. O caminho completo é este:

O mesmo servidor por três caminhos: cada um termina com a EC2 pronta, com o mesmo Security Group, a mesma role e o mesmo User DataConsolepasso a passo na telaAWS CLIscript de 14 linhasTerraformmain.tf, plan e applyMesma EC2Security Group, role SSM e User Data
O mesmo servidor por três caminhos: cada um termina com a EC2 pronta, com o mesmo Security Group, a mesma role e o mesmo User Data

1Preparar o Security Group e a role

Os dois recursos são criados no console e servem aos três caminhos.

  • Security Group bia-dev: removi a regra de entrada que existia (a da porta 3001) e deixei só a de saída.
  • Role role-acesso-ssm: no IAM, uma role para EC2 com só a policy AmazonSSMManagedInstanceCore.

2Lançar pelo console

Em EC2, lançar instância: nome bia-manual, imagem x86, tipo t3.micro, sem par de chaves, a VPC padrão (a única que tenho), o Security Group bia-dev e, em Advanced details, o instance profile role-acesso-ssm.

Eu esqueci o User Data. Só percebi depois, ao abrir o User Data da instância e ver que estava vazio. É o ponto fraco do caminho manual: depende de lembrar de tudo, toda vez.

3Lançar pela AWS CLI

Para a CLI usei o CloudShell: clonei o projeto do GitHub, entrei na pasta scripts e rodei lancar_ec2_zona_a.sh. O script tem 14 linhas. Ele descobre a VPC padrão, a subnet e o Security Group, e só depois lança a instância:

bash
vpc_id=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true --query "Vpcs[0].VpcId" --output text)
subnet_id=$(aws ec2 describe-subnets --filters Name=vpc-id,Values=$vpc_id Name=availabilityZone,Values=us-east-1a --query "Subnets[0].SubnetId" ...)
security_group_id=$(aws ec2 describe-security-groups --group-names "bia-dev" --query "SecurityGroups[0].GroupId" --output text 2>/dev/null)

A segunda linha passa da borda da tela, e o resto dela não aparece no vídeo. Em seguida o script confere se o Security Group existe e aborta se não existir:

bash
if [ -z "$security_group_id" ]; then
    echo ">[ERRO] Security group bia-dev não foi criado na VPC $vpc_id"
    exit 1
fi

Por fim, o comando que lança a instância:

bash
aws ec2 run-instances --image-id ami-02f3f602d23f1659d --count 1 --instance-type t3.micro \
--security-group-ids $security_group_id --subnet-id $subnet_id --associate-public-ip-address \
--block-device-mappings '[{"DeviceName":"/dev/xvda","Ebs":{"VolumeSize":15,"VolumeType":"gp2"}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bia-dev}]' \
--iam-instance-profile Name=role-acesso-ssm --user-data file://user_data_ec2_zona_a.sh

A AMI é uma Amazon Linux 2023 da região da Virgínia. O script nomeia a instância bia-dev; eu renomeei para bia-cli no console.

O User Data

O --user-data aponta para um arquivo que roda quando a máquina liga. O conteúdo do user_data_ec2_zona_a.sh:

bash
#!/bin/bash

#Instalar Docker e Git
sudo yum update -y
sudo yum install git -y
sudo yum install docker -y
sudo usermod -a -G docker ec2-user
sudo usermod -a -G docker ssm-user
id ec2-user ssm-user
sudo newgrp docker

#Ativar docker
sudo systemctl enable docker.service
sudo systemctl start docker.service

#Instalar docker compose 2
sudo mkdir -p /usr/local/lib/docker/cli-plugins
# (baixar o plugin do Compose: veja a documentação oficial)
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

#Adicionar swap
sudo dd if=/dev/zero of=/swapfile bs=128M count=32
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo echo "/swapfile swap swap defaults 0 0" >> /etc/fstab

#Instalar node e npm
curl -fsSL https://rpm.nodesource.com/setup_21.x | sudo bash -
sudo yum install -y nodejs

A linha que baixa o Docker Compose passa da borda da tela e o vídeo não mostra o destino do download inteiro, então não a reproduzo. Para essa etapa, siga a documentação oficial do Docker Compose para Linux. Na instância criada pelo script, o docker compose version mostrou a v2.23.3.

4Lançar com Terraform

Antes do código, dois pré-requisitos: o Terraform instalado e a AWS CLI com um profile configurado (o meu se chama bia-tf; o profile permite usar várias contas AWS na mesma máquina).

Crio um main.tf numa pasta vazia. No Terraform, tudo que você lança na AWS é um recurso (resource). Começo pelo bloco do provider, o destino da conexão:

hcl
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

# Configure the AWS Provider
provider "aws" {
  region  = "us-east-1"
  profile = "bia-tf"
}

Depois o recurso da instância, com a mesma AMI, o mesmo tipo e os mesmos itens do script (o ID do Security Group está trocado por um placeholder):

hcl
resource "aws_instance" "bia-dev" {
  ami           = "ami-02f3f602d23f1659d"
  instance_type = "t3.micro"
  tags = {
    Name = "bia-terraform"
  }
  vpc_security_group_ids = [ "sg-xxxxxxxxxxxxxxxxx" ]
  user_data              = file("userdata.sh")
  iam_instance_profile   = "role-acesso-ssm"
}

O userdata.sh é o conteúdo do arquivo acima, copiado para a raiz do projeto. O ID do Security Group você copia da tela de Security Groups do console.

O main.tf com o provider e o resource aws_instance: AMI, t3.micro, tag bia-terraform, Security Group, user_data por arquivo e role-acesso-ssm (ID do Security Group borrado)
O main.tf com o provider e o resource aws_instance: AMI, t3.micro, tag bia-terraform, Security Group, user_data por arquivo e role-acesso-ssm (ID do Security Group borrado)

Os comandos, na ordem:

bash
terraform init
terraform plan
terraform apply
  • terraform init lê o main.tf, instala o provider e cria o arquivo .terraform.lock.hcl.
  • terraform plan mostra o que vai ser criado, sem criar. No meu caso: um recurso a adicionar, com AMI, tipo, instance profile, tag, referência ao User Data e Security Group.
  • terraform apply pede confirmação e cria. Respondi yes.

A bia-terraform apareceu no console com a mesma role e o mesmo Security Group das outras.

A lista de instâncias com bia-cli, bia-terraform e bia-manual em execução, todas t3.micro; as duas linhas bia-dev aparecem como terminadas
A lista de instâncias com bia-cli, bia-terraform e bia-manual em execução, todas t3.micro; as duas linhas bia-dev aparecem como terminadas

5Ver a diferença que o Terraform traz

Comparei o User Data das três em Edit user data: a manual estava vazia, a da CLI e a do Terraform tinham o script.

A grande diferença está no estado. Com a CLI, nada fica guardado localmente depois que o comando roda. Com o Terraform, ele guarda o estado do que foi criado (zona de disponibilidade, tipo, ID, role). Dá para ver com:

bash
terraform show

Esse estado deixa a alteração simples. Troquei o Security Group da instância editando o ID em vpc_security_group_ids e rodei terraform apply. Ele atualizou o estado, comparou com o código e mostrou Plan: 0 to add, 1 to change, 0 to destroy: só o Security Group mudou. Confirmei, e no console o grupo já tinha trocado. Voltei ao grupo anterior do mesmo jeito.

O plan do Terraform ao trocar o Security Group: o vpc_security_group_ids muda e o resumo diz 0 to add, 1 to change, 0 to destroy (IDs borrados)
O plan do Terraform ao trocar o Security Group: o vpc_security_group_ids muda e o resumo diz 0 to add, 1 to change, 0 to destroy (IDs borrados)

Com a CLI, trocar o Security Group de uma instância existente exigiria um novo script.

6Conferir que as duas funcionam e destruir

Para ver se a máquina saiu pronta, me conectei na bia-cli por SSM, com o instance ID dela:

bash
aws ssm start-session --target <instance-id> --profile bia-tf

Troquei para o ec2-user, e o Docker e o Docker Compose estavam instalados. Clonei o projeto (git clone https://github.com/henrylle/bia.git), entrei na pasta bia e rodei docker compose up -d. Repeti na instância do Terraform, que também tinha Docker e Docker Compose, e o build começou do mesmo jeito. Para entender melhor essa conexão sem SSH, veja AWS CloudShell e Session Manager.

Para apagar o que o Terraform criou:

bash
terraform destroy

Ele lê o estado e destrói só o que lançou, no meu caso um recurso. A bia-manual e a bia-cli continuam; essas você termina à mão no console.

Checklist

  • Instale o Terraform e configure um profile na AWS CLI.
  • Crie o Security Group e a role com a policy AmazonSSMManagedInstanceCore.
  • Escreva o bloco provider e o resource "aws_instance" no main.tf.
  • Rode terraform init, terraform plan e só então terraform apply.
  • Confira no console a role, o Security Group e o User Data da instância.
  • Teste a conexão por SSM e veja se o Docker subiu.
  • Para trocar, por exemplo, o Security Group, edite o main.tf e rode terraform apply.
  • No fim, terraform destroy e termine à mão o que foi lançado fora dele.

O console serve para entender cada peça, a CLI repete o lançamento por script e o Terraform guarda o estado e muda só o que você alterou. É esse caminho que a Formação AWS percorre com um projeto inteiro, e aqui você viu só o primeiro recurso.

O que os alunos dizem

  • “Eu sempre fugi da AWS. Achava complicado, distante da minha rotina de dev. Mas só a primeira aula do Bootcamp já mudou tudo: usei o conteúdo para resolver um problema real de um cliente e fui contratado por causa disso.”

    André Scherrer, Engenheiro de Software

    Ver depoimento
  • “Só quando coloquei a mão na massa com a Formação AWS é que avancei de verdade. Essa prática me levou ao próximo nível”

    Caio, Desenvolvedor (Dev)

    Ver depoimento
  • “Eu trabalhava há mais de 20 anos com TI tradicional e atendia empresas da região, mas sentia minha carreira estagnada.”

    Carlos Souza, Consultor Cloud

    Ver depoimento

Formação AWS

Aprenda AWS e IA na prática, com projeto real e acompanhamento. Converse com o time sobre a Formação AWS.

Agende uma conversa

Prefere assistir?

A aula completa (33 min) está no canal Domine AWS. O vídeo toca aqui mesmo.

Leia também

Compartilhe este post