Info
Content

Terraform - ADV IT

Ссылка на плейлист


Файлы имеют расширение .tf


После устновки можно добавить completion

terraform -install-autocomplete

Для работы с aws:

  • Нужен пользователь с programmatic access и соответствующими полиси
  • И нужно указать access_key и secret_key в манифесте:
    provider "aws" {
      access_key = "AKIAZ****VWL"
      secret_key = "JOCF/mUjIZMX*****0"
      region     = "eu-west-3"
    }
    
  • Дальше можно работать (нужно выполнить terraform init чтобы терраформ скачал aws провайдер)

Важно понимать что во время terraform init будут скачаны тяжелые файлы (aws-провайдер - 200M)
Им в гите делать нечего, да и к тому же они бинарные
Поэтому нужно в .gitignore добавить .terraform


terraform plan покажет что будет сделано

[02:31:18] vandud@macbook: terraform-test [0]$ terraform plan

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_instance.vandud-test-ubuntu will be created
  + resource "aws_instance" "vandud-test-ubuntu" {
      + ami                                  = "ami-0f7cd40eac2214b37"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)
      + availability_zone                    = (known after apply)
      + cpu_core_count                       = (known after apply)
      + cpu_threads_per_core                 = (known after apply)
      + get_password_data                    = false
      + host_id                              = (known after apply)
      + id                                   = (known after apply)
      + instance_initiated_shutdown_behavior = (known after apply)
...

При terraform apply он выведет план и попросит ввести 'yes'

...
          + kms_key_id            = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.vandud-test-ubuntu: Creating...
aws_instance.vandud-test-ubuntu: Still creating... [10s elapsed]
aws_instance.vandud-test-ubuntu: Still creating... [20s elapsed]
aws_instance.vandud-test-ubuntu: Still creating... [30s elapsed]
aws_instance.vandud-test-ubuntu: Creation complete after 34s [id=i-00c0314fa4663a683]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
[02:35:19] vandud@macbook: terraform-test [0]$

Так как terraform идемпотентен, то повторный запуск ничего не сломает

[02:35:19] vandud@macbook: terraform-test [0]$ terraform apply
aws_instance.vandud-test-ubuntu: Refreshing state... [id=i-00c0314fa4663a683]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

После apply создается terraform.tfstate
В нем хранится инфа о созданной инфраструктуре и связь между инфраструктурой и описанием ресурсов из манифестов

Если удалить его то терраформ перестанет быть идемпотентным

Поэтому их хранят удаленно, например в s3

Вот пример где видно что после удаления этого файла терраформ хочет создать инфраструктуру заново:

