Przejdź do treści
DevOps

Terraform - Infrastructure as Code w praktyce. Kompletny przewodnik

Opublikowano:
·6 min czytania·Autor: MDS Software Solutions Group

Terraform Infrastructure as

devops

Terraform - Infrastructure as Code w praktyce

Zarządzanie infrastrukturą IT ewoluowało na przestrzeni lat od ręcznej konfiguracji serwerów, przez skrypty automatyzujące, aż po pełne podejście Infrastructure as Code. Terraform, stworzony przez HashiCorp, jest jednym z najpopularniejszych narzędzi IaC, które pozwala definiować, wersjonować i automatycznie wdrażać infrastrukturę w sposób deklaratywny. W tym przewodniku omówimy wszystkie kluczowe aspekty pracy z Terraform -- od podstaw składni HCL po zaawansowane wzorce produkcyjne.

Czym jest Infrastructure as Code i dlaczego jest potrzebne?#

Infrastructure as Code (IaC) to praktyka zarządzania infrastrukturą IT za pomocą plików konfiguracyjnych zamiast ręcznych procesów. Zamiast logować się do konsoli AWS czy Azure i klikać przyciski, definiujesz całą infrastrukturę w plikach tekstowych, które mogą być wersjonowane w Git, recenzowane w pull requestach i automatycznie wdrażane przez pipeline CI/CD.

Problemy bez IaC#

Bez podejścia IaC zespoły napotykają szereg problemów:

  • Snowflake servers -- każdy serwer konfigurowany ręcznie jest unikalny, co utrudnia odtworzenie środowiska
  • Brak powtarzalności -- nie ma gwarancji, że środowisko staging jest identyczne z produkcyjnym
  • Drift konfiguracji -- z czasem konfiguracja serwerów odchodzi od zamierzonego stanu
  • Brak audytu -- trudno prześledzić kto, kiedy i dlaczego zmienił konfigurację
  • Wolne wdrożenia -- ręczna konfiguracja jest powolna i podatna na błędy ludzkie

Korzyści z IaC#

Przyjęcie podejścia IaC rozwiązuje te problemy:

  • Wersjonowanie -- cała infrastruktura w Git z pełną historią zmian
  • Powtarzalność -- identyczne środowiska tworzone jednym poleceniem
  • Automatyzacja -- wdrożenia bez interwencji człowieka
  • Dokumentacja -- kod sam w sobie jest dokumentacją infrastruktury
  • Code review -- zmiany infrastrukturalne recenzowane jak kod aplikacyjny
  • Szybkie odtwarzanie -- disaster recovery sprowadza się do uruchomienia skryptu

Terraform vs CloudFormation vs Pulumi#

Na rynku istnieje kilka narzędzi IaC. Porównajmy trzy najpopularniejsze:

AWS CloudFormation#

CloudFormation jest natywnym narzędziem IaC od AWS. Jego główną zaletą jest głęboka integracja z ekosystemem AWS, ale działa wyłącznie z usługami Amazon. Pliki konfiguracyjne pisane są w JSON lub YAML, co przy dużych infrastrukturach staje się nieczytelne.

Pulumi#

Pulumi pozwala definiować infrastrukturę za pomocą popularnych języków programowania -- TypeScript, Python, Go czy C#. To świetne rozwiązanie dla zespołów, które wolą pisać infrastrukturę w języku, który już znają. Wadą jest większa złożoność i mniejsza baza społeczności.

Terraform#

Terraform łączy zalety obu podejść. Używa własnego języka HCL (HashiCorp Configuration Language), który jest deklaratywny, czytelny i zaprojektowany specjalnie do opisywania infrastruktury. Kluczowe przewagi Terraform to:

  • Multi-cloud -- jeden język do AWS, Azure, GCP i setek innych providerów
  • Ogromna społeczność -- tysiące modułów w Terraform Registry
  • Dojrzały ekosystem -- stabilne narzędzie z wieloletnią historią
  • Plan przed wdrożeniem -- terraform plan pokazuje dokładnie co się zmieni
  • State management -- śledzenie aktualnego stanu infrastruktury

