Przejdź do treści
Automatyzacja i DevOps · ~15 min czytania

Terraform od podstaw: providery, stan, zmienne i moduły

Jak opisujemy całą infrastrukturę jako kod — powtarzalny, wersjonowany i odtwarzalny — i dlaczego to bezpośrednio przekłada się na mniejszą liczbę awarii i krótszy czas reakcji.

Autor: Zespół LinuxLab

W LinuxLab korzystamy z Terraform, aby przyspieszać i automatyzować powtarzalne zadania związane z infrastrukturą — tworzenie serwerów, sieci, reguł firewalla, rekordów DNS czy certyfikatów. Cała infrastruktura jest opisana w kodzie, wersjonowana w git i odtwarzalna jednym poleceniem. To przekłada się na skuteczne zapobieganie awariom serwerów, którymi się opiekujemy: eliminujemy ręczne pomyłki w panelach, utrzymujemy spójne środowiska i potrafimy odtworzyć całą platformę w minuty, a nie w godziny.

W tym artykule wyjaśniamy, czym jest Terraform i jak w praktyce zarządzać nim infrastrukturą jako kodem (IaC) — od języka HCL i providerów, przez zasoby i kluczowe pojęcie stanu (state), cykl init / plan / apply, zmienne i outputy, po moduły i bezpieczne przechowywanie sekretów. Pokazujemy też, jak Terraform i Ansible uzupełniają się w pracy LinuxLab i jak razem ograniczają liczbę awarii.

Spis treści

  1. Czym jest Terraform?
  2. Język HCL
  3. Providery
  4. Zasoby i data sources
  5. Stan (state)
  6. Cykl pracy: init, plan, apply
  7. Zmienne i outputy
  8. Moduły
  9. Idempotencja i graf zależności
  10. Bezpieczeństwo i sekrety
  11. Terraform a Ansible w LinuxLab
  12. Podsumowanie

Czym jest Terraform?

Terraform to narzędzie do zarządzania infrastrukturą jako kodem (Infrastructure as Code). Zamiast klikać w panelach dostawców chmury czy hostingu, opisujemy pożądany stan infrastruktury w plikach tekstowych, a Terraform tworzy, zmienia i usuwa zasoby tak, aby rzeczywistość odpowiadała temu opisowi.