[02:47:28] vandud@macbook: terraform-test [0]$ terraform apply
aws_instance.vandud-test-ubuntu: Refreshing state... [id=i-00c0314fa4663a683]
aws_instance.vandud-test-amazonlinux2: Refreshing state... [id=i-02a90eb65e901e6e8]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
[02:48:04] vandud@macbook: terraform-test [0]$ rm terraform.tfstate
[02:48:18] vandud@macbook: terraform-test [0]$ 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_instance.vandud-test-amazonlinux2 will be created
  + resource "aws_instance" "vandud-test-amazonlinux2" {
      + ami                                  = "ami-0b3e57ee3b63dd76b"
      + arn                                  = (known after apply)
      + associate_public_ip_address          = (known after apply)

Хорошо что по умолчанию создается terraform.tfstate.backup

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: ^C

Interrupt received.
Please wait for Terraform to exit or data loss may occur.
Gracefully shutting down...

╷
│ Error: Error asking for approval: interrupted
│
│
╵
[02:50:50] vandud@macbook: terraform-test [1]$ ls
lesson-1.tf			terraform.tfstate.backup
[02:50:52] vandud@macbook: terraform-test [0]$ mv terraform.tfstate.backup terraform.tfstate
[02:50:58] vandud@macbook: terraform-test [0]$ terraform apply
aws_instance.vandud-test-amazonlinux2: Refreshing state... [id=i-02a90eb65e901e6e8]
aws_instance.vandud-test-ubuntu: Refreshing state... [id=i-00c0314fa4663a683]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
[02:51:06] vandud@macbook: terraform-test [0]$ ls
lesson-1.tf			terraform.tfstate		terraform.tfstate.backup
[02:51:18] vandud@macbook: terraform-test [0]$

После изменения типа виртуалок:
Screenshot_2021_02_02-12_49_03-2021-05-17-at-03ea.png

Сначала удалит старые машины и только после удаления начнет создавать новые


Вручную сделанные изменения будут откачены
Пример ниже показывает как терраформ хочет удалить тэг с сервера который я добавил руками через web

[03:15:48] vandud@macbook: terraform-test [0]$ terraform apply
aws_instance.vandud-test-ubuntu[0]: Refreshing state... [id=i-00c0314fa4663a683]
aws_instance.vandud-test-ubuntu[2]: Refreshing state... [id=i-0171014586a119442]
aws_instance.vandud-test-amazonlinux2: Refreshing state... [id=i-02a90eb65e901e6e8]
aws_instance.vandud-test-ubuntu[1]: Refreshing state... [id=i-08b0b48d29e69ca8e]

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_instance.vandud-test-ubuntu[2] will be updated in-place
  ~ resource "aws_instance" "vandud-test-ubuntu" {
        id                                   = "i-0171014586a119442"
      ~ tags                                 = {
          - "astoeu"  = "aoeuaoeuaoeu" -> null
            # (3 unchanged elements hidden)
        }
      ~ tags_all                             = {
          - "astoeu"  = "aoeuaoeuaoeu" -> null
            # (3 unchanged elements hidden)
        }
        # (27 unchanged attributes hidden)




        # (4 unchanged blocks hidden)
    }

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

Для удаления нужно удалить описание ресурса или можно поставить count = 0


destroy удалит все что контролируется терраформом


Хранить креды в манифесте нельзя
Можно вынести их в окружение

[03:37:14] vandud@macbook: terraform-test [0]$ export AWS_ACCESS_KEY_ID=AKIA***O2VWL
[04:07:10] vandud@macbook: terraform-test [0]$ export AWS_SECRET_ACCESS_KEY=JOCF/mU****HTNN1FIcGR8Sd0

Функция file() позволяет подставить на свое место содержимое файла

user_data = file("user_data.sh")

Темплейты принято обозначать расширением .tpl, например user_data.sh.tpl

Вызвать темплейт можно через функцию templatefile

  user_data = templatefile("user_data.sh.tpl", {
    f_name = "Ivan",
    l_name = "Dudin",
    names = ["Alice", "Bob", "Calley", "Den"]
  })

И сразу же в этой функции в шаблон передаются переменные

Сам шаблон:

[08:02:00] vandud@macbook: terraform-test [0]$ cat user_data.sh.tpl
#!/bin/bash
apt update
apt install nginx -y
myip=`curl http://2ip.ru`
echo "<h2>WebServer with IP: $myip </h2><br>Build by Terraform!<br><h1>user_data in external file!</h1>" > /var/www/html/index.html
cat <<EOF >> /var/www/html/index.html
Owner: ${f_name} ${l_name} <br>

%{ for name in names ~}
Hello ${name}!<br>
%{ endfor ~}

EOF
systemctl start nginx

Результат:
Screenshot_2021_02_02-12_49_03-2021-05-17-ataoeuaou.png


Комментарии делаются так

/*
comment
*/

Цикл

resource "aws_security_group" "vandud_webserver_allow_http" {
  name        = "vandud_webserver_allow_http"
  description = "Allow HTTP traffic"

  dynamic "ingress" {
    for_each = ["80", "443"]
    content {
      from_port        = ingress.value
      to_port          = ingress.value
      protocol         = "tcp"
      cidr_blocks      = ["0.0.0.0/0"]
    }
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
  }

  tags = {
    Name = "allow_http_with_for_each"
  }
}

Запретить удаление:

lifecycle { prevent_destroy = true }
[09:54:53] vandud@macbook: terraform-test [0]$ terraform apply
aws_security_group.vandud_webserver_allow_http: Refreshing state... [id=sg-0f78c46cdf8f193d4]
aws_instance.vandud-test-ubuntu: Refreshing state... [id=i-0db43c9b8a2b0f6f1]
╷
│ Error: Instance cannot be destroyed
│
│   on terraform.tf line 5:
│    5: resource "aws_instance" "vandud-test-ubuntu" {
│
│ Resource aws_instance.vandud-test-ubuntu has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable
│ lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.

Игнорировать какие-то конкретные изменения:

lifecycle { ignore_changes = [user_data] }

elastic ip

resource "aws_eip" "my_static_ip" {
  instance = aws_instance.vandud-test-ubuntu.id
  tags = { Name = "Elastic IP for Ubuntu" }
}

Сначала создать новый, потом удалить старый:

lifecycle { create_before_destroy = true }

Работает так

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.vandud-test-ubuntu: Creating...
aws_instance.vandud-test-ubuntu: Still creating... [10s elapsed]
aws_instance.vandud-test-ubuntu: Still creating... [20s elapsed]
aws_instance.vandud-test-ubuntu: Still creating... [30s elapsed]
aws_instance.vandud-test-ubuntu: Creation complete after 34s [id=i-0bee1bc2c8ee22d92]
aws_eip.my_static_ip: Modifying... [id=eipalloc-00ba6881fbe733088]
aws_eip.my_static_ip: Modifications complete after 2s [id=eipalloc-00ba6881fbe733088]
aws_instance.vandud-test-ubuntu (8e7594e5): Destroying... [id=i-0db43c9b8a2b0f6f1]
aws_instance.vandud-test-ubuntu: Still destroying... [id=i-0db43c9b8a2b0f6f1, 10s elapsed]
aws_instance.vandud-test-ubuntu: Still destroying... [id=i-0db43c9b8a2b0f6f1, 20s elapsed]
aws_instance.vandud-test-ubuntu: Still destroying... [id=i-0db43c9b8a2b0f6f1, 30s elapsed]
aws_instance.vandud-test-ubuntu: Still destroying... [id=i-0db43c9b8a2b0f6f1, 40s elapsed]
aws_instance.vandud-test-ubuntu: Destruction complete after 42s

Apply complete! Resources: 1 added, 1 changed, 1 destroyed.

Выше видно что сначала удалил, потом создал
То же самое было видно в консоли


После выполнения можно выводить какую-то информацию

output "webserver_instance_id" { value = aws_instance.vandud-test-ubuntu.id }
output "webserver_public_ip_address" { value = aws_eip.my_static_ip.public_ip }
[10:20:16] vandud@macbook: terraform-test [0]$ terraform apply
aws_instance.vandud-test-ubuntu: Refreshing state... [id=i-0bee1bc2c8ee22d92]
aws_security_group.vandud_webserver_allow_http: Refreshing state... [id=sg-0f78c46cdf8f193d4]
aws_eip.my_static_ip: Refreshing state... [id=eipalloc-00ba6881fbe733088]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

webserver_instance_id = "i-0bee1bc2c8ee22d92"
webserver_public_ip_address = "13.36.217.252"

Для этого даже есть отдельная команда

[10:20:44] vandud@macbook: terraform-test [0]$ terraform output
webserver_instance_id = "i-0bee1bc2c8ee22d92"
webserver_public_ip_address = "13.36.217.252"

Манифесты могут быть разделены на несколько файлов

vandud@macbook: terraform-test [0]$ cat outputs.tf
output "webserver_instance_id" { value = aws_instance.vandud-test-ubuntu.id }
output "webserver_public_ip_address" { value = aws_eip.my_static_ip.public_ip }
vandud@macbook: terraform-test [0]$ terraform apply
aws_security_group.vandud_webserver_allow_http: Refreshing state... [id=sg-0f78c46cdf8f193d4]
aws_instance.vandud-test-ubuntu: Refreshing state... [id=i-0bee1bc2c8ee22d92]
aws_eip.my_static_ip: Refreshing state... [id=eipalloc-00ba6881fbe733088]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

webserver_instance_id = "i-0bee1bc2c8ee22d92"
webserver_public_ip_address = "13.36.217.252"

Терраформ читает все .tf файлы и понимает что к чему


depends_on позволяет делать зависимости

  depends_on = [aws_instance.vandud-test-ubuntu]

При циклической зависимости ничего не заведется


Полезно использовать разную информацию через data sources

No Comments
Back to top