Składnia HCL -- podstawy#

HashiCorp Configuration Language (HCL) jest deklaratywnym językiem zaprojektowanym specjalnie dla narzędzi HashiCorp. Jego składnia jest intuicyjna i łatwa do nauki.

Struktura pliku#

Typowy projekt Terraform składa się z kilku plików:

# main.tf - główna konfiguracja zasobów
# variables.tf - definicje zmiennych
# outputs.tf - wartości wyjściowe
# providers.tf - konfiguracja providerów
# terraform.tf - konfiguracja backendu i wymaganych providerów

Bloki konfiguracyjne#

HCL opiera się na blokach konfiguracyjnych. Każdy blok ma typ, opcjonalne etykiety i ciało:

# Blok zasobu
resource "aws_instance" "web_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"

  tags = {
    Name        = "WebServer"
    Environment = "production"
  }
}

# Blok danych
data "aws_ami" "ubuntu" {
  most_recent = true

  filter {
    name   = "name"
    values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
  }

  owners = ["099720109477"] # Canonical
}

# Blok zmiennej
variable "instance_type" {
  description = "Typ instancji EC2"
  type        = string
  default     = "t3.micro"

  validation {
    condition     = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
    error_message = "Dozwolone typy to: t3.micro, t3.small, t3.medium."
  }
}

Providery -- AWS, Azure, GCP#

Providery to wtyczki, które pozwalają Terraform komunikować się z platformami chmurowymi i innymi usługami. Każdy provider udostępnia zasoby (resources) i źródła danych (data sources).

Konfiguracja providerów#

# providers.tf

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0"
    }
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

# Provider AWS
provider "aws" {
  region = var.aws_region

  default_tags {
    tags = {
      Project     = var.project_name
      Environment = var.environment
      ManagedBy   = "terraform"
    }
  }
}

# Provider Azure
provider "azurerm" {
  features {}
  subscription_id = var.azure_subscription_id
}

# Provider GCP
provider "google" {
  project = var.gcp_project_id
  region  = var.gcp_region
}

Aliasy providerów#

Gdy potrzebujesz wielu instancji tego samego providera (np. różne regiony AWS):

provider "aws" {
  alias  = "eu_west"
  region = "eu-west-1"
}

provider "aws" {
  alias  = "us_east"
  region = "us-east-1"
}

resource "aws_s3_bucket" "eu_bucket" {
  provider = aws.eu_west
  bucket   = "my-eu-bucket"
}

resource "aws_s3_bucket" "us_bucket" {
  provider = aws.us_east
  bucket   = "my-us-bucket"
}

Resources i Data Sources#

Resources#

Resources to główne bloki budulcowe w Terraform. Każdy resource reprezentuje element infrastruktury, który Terraform tworzy i zarządza:

# Tworzenie VPC
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "${var.project_name}-vpc"
  }
}

# Tworzenie subnetu
resource "aws_subnet" "public" {
  count             = length(var.availability_zones)
  vpc_id            = aws_vpc.main.id
  cidr_block        = cidrsubnet(var.vpc_cidr, 8, count.index)
  availability_zone = var.availability_zones[count.index]

  map_public_ip_on_launch = true

  tags = {
    Name = "${var.project_name}-public-${count.index + 1}"
    Type = "public"
  }
}

Data Sources#

Data Sources pozwalają pobierać informacje o istniejących zasobach, które nie są zarządzane przez Terraform:

# Pobranie informacji o aktualnym koncie AWS
data "aws_caller_identity" "current" {}

# Pobranie dostępnych stref AZ
data "aws_availability_zones" "available" {
  state = "available"
}

# Pobranie najnowszego AMI
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

  filter {
    name   = "name"
    values = ["amzn2-ami-hvm-*-x86_64-gp2"]
  }
}

# Użycie data source w resource
resource "aws_instance" "app" {
  ami               = data.aws_ami.amazon_linux.id
  instance_type     = var.instance_type
  availability_zone = data.aws_availability_zones.available.names[0]
}

Zmienne i Outputs#

