>> Apresentação e considerações
Voltamos a falar de Terraform, se você não viu o nosso primeiro artigo falando sobre a teoria e instalação do Terraform convido a todos que leiam.
Nesse artigo vou mostrar um exemplo prático de Terraform na AWS, ele é baseado na versão “Terraform v0.9.11”.
Let’s go!!!
>> O que veremos nesse artigo?
Estou utilizando nesse artigo itens que entram no quesito FreeTier
na AWS, ou seja, se você possuir uma conta na AWS e seguir esse tutorial você não será cobrado pelos seus testes, certo disso vamos ver como:
- Provisionar uma instância EC2 na AWS
- Criar as
TAGS
para a instância - Execução de
UserData
com script de instalação e start do Nginx - 2 Regras
Security Group INBOUND
, portas 80 e 22 - 1 Regra de
Security Group OUTBOUND
para a instância ter acesso à internet
>> Pré requisitos
- Ter o
git
instalado - Uma conta na AWS, podendo tranquilamente ser Free Tier
- Possui uma
aws_access_key
eaws_secret_key
com permissões administrativas Key pair
para conectar na instância via SSH| -> Gerando “ssh key pair” <-- Ter o Terraform instalado | -> Conhecendo e Instalando Terraform <-
>> Clonando o repositório com o git
~$ git clone https://github.com/rdglinux/terraform-aws-churrops-project-one.git ~$ cd terraform-aws-churrops-project-one
Segue uma breve descrição de cada arquivo que foi baixado
- provider.tf
Informações de conexão com a AWS - main.tf
Nesse arquivo declaramos as informações da instância que vamos criar - security-group.tf
Security group que será criado e adicionado na instância - nginx.sh
Sccript para ser utilizado no UserData que instala o NGINX - output.tf
Aqui declaramos que o ip da instância será exibido após o lançamento - vars.tf
O arquivo vars, é onde podemos definir as variáveis principais que serão utilizadas
Basicamente para rodar esse Terraform você precisa setar os parâmetros:aws_access_key, aws_secret_key, key_name
, sendo que as chaves você pode inserir em tempo de exeução
variable "aws_access_key" {} variable "aws_secret_key" {} variable "aws_region" { default = "us-east-1" } variable "amis" { type = "map" default = { us-east-1 = "ami-a4c7edb2" } } variable "key_name" { default = "rdglinux-awskey-us" } variable "instance_type" { default = "t2.micro" }
>> Vamos planejar a nossa Infraestrutura
O comando utilizando é o terraform plan
, através dele vamos poder ver todos os itens que serão implantados em nosso Infra, baseado nas configurações ele monta um plano de execução e exibe os resultados sem de fato aplicar.
Da forma que está o nosso código, ao ser executado ele irá solicitar de forma interativa as chaves AWS para poder realizar o plano
~$ terraform init ~$ terraform plan var.aws_access_key Enter a value: var.aws_secret_key Enter a value:
Abaixo é o resultado do plan
temos basicamente chave/valor
, tudo o que está com o valor computed
será criado em tempo de execução, ou seja, são valores que não foram definidos na nossa receita, os demais são valores que foram declarados.
Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. The Terraform execution plan has been generated and is shown below. Resources are shown in alphabetical order for quick scanning. Green resources will be created (or destroyed and then created if an existing resource exists), yellow resources are being changed in-place, and red resources will be destroyed. Cyan entries are data sources to be read. Note: You didn't specify an "-out" parameter to save this plan, so when "apply" is called, Terraform can't guarantee this is what will execute. + aws_instance.churrops ami: "ami-a4c7edb2" associate_public_ip_address: "<computed>" availability_zone: "<computed>" ebs_block_device.#: "<computed>" ephemeral_block_device.#: "<computed>" instance_state: "<computed>" instance_type: "t2.micro" ipv6_address_count: "<computed>" ipv6_addresses.#: "<computed>" key_name: "rdglinux-awskey-us" network_interface.#: "<computed>" network_interface_id: "<computed>" placement_group: "<computed>" primary_network_interface_id: "<computed>" private_dns: "<computed>" private_ip: "<computed>" public_dns: "<computed>" public_ip: "<computed>" root_block_device.#: "<computed>" security_groups.#: "1" security_groups.3344374217: "sg_DefaultWebserver" source_dest_check: "true" subnet_id: "<computed>" tags.%: "3" tags.Name: "churrops" tags.Provider: "terraform" tags.Role: "test" tenancy: "<computed>" user_data: "37f0a6f40ea50e1784ae62ac96c55fe1aac34ac3" volume_tags.%: "<computed>" vpc_security_group_ids.#: "<computed>" + aws_security_group.sg_DefaultWebserver description: "Allow all outbound traffic and inbound 22/80" egress.#: "1" egress.482069346.cidr_blocks.#: "1" egress.482069346.cidr_blocks.0: "0.0.0.0/0" egress.482069346.from_port: "0" egress.482069346.ipv6_cidr_blocks.#: "0" egress.482069346.prefix_list_ids.#: "0" egress.482069346.protocol: "-1" egress.482069346.security_groups.#: "0" egress.482069346.self: "false" egress.482069346.to_port: "0" ingress.#: "2" ingress.2214680975.cidr_blocks.#: "1" ingress.2214680975.cidr_blocks.0: "0.0.0.0/0" ingress.2214680975.from_port: "80" ingress.2214680975.ipv6_cidr_blocks.#: "0" ingress.2214680975.protocol: "tcp" ingress.2214680975.security_groups.#: "0" ingress.2214680975.self: "false" ingress.2214680975.to_port: "80" ingress.2541437006.cidr_blocks.#: "1" ingress.2541437006.cidr_blocks.0: "0.0.0.0/0" ingress.2541437006.from_port: "22" ingress.2541437006.ipv6_cidr_blocks.#: "0" ingress.2541437006.protocol: "tcp" ingress.2541437006.security_groups.#: "0" ingress.2541437006.self: "false" ingress.2541437006.to_port: "22" name: "sg_DefaultWebserver" owner_id: "<computed>" tags.%: "2" tags.Name: "sg_DefaultWebserver" tags.Provisioner: "terraform" vpc_id: "<computed>" Plan: 2 to add, 0 to change, 0 to destroy.
Ao final de tudo temos o resultado onde vemos que serão adicionados 2 recursos, que seria 1 instância EC2 e 1 Security Group, ou seja, ele entendo como os recursos de Infraestrutura.
>> Fazendo o build da Infraestrutura
Depois de utilizarmos o terraform plan
vamos usar o terraform apply
, estando tudo ok, ao executar o comando todos os recursos declarados serão criados na AWS
Na execução, novamente precisa informar as chaves
~$ terraform apply var.aws_access_key Enter a value: AKIAIF334ROEKSYDTA var.aws_secret_key Enter a value: qskeirjd85jfufjdjsk5jAwOD3CG6V4Nu/Brhf
Agora vamos acompanhar os recursos sendo criados.
aws_instance.churrops: Creating... ami: "" => "ami-a4c7edb2" associate_public_ip_address: "" => "<computed>" availability_zone: "" => "<computed>" ebs_block_device.#: "" => "<computed>" ephemeral_block_device.#: "" => "<computed>" instance_state: "" => "<computed>" instance_type: "" => "t2.micro" ipv6_address_count: "" => "<computed>" ipv6_addresses.#: "" => "<computed>" key_name: "" => "rdglinux-awskey-us" network_interface.#: "" => "<computed>" network_interface_id: "" => "<computed>" placement_group: "" => "<computed>" primary_network_interface_id: "" => "<computed>" private_dns: "" => "<computed>" private_ip: "" => "<computed>" public_dns: "" => "<computed>" public_ip: "" => "<computed>" root_block_device.#: "" => "<computed>" security_groups.#: "" => "1" security_groups.3344374217: "" => "sg_DefaultWebserver" source_dest_check: "" => "true" subnet_id: "" => "<computed>" tags.%: "" => "3" tags.Name: "" => "churrops" tags.Provider: "" => "terraform" tags.Role: "" => "test" tenancy: "" => "<computed>" user_data: "" => "37f0a6f40ea50e1784ae62ac96c55fe1aac34ac3" volume_tags.%: "" => "<computed>" vpc_security_group_ids.#: "" => "<computed>" aws_security_group.sg_DefaultWebserver: Creating... description: "" => "Allow all outbound traffic and inbound 22/80" egress.#: "" => "1" egress.482069346.cidr_blocks.#: "" => "1" egress.482069346.cidr_blocks.0: "" => "0.0.0.0/0" egress.482069346.from_port: "" => "0" egress.482069346.ipv6_cidr_blocks.#: "" => "0" egress.482069346.prefix_list_ids.#: "" => "0" egress.482069346.protocol: "" => "-1" egress.482069346.security_groups.#: "" => "0" egress.482069346.self: "" => "false" egress.482069346.to_port: "" => "0" ingress.#: "" => "2" ingress.2214680975.cidr_blocks.#: "" => "1" ingress.2214680975.cidr_blocks.0: "" => "0.0.0.0/0" ingress.2214680975.from_port: "" => "80" ingress.2214680975.ipv6_cidr_blocks.#: "" => "0" ingress.2214680975.protocol: "" => "tcp" ingress.2214680975.security_groups.#: "" => "0" ingress.2214680975.self: "" => "false" ingress.2214680975.to_port: "" => "80" ingress.2541437006.cidr_blocks.#: "" => "1" ingress.2541437006.cidr_blocks.0: "" => "0.0.0.0/0" ingress.2541437006.from_port: "" => "22" ingress.2541437006.ipv6_cidr_blocks.#: "" => "0" ingress.2541437006.protocol: "" => "tcp" ingress.2541437006.security_groups.#: "" => "0" ingress.2541437006.self: "" => "false" ingress.2541437006.to_port: "" => "22" name: "" => "sg_DefaultWebserver" owner_id: "" => "<computed>" tags.%: "" => "2" tags.Name: "" => "sg_DefaultWebserver" tags.Provisioner: "" => "terraform" vpc_id: "" => "<computed>" aws_security_group.sg_DefaultWebserver: Creation complete (ID: sg-f26b8c82) aws_instance.churrops: Still creating... (10s elapsed) aws_instance.churrops: Still creating... (20s elapsed) aws_instance.churrops: Still creating... (31s elapsed) aws_instance.churrops: Still creating... (41s elapsed) aws_instance.churrops: Creation complete (ID: i-010171b96e02657d5) Apply complete! Resources: 2 added, 0 changed, 0 destroyed. The state of your infrastructure has been saved to the path below. This state is required to modify and destroy your infrastructure, so keep it safe. To inspect the complete state use the `terraform show` command. State path: Outputs: ip = 52.54.109.23
>> State
Ao final da execução o terraform cria um arquivo com o estado da Infraestrutura chamado: terraform.tfstate
, esse arquivo é importante para que o terraform tenha controle das alterações, para mais informações:
https://www.terraform.io/docs/state/
>> terraform graph
O comando de graph
é usado para gerar uma representação visual de uma configuração ou plano de execução. A saída está no formato DOT, que pode ser usado pelo GraphViz para gerar gráficos.
https://www.terraform.io/docs/commands/graph.html
~$ terraform graph
digraph { compound = "true" newrank = "true" subgraph "root" { "[root] aws_instance.churrops" [label = "aws_instance.churrops", shape = "box"] "[root] aws_security_group.sg_DefaultWebserver" [label = "aws_security_group.sg_DefaultWebserver", shape = "box"] "[root] provider.aws" [label = "provider.aws", shape = "diamond"] "[root] aws_instance.churrops" -> "[root] provider.aws" "[root] aws_instance.churrops" -> "[root] var.amis" "[root] aws_instance.churrops" -> "[root] var.instance_type" "[root] aws_instance.churrops" -> "[root] var.key_name" "[root] aws_security_group.sg_DefaultWebserver" -> "[root] provider.aws" "[root] meta.count-boundary (count boundary fixup)" -> "[root] aws_security_group.sg_DefaultWebserver" "[root] meta.count-boundary (count boundary fixup)" -> "[root] output.ip" "[root] output.ip" -> "[root] aws_instance.churrops" "[root] provider.aws (close)" -> "[root] aws_instance.churrops" "[root] provider.aws (close)" -> "[root] aws_security_group.sg_DefaultWebserver" "[root] provider.aws" -> "[root] var.aws_access_key" "[root] provider.aws" -> "[root] var.aws_region" "[root] provider.aws" -> "[root] var.aws_secret_key" "[root] root" -> "[root] meta.count-boundary (count boundary fixup)" "[root] root" -> "[root] provider.aws (close)" } }
Utilizando um gerador de Graphiz online, podemos ver o resultado!
>> terraform show
~$ terraform show
O comando terraform show é usado para fornecer saída legível por humanos de um arquivo de estado ou plano. Isso pode ser usado para inspecionar um plano para assegurar que as operações planejadas são esperadas, ou para inspecionar o estado atual como Terraform vê.
aws_instance.churrops: id = i-010171b96e02657d5 ami = ami-a4c7edb2 associate_public_ip_address = true availability_zone = us-east-1c disable_api_termination = false ebs_block_device.# = 0 ebs_optimized = false ephemeral_block_device.# = 0 iam_instance_profile = instance_state = running instance_type = t2.micro ipv6_addresses.# = 0 key_name = rdglinux-awskey-us monitoring = false network_interface.# = 0 network_interface_id = eni-5335dd86 primary_network_interface_id = eni-5335dd86 private_dns = ip-172-31-0-196.ec2.internal private_ip = 172.31.0.196 public_dns = ec2-52-54-109-23.compute-1.amazonaws.com public_ip = 52.54.109.23 root_block_device.# = 1 root_block_device.0.delete_on_termination = true root_block_device.0.iops = 100 root_block_device.0.volume_size = 8 root_block_device.0.volume_type = gp2 security_groups.# = 1 security_groups.3344374217 = sg_DefaultWebserver source_dest_check = true subnet_id = subnet-fe0402b7 tags.% = 3 tags.Name = churrops tags.Provider = terraform tags.Role = test tenancy = default user_data = 37f0a6f40ea50e1784ae62ac96c55fe1aac34ac3 volume_tags.% = 0 vpc_security_group_ids.# = 0 aws_security_group.sg_DefaultWebserver: id = sg-f26b8c82 description = Allow all outbound traffic and inbound 22/80 egress.# = 1 egress.482069346.cidr_blocks.# = 1 egress.482069346.cidr_blocks.0 = 0.0.0.0/0 egress.482069346.from_port = 0 egress.482069346.ipv6_cidr_blocks.# = 0 egress.482069346.prefix_list_ids.# = 0 egress.482069346.protocol = -1 egress.482069346.security_groups.# = 0 egress.482069346.self = false egress.482069346.to_port = 0 ingress.# = 2 ingress.2214680975.cidr_blocks.# = 1 ingress.2214680975.cidr_blocks.0 = 0.0.0.0/0 ingress.2214680975.from_port = 80 ingress.2214680975.ipv6_cidr_blocks.# = 0 ingress.2214680975.protocol = tcp ingress.2214680975.security_groups.# = 0 ingress.2214680975.self = false ingress.2214680975.to_port = 80 ingress.2541437006.cidr_blocks.# = 1 ingress.2541437006.cidr_blocks.0 = 0.0.0.0/0 ingress.2541437006.from_port = 22 ingress.2541437006.ipv6_cidr_blocks.# = 0 ingress.2541437006.protocol = tcp ingress.2541437006.security_groups.# = 0 ingress.2541437006.self = false ingress.2541437006.to_port = 22 name = sg_DefaultWebserver owner_id = 745505968289 tags.% = 2 tags.Name = sg_DefaultWebserver tags.Provisioner = terraform vpc_id = vpc-bc2f4ada Outputs: ip = 52.54.109.23
>> Será que funcionou? Vamos validar
Foi passado um output
na declaração onde o terraform nos devolveu o IP da nossa instância criada, ou seja, com o output podemos extrair informações dos recursos criados de forma simples
$ terraform output ip = 52.54.109.23
Podemos ver no painel da AWS que a nossa instância foi criada conforme solicitamos
Como utilizamos o UserData
da AWS para a instalação do NGINX
podemos abrir a página no Browser e validar que está acessível.
SENSACIONAL, tudo funcionando, se chegou até aqui parabéns você acaba que criar uma Infraestrutura como código!
>> Destruindo tudo o que foi criado
Sucesso!!! já construimos a nossa Infra, validamos e podemos ver que realmente funciona, agora vamos destruir os recursos criados utilizando o próprio terraform, para isso utilizamos o terraform destroy
ATENÇÃO: MUITO CUIDADO ao usar o destroy, pois ele DESTROY DE VERDADE, se der esse comando quando não deveria, você pode ter sérios problemas
~$ terraform destroy Do you really want to destroy? Terraform will delete all your managed infrastructure. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes var.aws_access_key Enter a value: var.aws_secret_key Enter a value:
Simples assim, abaixo podemos ver que os recursos estão sendo destruídos
aws_security_group.sg_DefaultWebserver: Refreshing state... (ID: sg-f26b8c82) aws_instance.churrops: Refreshing state... (ID: i-010171b96e02657d5) aws_security_group.sg_DefaultWebserver: Destroying... (ID: sg-f26b8c82) aws_instance.churrops: Destroying... (ID: i-010171b96e02657d5) aws_instance.churrops: Still destroying... (ID: i-010171b96e02657d5, 10s elapsed) aws_security_group.sg_DefaultWebserver: Still destroying... (ID: sg-f26b8c82, 10s elapsed) aws_instance.churrops: Still destroying... (ID: i-010171b96e02657d5, 20s elapsed) aws_security_group.sg_DefaultWebserver: Still destroying... (ID: sg-f26b8c82, 20s elapsed) aws_security_group.sg_DefaultWebserver: Still destroying... (ID: sg-f26b8c82, 30s elapsed) aws_instance.churrops: Still destroying... (ID: i-010171b96e02657d5, 30s elapsed) aws_instance.churrops: Still destroying... (ID: i-010171b96e02657d5, 40s elapsed) aws_security_group.sg_DefaultWebserver: Still destroying... (ID: sg-f26b8c82, 40s elapsed) aws_instance.churrops: Still destroying... (ID: i-010171b96e02657d5, 50s elapsed) aws_security_group.sg_DefaultWebserver: Still destroying... (ID: sg-f26b8c82, 50s elapsed) aws_security_group.sg_DefaultWebserver: Still destroying... (ID: sg-f26b8c82, 1m0s elapsed) aws_instance.churrops: Still destroying... (ID: i-010171b96e02657d5, 1m0s elapsed) aws_instance.churrops: Destruction complete aws_security_group.sg_DefaultWebserver: Destruction complete Destroy complete! Resources: 2 destroyed.
Ao final vemos que foram destruídos um total de 2 recursos!
E podemos olhar no painel da AWS que a instância foi terminada com sucesso!
>> Conclusão
Bom, esse não foi um artigo fácil de se escrever, me custou bastante testes para validar o funcionamento de fato dos recursos criados, espero que assim como eu aprendi você possa também se beneficiar com esse tutorial.
O Terraform nos dá MUITAS, mais MUITAS possibilidades mesmo, aqui não tem as melhores práticas, ou as melhores formas de se utilizar, mais acredito que com esse Post já fica mais fácil na evolução ferramenta!
Obrigado a todos e não deixem de compartilhar!
02/10/2017 at 4:43 pm
Show!!!! impossível nao compreender.
CurtirCurtido por 1 pessoa
05/03/2019 at 1:12 pm
Sensacional, esse artigo ajudou muito.
CurtirCurtido por 1 pessoa