Trzy cechy, które wyróżniają je w praktyce:

  • Deklaratywność — opisujemy co ma istnieć („serwer o tych parametrach, z tym adresem IP"), a nie krok po kroku jak to utworzyć. Terraform sam wylicza, jakie operacje wykonać.
  • Niezależność od dostawcy — ten sam język (HCL) opisuje zasoby w różnych chmurach i usługach dzięki providerom: AWS, Azure, GCP, Hetzner, OVH, Cloudflare, DNS, bazy danych i wiele innych.
  • Świadomość stanu — Terraform pamięta, co już utworzył, dzięki czemu wie, co dodać, a czego nie ruszać. To fundament bezpiecznych, powtarzalnych zmian.

Warto od razu rozróżnić role: Terraform tworzy infrastrukturę (provisioning — serwery, sieci, DNS), a narzędzia takie jak Ansible konfigurują to, co już istnieje (pakiety, usługi, pliki). Wracamy do tego na końcu artykułu.

Język HCL — opis infrastruktury

Konfigurację piszemy w HCL (HashiCorp Configuration Language) — czytelnym języku opartym na blokach. Pliki mają rozszerzenie .tf, a Terraform wczytuje wszystkie pliki .tf z bieżącego katalogu. Podstawowy blok wygląda tak:

# main.tf — typowy blok zasobu
resource "hcloud_server" "web" {
  name        = "web-01"
  server_type = "cx22"
  image       = "debian-12"
  location    = "nbg1"
}

Składnia resource "<typ>" "<nazwa_lokalna>" definiuje zasób danego typu, a w nawiasach klamrowych podajemy jego atrybuty. Do zasobu odwołujemy się później przez hcloud_server.web.

Providery — wtyczki do API

Provider to wtyczka, która tłumaczy konfigurację HCL na wywołania API konkretnej platformy. To provider „wie", jak utworzyć serwer w Hetznerze albo rekord w Cloudflare. Wymagane providery i ich wersje deklarujemy jawnie — dzięki temu zespół zawsze pracuje na tych samych, przewidywalnych wersjach.

# versions.tf
terraform {
  required_version = ">= 1.6"
  required_providers {
    hcloud = {
      source  = "hetznercloud/hcloud"
      version = "~> 1.45"
    }
  }
}

# providers.tf
provider "hcloud" {
  token = var.hcloud_token   # sekret przekazany przez zmienną
}

Zasoby i data sources

Zasób (resource) to obiekt zarządzany przez Terraform — serwer, wolumen, sieć, rekord DNS. Terraform odpowiada za jego pełny cykl życia: utworzenie, aktualizację i usunięcie.

Data source (data) służy z kolei do odczytu istniejących, niezarządzanych przez nas informacji — np. najnowszego obrazu systemu — bez ich modyfikowania. Zasoby można też ze sobą łączyć, odwołując się do atrybutów: Terraform sam wykryje zależności.

# Odczyt istniejącej sieci (data source)
data "hcloud_network" "core" {
  name = "core-net"
}

# Zasób korzystający z atrybutu innego obiektu
resource "hcloud_server_network" "web_net" {
  server_id  = hcloud_server.web.id
  network_id = data.hcloud_network.core.id
}

Stan (state) — pamięć Terraform

Stan (plik terraform.tfstate) to mapa łącząca obiekty z konfiguracji z realnymi zasobami u dostawcy (po ich identyfikatorach). To dzięki stanowi Terraform wie, że hcloud_server.web to konkretny, już istniejący serwer — a nie powód, by tworzyć kolejny.

Stan jest krytyczny i wrażliwy, dlatego w pracy zespołowej trzymamy go w backendzie zdalnym (np. S3, Terraform Cloud) z blokadą (locking), która zapobiega równoczesnym zmianom przez dwie osoby:

# backend.tf — stan zdalny z blokadą
terraform {
  backend "s3" {
    bucket         = "linuxlab-tfstate"
    key            = "klient-x/prod.tfstate"
    region         = "eu-central-1"
    dynamodb_table = "tf-locks"   # blokada współbieżności
    encrypt        = true
  }
}

Dlaczego to ważne dla stabilności? Zdalny, zaszyfrowany stan z blokadą oznacza jedno wspólne „źródło prawdy" o infrastrukturze. Nikt nie nadpisze zmian kolegi, a w razie awarii odtworzenie środowiska to kwestia ponownego apply, a nie żmudnego odtwarzania konfiguracji z pamięci.

Cykl pracy: init, plan, apply

Codzienna praca z Terraform sprowadza się do trzech poleceń:

# 1. Inicjalizacja — pobranie providerów i konfiguracja backendu
terraform init

# 2. Plan — pokaż, co dokładnie się zmieni (bez wprowadzania zmian)
terraform plan

# 3. Apply — wprowadź zmiany po zatwierdzeniu
terraform apply

Najważniejszy z punktu widzenia bezpieczeństwa jest terraform plan. Pokazuje dokładnie, co zostanie utworzone (+), zmienione (~) lub usunięte (-), zanim cokolwiek się wydarzy:

  # hcloud_server.web will be updated in-place
  ~ resource "hcloud_server" "web" {
        id          = "12345678"
      ~ server_type = "cx22" -> "cx32"
        # (pozostałe atrybuty bez zmian)
    }

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

Ten przegląd to nasza „siatka ochronna": zmianę zatwierdzamy świadomie, a niebezpieczne operacje (np. 1 to destroy na produkcji) wychwytujemy zanim się wykonają. Do trwałego usuwania środowiska służy terraform destroy — używane głównie dla środowisk testowych.

Zmienne i outputy

Aby konfiguracja była elastyczna, oddzielamy logikę od danych za pomocą zmiennych (variable). To pozwala tym samym kodem stawiać środowiska dev, staging i prod, zmieniając jedynie wartości.

# variables.tf
variable "server_type" {
  description = "Typ serwera Hetzner"
  type        = string
  default     = "cx22"
}

variable "hcloud_token" {
  description = "Token API Hetzner Cloud"
  type        = string
  sensitive   = true   # nie pokazuj wartości w logach
}

Wartości przekazujemy w plikach *.tfvars (np. prod.tfvars) lub przez zmienne środowiskowe TF_VAR_*. Z kolei outputy (output) zwracają istotne wartości po zakończeniu — np. adres IP utworzonego serwera, który przekażemy dalej do konfiguracji:

# outputs.tf
output "web_ip" {
  value = hcloud_server.web.ipv4_address
}

Moduły — ponowne użycie

Gdy konfiguracja rośnie, porządkujemy ją w moduły — samodzielne, wielokrotnego użytku paczki zasobów o zdefiniowanych wejściach (zmienne) i wyjściach (outputy). Moduł raz napisany i przetestowany wdrażamy u wielu klientów, zachowując ten sam standard.

modules/
└── webserver/
    ├── main.tf        # zasoby modułu
    ├── variables.tf   # wejścia modułu
    └── outputs.tf     # wyjścia modułu

# Użycie modułu w głównej konfiguracji:
module "web_prod" {
  source      = "./modules/webserver"
  server_type = "cx32"
  environment = "prod"
}

Moduły to nasz sposób na standaryzację: poprawka wprowadzona raz w module propaguje się wszędzie tam, gdzie jest używany — spójnie i bez przeoczeń. To bezpośredni odpowiednik ról w Ansible.

Idempotencja i graf zależności

Terraform jest idempotentny: wielokrotne apply tej samej konfiguracji daje ten sam efekt co jednokrotne. Jeśli infrastruktura jest już w pożądanym stanie, plan pokaże „No changes" i nic się nie wydarzy. To pozwala bezpiecznie uruchamiać Terraform w dowolnym momencie, by wykryć i naprawić rozjazd konfiguracji (drift) — gdy ktoś zmienił coś ręcznie poza kodem.

Pod spodem Terraform buduje graf zależności między zasobami. Sam ustala właściwą kolejność (najpierw sieć, potem serwer w tej sieci) i zrównolegla operacje tam, gdzie to bezpieczne. Nie musimy ręcznie pilnować kolejności — wystarczy, że zasoby odwołują się do siebie.

Bezpieczeństwo i sekrety

Konfiguracja często wymaga danych wrażliwych: tokenów API, haseł, kluczy. Kilka zasad, których pilnujemy:

  • Nigdy nie commitujemy sekretów do repozytorium. Tokeny przekazujemy przez zmienne środowiskowe TF_VAR_* lub menedżer sekretów (np. Vault), a pliki *.tfvars z wartościami dodajemy do .gitignore.
  • Oznaczamy zmienne i outputy jako sensitive, aby Terraform nie wypisywał ich wartości w logach i planie.
  • Szyfrujemy i zabezpieczamy stan — plik tfstate może zawierać wartości wrażliwe, dlatego trzymamy go w zdalnym backendzie z szyfrowaniem i ograniczonym dostępem, nigdy w repozytorium.

Terraform a Ansible — jak łączymy je w LinuxLab

Terraform i Ansible nie konkurują — uzupełniają się, a my używamy ich razem:

  • Terraform tworzy i utrzymuje infrastrukturę: serwery, sieci, firewall, DNS, certyfikaty. Odpowiada na pytanie „co ma istnieć".
  • Ansible konfiguruje to, co Terraform powołał do życia: instaluje pakiety, ustawia usługi, wdraża aplikacje. Odpowiada na pytanie „jak ma być skonfigurowane" (zob. nasz wpis o Ansible).

Dzięki temu połączeniu Terraform pozwala nam:

  • przyspieszać powtarzalne zadania — całe środowisko stawiamy jednym apply, w minuty zamiast godzin ręcznej pracy w panelach;
  • utrzymywać spójne środowiska — dev, staging i prod opisuje ten sam kod, więc nie ma różnic „przypadkiem";
  • eliminować błędy ludzkieplan pokazuje skutki zmiany przed jej wykonaniem, a kod w git daje pełną historię i możliwość rollbacku;
  • szybko odtwarzać infrastrukturę — po awarii odtworzenie zasobów to ponowny apply z tego samego kodu.

Efekt jest mierzalny: mniej awarii, krótsze przestoje i przewidywalna, udokumentowana infrastruktura.

Podsumowanie

Terraform opiera się na kilku spójnych ideach: HCL opisuje pożądany stan; providery tłumaczą go na API platform; zasoby i data sources definiują i odczytują obiekty; stan łączy kod z rzeczywistością; cykl plan/apply czyni zmiany bezpiecznymi i świadomymi; a moduły porządkują i umożliwiają ponowne użycie. Razem zamieniają zarządzanie infrastrukturą z klikania w panelach w inżynierię opartą na kodzie — wersjonowaną, powtarzalną i odporną na pomyłki.

Potrzebujesz powtarzalnej, odpornej na awarie infrastruktury?

Projektujemy i utrzymujemy infrastrukturę jako kod (Terraform + Ansible), która ogranicza awarie i przyspiesza wdrożenia. Porozmawiajmy o Twoim środowisku — bezpłatnie i bez zobowiązań.