Zmienne wejściowe#

Zmienne pozwalają parametryzować konfigurację Terraform:

# variables.tf

variable "environment" {
  description = "Nazwa srodowiska (dev, staging, prod)"
  type        = string

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Srodowisko musi byc: dev, staging lub prod."
  }
}

variable "vpc_cidr" {
  description = "Blok CIDR dla VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "instance_count" {
  description = "Liczba instancji EC2"
  type        = number
  default     = 2
}

variable "allowed_cidrs" {
  description = "Lista CIDR dozwolonych w Security Group"
  type        = list(string)
  default     = ["0.0.0.0/0"]
}

variable "tags" {
  description = "Mapa tagow do przypisania zasobom"
  type        = map(string)
  default     = {}
}

variable "db_config" {
  description = "Konfiguracja bazy danych"
  type = object({
    engine         = string
    engine_version = string
    instance_class = string
    allocated_storage = number
  })
  default = {
    engine            = "postgres"
    engine_version    = "15.4"
    instance_class    = "db.t3.micro"
    allocated_storage = 20
  }
}

Zmienne wrażliwe#

Zmienne zawierające sekrety oznaczaj jako sensitive:

variable "db_password" {
  description = "Haslo do bazy danych"
  type        = string
  sensitive   = true
}

Outputs#

Outputs pozwalają eksportować wartości z konfiguracji:

# outputs.tf

output "vpc_id" {
  description = "ID VPC"
  value       = aws_vpc.main.id
}

output "public_subnet_ids" {
  description = "Lista ID publicznych subnetow"
  value       = aws_subnet.public[*].id
}

output "instance_public_ips" {
  description = "Publiczne adresy IP instancji"
  value       = aws_instance.app[*].public_ip
}

output "rds_endpoint" {
  description = "Endpoint bazy danych RDS"
  value       = aws_db_instance.main.endpoint
  sensitive   = true
}

Moduły Terraform#

Moduły pozwalają organizować i wielokrotnie wykorzystywać kod Terraform. Moduł to po prostu katalog z plikami .tf.

Struktura modułu#

modules/
├── vpc/
│   ├── main.tf
│   ├── variables.tf
│   ├── outputs.tf
│   └── README.md
├── ec2/
│   ├── main.tf
│   ├── variables.tf
│   └── outputs.tf
└── rds/
    ├── main.tf
    ├── variables.tf
    └── outputs.tf

Tworzenie modułu#

# modules/vpc/main.tf

resource "aws_vpc" "this" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(var.tags, {
    Name = "${var.name}-vpc"
  })
}

resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id

  tags = merge(var.tags, {
    Name = "${var.name}-igw"
  })
}

resource "aws_subnet" "public" {
  count = length(var.public_subnet_cidrs)

  vpc_id                  = aws_vpc.this.id
  cidr_block              = var.public_subnet_cidrs[count.index]
  availability_zone       = var.availability_zones[count.index]
  map_public_ip_on_launch = true

  tags = merge(var.tags, {
    Name = "${var.name}-public-${count.index + 1}"
  })
}

resource "aws_subnet" "private" {
  count = length(var.private_subnet_cidrs)

  vpc_id            = aws_vpc.this.id
  cidr_block        = var.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = merge(var.tags, {
    Name = "${var.name}-private-${count.index + 1}"
  })
}

Użycie modułu#

# main.tf

module "vpc" {
  source = "./modules/vpc"

  name                 = var.project_name
  vpc_cidr             = "10.0.0.0/16"
  public_subnet_cidrs  = ["10.0.1.0/24", "10.0.2.0/24"]
  private_subnet_cidrs = ["10.0.10.0/24", "10.0.11.0/24"]
  availability_zones   = ["eu-central-1a", "eu-central-1b"]
  tags                 = local.common_tags
}

module "ec2" {
  source = "./modules/ec2"

  name          = var.project_name
  instance_type = var.instance_type
  subnet_ids    = module.vpc.public_subnet_ids
  vpc_id        = module.vpc.vpc_id
  tags          = local.common_tags
}

Moduły z Terraform Registry#

