Terraform INIT + PLAN
Tags: terraform, hashicorp, curso
Published: 2021-03-21 12:51:32

---
title: Terraform INIT + PLAN
published: true
description: 
tags: terraform, hashicorp, curso
cover_image: https://cdn.cycloid.io/s3fs-public/2020-05/terraform_logo_300.png
---

# Curso Completo Gratis!

Esse post faz parte de um curso completo sobre Terraform, caso voce queira saber mais sobre terraform da um pulo no meu [blog](https://igordcsouza.com) ou no meu [youtube](https://www.youtube.com/channel/UC3yoPZJdjwOjrHWy_iEP08A) para ter acesso ao curso completo 100% gratuito.

# Mas se quiser contribuir:
[Aqui no Udemy](https://www.udemy.com/course/terraform-na-pratica/?referralCode=6DAB87D1325C238B4745) tem o mesmo curso que esta disponivel 100% gratuito no youtube pra quem quiser e puder contribuir financeiramete :)

## Terraform Init

Agora que já temos uma noção de como a estrutura de pasta e arquivos funcionam, vamos conhecer os principais comandos do Terraform. O primeiro comando dos nossos 4 principais é o terraform init.

Para entender na prática o que ele faz vamos criar um arquivo chamado `main.tf` e adicionar o seguinte:

```
provider "digitalocean" {}
```

Vamos falar mais pra frente sobre providers, mas o que você precisa saber nesse momento é que cada provider é como se fosse um dicionário. No nosso caso, estamos baixando um dicionário para ensinar o nosso código a se comunicar com a API do Digital Ocean.

Para manter um tamanho decente, nosso binário não vem com todos os providers por padrão. Então precisamos baixar o nosso plugin antes de executar qualquer outro comando.

Para isso que temos o comando:

```
terraform init
```

Esse comando analisa todo nosso código terraform e encontra todos os `dicionários` declarados e faz download do provider para que possamos utilizar os seus recursos.

Um problema diferença na versão atual, é que agora precisamos especificar exatamente qual o provedor estamos utilizando.

Abra o terminal e na mesma pasta que criamos o arquivo main.tf execute o comando e verá uma saída parecida com a seguinte:

```
terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/digitalocean...

Error: Failed to query available provider packages

Could not retrieve the list of available versions for provider
hashicorp/digitalocean: provider registry registry.terraform.io does not have
a provider named registry.terraform.io/hashicorp/digitalocean

If you have just upgraded directly from Terraform v0.12 to Terraform v0.14
then please upgrade to Terraform v0.13 first and follow the upgrade guide for
that release, which might help you address this problem.

Did you intend to use digitalocean/digitalocean? If so, you must specify that
source address in each module which requires that provider. To see which
modules are currently depending on hashicorp/digitalocean, run the following
command:
    terraform providers
```

Para resolver isso vamos criar um arquivo chamado de `versions.tf` e vamos adicionar o seguinte:

```
 terraform {
    required_version = ">= 0.14, < 0.15"
    required_providers {
        digitalocean = {
            source  = "digitalocean/digitalocean"
        }
    }
}
```

Nesse caso estamos deixando explícito para o terraform que quando utilizarmos o provider `digitalocean` ele deve olhar o provider que se encontra no registry utilizando o path `digitalocean/digitalocean`. Eu não vou colocar uma versão específica do provider pois quero usar sempre a última disponível, mas é uma boa prática especificar uma versão principalmente se você estiver fazendo uso do código em produção. 

Vamos aproveitar pra cadastrar uma versão mínima e máxima do terraform, a princípio as próximas versões não devem ter muitas break changes, mas quando lançarem a 0.15.x eu atualizo esse material!

Vamos rodar novamente o `terraform init`:

```
terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of digitalocean/digitalocean...
- Installing digitalocean/digitalocean v2.5.1...
- Installed digitalocean/digitalocean v2.5.1 (signed by a HashiCorp partner, key ID F82037E524B9C0E8)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```


Podemos ver que a versão do provider baixada foi a “2.5.1” e agora podemos seguir para conhecer os próximos comandos pois nosso código está pronto para ser “compreendido” pelo terraform.

Um ponto importante aqui e o arquivo `.terraform.lock.hcl`, ele se parece com isso:

```
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.

provider "registry.terraform.io/digitalocean/digitalocean" {
  version = "2.5.1"
  hashes = [
    "h1:k9itTwJzUpMBTYsXYPoEW/fyoDOcteQc4+OMRmFErbc=",
    "zh:057b8fa0f95213e7d856208d456175335fb673cfef14abf41193f0a2d76e1210",
    "zh:0daee13dd46de95ce2550459942c1433290798bfb5faac12781f81799dd6b05c",
    "zh:13778c00db5c43b2ed5781e2de32d73f34b391c865a52ad3380714bf86251785",
    "zh:2b2bbb1b057c8bf15804a9fd47c30f30b39bcd7ed478bfcad11e221c654f5d02",
    "zh:43284d2b1a356f541723a46219812590d24742558ef4111eda545212fd60f011",
    "zh:6a6e13b55f9aa889e3162d75cb3e585116e8a0d12084629af38f68cdac6aa777",
    "zh:6fa3dbbad99a075768e9449fc6082769da1b76ae31a8e296ae50899835e859a1",
    "zh:79336598d190f511cf3d3323b49081474669d0daa9c1c0d3b21475110ad97bd9",
    "zh:84c4c8d29820229bd94f7d3c5310f1f7208b97e7d4efca2c8e24ae0c0e032267",
    "zh:86926853140d9072986d2cb8ff4693784abd5f5d241b8cec402dfad77d8060ed",
    "zh:95a896f51656b51519b10edf38f11eb766de60297b8551dc0d14a4041dd16d6f",
    "zh:d163da24466cd60eed4749fef56c6593cc6e33be2e210e1b57edfd1c968aa742",
    "zh:e830649afac9e505603002f8a76b2441a0a41c96c6516609e2c07ce0c45f9dc3",
  ]
}
```

Esse arquivo é mantido pelo próprio comando `init` e deve ser adicionado ao seu repositório. Isso ajuda a manter a versão da mesma forma que você faria setando uma versão direto no arquivo `versions.tf`.

Caso você queira atualizar a versão de um provider onde existe esse arquivo, basta executar o comando `terraform init -upgrade=true`, ele vai baixar a última versão disponível do provider sempre respeitando qualquer restrição que você tenha adicionado. Caso você tenha mantido como o meu, vai sempre ter a versão mais nova e consequentemente mais instável.

## Terraform plan

Agora que já rodamos o init e fizemos download da biblioteca da DigitalOcean, vamos fazer uso dos recursos disponíveis para entender melhor como o comando plan funciona. A primeira coisa que iremos fazer é abrir a documentação do terraform na parte que se refere a biblioteca da DigitalOcean (https://www.terraform.io/docs/providers/do/index.html).

Se você prestar atenção vai ver que existem basicamente 3 itens no menu lateral:

* DigitalOcean Provider: Aqui você encontra uma explicação de como configurar corretamente o provider no seu código. Ele explica coisas como quais variáveis ele busca por padrão no seu ambiente e quais parâmetros são aceitos.

* Data Sources: Não é incomum precisar de algum recurso que foi criado em outro código terraform ou mesmo de forma manual, e são nesses momentos que o Data Source pode nos ajudar. Com ele podemos pegar alguns valores de recursos já criados para utilizar no nosso código. Vamos entender mais pra frente com detalhes. 

* Resources: Todos os itens dentro desse menu se referem a como criar e gerenciar recursos diretamente pelo terraform. Ou seja, se queremos criar um droplet e aqui dentro que vamos buscar.

Nesse momento já configuramos o nosso provider, então vamos criar um droplet usando o recurso para isso. Uma dica: apesar da navegação na documentação ser bem simples as vezes é mais rápido buscar no google por “terraform + versão + recurso”. No nosso caso seria: “terraform 0.14 droplet”.

Agora que já encontramos a página vamos copiar exatamente o exemplo disponível na página e colar logo abaixo do nosso provider la no arquivo `main.tf`:

```
resource "digitalocean_droplet" "web" {
  image  = "ubuntu-20-04-x64"
  name   = "web-1"
  region = "nyc2"
  size   = "s-1vcpu-1gb"
}
```

Salve o arquivo e volte para o terminal, nele vamos executar o seguinte comando:

```
terraform plan
```

A saída desse comando deve ser algo mais ou menos assim:


```
terraform plan

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # digitalocean_droplet.web will be created
  + resource "digitalocean_droplet" "web" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "web-1"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "nyc2"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

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

```



O `terraform plan` nunca vai alterar o seu arquivo de estado esteja ele local ou remoto. Mas o que é arquivo de estado? Temos um capítulo inteiro para falar sobre isso, mas por hora entenda que o arquivo de estado é o local onde o terraform armazena toda informação sobre sua infraestrutura. Quando a gente pede pra ele planejar a execução ele usa esse arquivo de estado apenas para comparar o que deveria ser criado com o que ja foi criado. Mas e se eu perder o arquivo? Como eu faço pra usar esse arquivo em time? Calma pequeno gafanhoto, vamos falar sobre isso mais pra frente!


```
  # digitalocean_droplet.web will be created
  + resource "digitalocean_droplet" "web" {
      + backups              = false
      + created_at           = (known after apply)
      + disk                 = (known after apply)
      + id                   = (known after apply)
      + image                = "ubuntu-20-04-x64"
      + ipv4_address         = (known after apply)
      + ipv4_address_private = (known after apply)
      + ipv6                 = false
      + ipv6_address         = (known after apply)
      + locked               = (known after apply)
      + memory               = (known after apply)
      + monitoring           = false
      + name                 = "web-1"
      + price_hourly         = (known after apply)
      + price_monthly        = (known after apply)
      + private_networking   = (known after apply)
      + region               = "nyc2"
      + resize_disk          = true
      + size                 = "s-1vcpu-1gb"
      + status               = (known after apply)
      + urn                  = (known after apply)
      + vcpus                = (known after apply)
      + volume_ids           = (known after apply)
      + vpc_uuid             = (known after apply)
    }

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


Aqui conseguimos ver exatamente tudo que o terraform irá criar para nós com um símbolo de + do lado. Alguns valores do droplet foram pré fixados, como por exemplo a região (region) que já mostra o valor de nyc2, alguns outros valores apenas serão recebidos após a criação do droplet, como é o caso do preço por hora (price_hourly).

Existem duas variações importantes no `plan` que precisamos entender: 
a primeira é o que o terraform chama de update in-place , que basicamente é atualizar um valor de um parâmetro sem afetar o recurso a nível de precisarmos recriar o mesmo. Esse tipo de variação é apresentada da seguinte forma:

```
 An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # digitalocean_droplet.web will be updated in-place
  ~ resource "digitalocean_droplet" "web" {
        backups            = false
        created_at         = "2020-04-10T13:38:03Z"
        disk               = 25
        id                 = "188070139"
        image              = "ubuntu-18-04-x64"
        ipv4_address       = "167.172.25.191"
        ipv6               = false
        locked             = false
        memory             = 1024
        monitoring         = false
      ~ name               = "web-1" -> "web-2"
        price_hourly       = 0.00744
        price_monthly      = 5
        private_networking = false
        region             = "nyc3"
        resize_disk        = true
        size               = "s-1vcpu-1gb"
        status             = "active"
        tags               = []
        urn                = "do:droplet:188070139"
        vcpus              = 1
        volume_ids         = []
    }

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

Enquanto alguns parâmetros podem ser atualizados, alguns precisam que os recursos sejam recriados para que a alteração tenha efeito. No nosso caso poderíamos testar isso modificando a região do nosso droplet.
A saída seria mais ou menos assim:

```
 An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # digitalocean_droplet.web must be replaced
-/+ resource "digitalocean_droplet" "web" {
        backups              = false
      ~ created_at           = "2020-04-10T13:38:03Z" -> (known after apply)
      ~ disk                 = 25 -> (known after apply)
      ~ id                   = "188070139" -> (known after apply)
        image                = "ubuntu-18-04-x64"
      ~ ipv4_address         = "167.172.25.191" -> (known after apply)
      + ipv4_address_private = (known after apply)
        ipv6                 = false
      + ipv6_address         = (known after apply)
      + ipv6_address_private = (known after apply)
      ~ locked               = false -> (known after apply)
      ~ memory               = 1024 -> (known after apply)
        monitoring           = false
        name                 = "web-1"
      ~ price_hourly         = 0.00744 -> (known after apply)
      ~ price_monthly        = 5 -> (known after apply)
        private_networking   = false
      ~ region               = "nyc3" -> "nyc1" # forces replacement
        resize_disk          = true
        size                 = "s-1vcpu-1gb"
      ~ status               = "active" -> (known after apply)
      - tags                 = [] -> null
      ~ urn                  = "do:droplet:188070139" -> (known after apply)
      ~ vcpus                = 1 -> (known after apply)
      ~ volume_ids           = [] -> (known after apply)
    }

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

Quando essa situação ocorre, podemos ver exatamente qual o parâmetro está forçando uma recriação pois o terraform colocar uma mensagem “# forces replacements” ao lado desse parâmetro, além de colocar uma mensagem de alerta no próprio recurso avisando que “must be replaced”!

Outro local que podemos ver isso é no fim do comando onde temos um compilado das informações dizendo que destruiremos 1 recurso, criaremos 1 recurso e não alteramos nenhum. Ou seja, no nosso caso vamos destruir o que existe e criar um novo.