Możesz także korzystać z gotowych modułów z rejestru:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.4.0"

  name = "my-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["eu-central-1a", "eu-central-1b", "eu-central-1c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"]

  enable_nat_gateway = true
  single_nat_gateway = true
}

Zarządzanie stanem (State Management)#

Terraform przechowuje stan infrastruktury w pliku terraform.tfstate. Ten plik jest krytyczny -- mapuje zasoby zdefiniowane w kodzie na realne zasoby w chmurze.

Remote State#

W środowisku zespołowym stan musi być przechowywany zdalnie:

# terraform.tf

terraform {
  backend "s3" {
    bucket         = "my-terraform-state-bucket"
    key            = "prod/infrastructure/terraform.tfstate"
    region         = "eu-central-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"
  }
}

State Locking#

DynamoDB zapewnia blokadę stanu, zapobiegając równoczesnym modyfikacjom:

# Tworzenie tabeli DynamoDB do lockowania stanu
resource "aws_dynamodb_table" "terraform_lock" {
  name         = "terraform-state-lock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"

  attribute {
    name = "LockID"
    type = "S"
  }

  tags = {
    Name = "Terraform State Lock"
  }
}

Azure Backend#

Dla Azure stan przechowujemy w Blob Storage:

terraform {
  backend "azurerm" {
    resource_group_name  = "tfstate-rg"
    storage_account_name = "tfstateaccount"
    container_name       = "tfstate"
    key                  = "prod.terraform.tfstate"
  }
}

Workflow: plan, apply, destroy#

terraform init#

Inicjalizacja projektu -- pobiera providery i moduły:

terraform init

terraform plan#

Plan pokazuje, jakie zmiany zostaną wprowadzone bez ich wykonywania:

terraform plan -out=tfplan

# Plan z plikiem zmiennych
terraform plan -var-file="prod.tfvars" -out=tfplan

terraform apply#

Zastosowanie zmian:

# Zastosowanie zapisanego planu
terraform apply tfplan

# Bezpośrednie zastosowanie z potwierdzeniem
terraform apply -var-file="prod.tfvars"

# Automatyczne potwierdzenie (CI/CD)
terraform apply -auto-approve -var-file="prod.tfvars"

terraform destroy#

Usunięcie wszystkich zasobów:

# Z potwierdzeniem
terraform destroy -var-file="prod.tfvars"

# Usunięcie konkretnego zasobu
terraform destroy -target=aws_instance.web_server

Przykład: Prowizjonowanie infrastruktury AWS#

Poniżej kompletny przykład tworzenia infrastruktury na AWS z VPC, EC2, RDS i S3:

# main.tf - Kompletna infrastruktura AWS

locals {
  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
  }
}

# ==================== VPC ====================

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-vpc"
  })
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-igw"
  })
}

resource "aws_subnet" "public" {
  count = 2

  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.${count.index + 1}.0/24"
  availability_zone       = data.aws_availability_zones.available.names[count.index]
  map_public_ip_on_launch = true

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-public-${count.index + 1}"
  })
}

resource "aws_subnet" "private" {
  count = 2

  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.${count.index + 10}.0/24"
  availability_zone = data.aws_availability_zones.available.names[count.index]

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-private-${count.index + 1}"
  })
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-public-rt"
  })
}

resource "aws_route_table_association" "public" {
  count          = 2
  subnet_id      = aws_subnet.public[count.index].id
  route_table_id = aws_route_table.public.id
}

# ==================== Security Groups ====================

resource "aws_security_group" "web" {
  name_prefix = "${var.project_name}-web-"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 443
    to_port     = 443
    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 = merge(local.common_tags, {
    Name = "${var.project_name}-web-sg"
  })

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_security_group" "rds" {
  name_prefix = "${var.project_name}-rds-"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port       = 5432
    to_port         = 5432
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]
  }

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-rds-sg"
  })
}

# ==================== EC2 ====================

resource "aws_instance" "web" {
  count = var.instance_count

  ami                    = data.aws_ami.amazon_linux.id
  instance_type          = var.instance_type
  subnet_id              = aws_subnet.public[count.index % 2].id
  vpc_security_group_ids = [aws_security_group.web.id]

  user_data = base64encode(templatefile("${path.module}/scripts/user_data.sh", {
    environment = var.environment
    db_endpoint = aws_db_instance.main.endpoint
  }))

  root_block_device {
    volume_type = "gp3"
    volume_size = 20
    encrypted   = true
  }

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-web-${count.index + 1}"
  })
}

# ==================== RDS ====================

resource "aws_db_subnet_group" "main" {
  name       = "${var.project_name}-db-subnet"
  subnet_ids = aws_subnet.private[*].id

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-db-subnet-group"
  })
}

resource "aws_db_instance" "main" {
  identifier     = "${var.project_name}-db"
  engine         = "postgres"
  engine_version = "15.4"
  instance_class = "db.t3.micro"

  allocated_storage     = 20
  max_allocated_storage = 100
  storage_encrypted     = true

  db_name  = var.db_name
  username = var.db_username
  password = var.db_password

  multi_az               = var.environment == "prod" ? true : false
  db_subnet_group_name   = aws_db_subnet_group.main.name
  vpc_security_group_ids = [aws_security_group.rds.id]

  backup_retention_period = 7
  skip_final_snapshot     = var.environment != "prod"

  tags = local.common_tags
}

# ==================== S3 ====================

resource "aws_s3_bucket" "assets" {
  bucket = "${var.project_name}-assets-${var.environment}"

  tags = merge(local.common_tags, {
    Name = "${var.project_name}-assets"
  })
}

resource "aws_s3_bucket_versioning" "assets" {
  bucket = aws_s3_bucket.assets.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "assets" {
  bucket = aws_s3_bucket.assets.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

resource "aws_s3_bucket_public_access_block" "assets" {
  bucket = aws_s3_bucket.assets.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

Workspaces#

Workspaces pozwalają zarządzać wieloma środowiskami z jednej konfiguracji:

# Tworzenie workspace'ów
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

# Przełączanie
terraform workspace select prod

# Lista workspace'ów
terraform workspace list

Użycie workspace w kodzie:

locals {
  environment = terraform.workspace

  instance_type = {
    dev     = "t3.micro"
    staging = "t3.small"
    prod    = "t3.medium"
  }
}

resource "aws_instance" "app" {
  instance_type = local.instance_type[local.environment]
  # ...
}

Integracja z CI/CD#

GitHub Actions#

# .github/workflows/terraform.yml
name: Terraform CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  TF_VERSION: "1.6.0"
  AWS_REGION: "eu-central-1"

jobs:
  terraform:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: ./infrastructure

    steps:
      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: ${{ env.TF_VERSION }}

      - name: Terraform Init
        run: terraform init

      - name: Terraform Format Check
        run: terraform fmt -check -recursive

      - name: Terraform Validate
        run: terraform validate

      - name: Terraform Plan
        if: github.event_name == 'pull_request'
        run: terraform plan -no-color -var-file="prod.tfvars"

      - name: Terraform Apply
        if: github.ref == 'refs/heads/main' && github.event_name == 'push'
        run: terraform apply -auto-approve -var-file="prod.tfvars"
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

GitLab CI#

# .gitlab-ci.yml
stages:
  - validate
  - plan
  - apply

variables:
  TF_ROOT: "./infrastructure"

validate:
  stage: validate
  script:
    - terraform init
    - terraform fmt -check
    - terraform validate

plan:
  stage: plan
  script:
    - terraform init
    - terraform plan -out=tfplan -var-file="prod.tfvars"
  artifacts:
    paths:
      - ${TF_ROOT}/tfplan

apply:
  stage: apply
  script:
    - terraform init
    - terraform apply tfplan
  when: manual
  only:
    - main

Najlepsze praktyki#

1. Wersjonowanie providerów#

Zawsze pinuj wersje providerów, aby uniknąć nieoczekiwanych zmian:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.31.0"  # Pozwala na patch updates
    }
  }
}

2. State locking#

Zawsze używaj state locking w środowisku zespołowym. DynamoDB (AWS) lub Blob Lease (Azure) zapobiegają równoczesnym modyfikacjom stanu.

3. Małe, skomponowane moduły#

Dziel infrastrukturę na małe, niezależne moduły. Jeden moduł na jeden logiczny komponent (VPC, baza danych, compute).

4. Używaj zmiennych i locals#

Nigdy nie hardkoduj wartości. Używaj zmiennych dla parametrów wejściowych i locals dla wartości obliczanych:

locals {
  name_prefix = "${var.project_name}-${var.environment}"

  common_tags = {
    Project     = var.project_name
    Environment = var.environment
    ManagedBy   = "terraform"
    Team        = var.team_name
  }
}

5. Formatowanie i walidacja#

Zawsze uruchamiaj formatowanie i walidację przed commitowaniem:

terraform fmt -recursive
terraform validate

6. Używaj .tfvars dla środowisk#

# environments/prod.tfvars
environment    = "prod"
instance_type  = "t3.medium"
instance_count = 3
db_instance_class = "db.r6g.large"

# environments/dev.tfvars
environment    = "dev"
instance_type  = "t3.micro"
instance_count = 1
db_instance_class = "db.t3.micro"

7. Import istniejących zasobów#

Terraform 1.5+ obsługuje import bloki:

import {
  to = aws_s3_bucket.existing
  id = "my-existing-bucket"
}

resource "aws_s3_bucket" "existing" {
  bucket = "my-existing-bucket"
}

Terraform Cloud#

Terraform Cloud to zarządzana platforma HashiCorp, która oferuje:

  • Remote state management -- bezpieczne przechowywanie stanu z szyfrowaniem
  • Remote execution -- plany i apply uruchamiane na serwerach HashiCorp
  • Private module registry -- prywatny rejestr modułów dla organizacji
  • Policy as Code -- Sentinel policies do wymuszania reguł bezpieczeństwa
  • Team management -- kontrola dostępu i uprawnienia
  • Cost estimation -- szacowanie kosztów przed wdrożeniem
  • Drift detection -- automatyczne wykrywanie zmian dokonanych poza Terraform
terraform {
  cloud {
    organization = "my-organization"

    workspaces {
      name = "my-app-prod"
    }
  }
}

Podsumowanie#

Terraform to narzędzie, które fundamentalnie zmienia sposób zarządzania infrastrukturą IT. Kluczowe aspekty to:

  • Infrastructure as Code -- cała infrastruktura jako wersjonowany kod
  • Multi-cloud -- jeden język do zarządzania wszystkimi platformami
  • Moduły -- wielokrotne wykorzystanie i standaryzacja konfiguracji
  • State management -- precyzyjne śledzenie stanu infrastruktury
  • CI/CD -- pełna automatyzacja wdrożeń infrastrukturalnych
  • Bezpieczeństwo -- szyfrowanie stanu, state locking, policy as code

Adopcja Terraform w organizacji to inwestycja, która szybko się zwraca poprzez zwiększoną powtarzalność, szybkość wdrożeń i redukcję błędów ludzkich.

Potrzebujesz pomocy z Terraform i infrastrukturą?#

W MDS Software Solutions Group specjalizujemy się w projektowaniu i wdrażaniu infrastruktury cloud z wykorzystaniem Terraform. Oferujemy:

  • Projektowanie architektury cloud (AWS, Azure, GCP)
  • Implementację Infrastructure as Code z Terraform
  • Migrację istniejącej infrastruktury do podejścia IaC
  • Konfigurację pipeline'ów CI/CD dla infrastruktury
  • Szkolenia z Terraform i praktyk DevOps
  • Audyt i optymalizację istniejącej konfiguracji Terraform

Skontaktuj się z nami, aby omówić Twój projekt!

Autor
MDS Software Solutions Group

Zespół ekspertów programistycznych specjalizujących się w nowoczesnych technologiach webowych.

Terraform - Infrastructure as Code w praktyce. Kompletny przewodnik | MDS Software Solutions Group | MDS Software Solutions Group