Przejdź do treści
DevOps

Terraform - Infrastructure as Code in der Praxis. Ein vollstaendiger Leitfaden

Veröffentlicht am:
·6 Min. Lesezeit·Autor: MDS Software Solutions Group

Terraform Infrastructure as

devops

Terraform - Infrastructure as Code in der Praxis

Die Verwaltung von IT-Infrastruktur hat sich im Laufe der Jahre weiterentwickelt -- von manueller Serverkonfiguration ueber Automatisierungsskripte bis hin zum vollstaendigen Infrastructure-as-Code-Ansatz. Terraform, entwickelt von HashiCorp, ist eines der beliebtesten IaC-Tools, mit dem Sie Infrastruktur deklarativ definieren, versionieren und automatisch bereitstellen koennen. In diesem Leitfaden behandeln wir alle wesentlichen Aspekte der Arbeit mit Terraform -- von den Grundlagen der HCL-Syntax bis hin zu fortgeschrittenen Produktionsmustern.

Was ist Infrastructure as Code und warum brauchen Sie es?#

Infrastructure as Code (IaC) ist die Praxis der Verwaltung von IT-Infrastruktur durch Konfigurationsdateien anstelle manueller Prozesse. Anstatt sich in die AWS- oder Azure-Konsole einzuloggen und Buttons zu klicken, definieren Sie Ihre gesamte Infrastruktur in Textdateien, die in Git versioniert, in Pull Requests ueberprueft und automatisch ueber CI/CD-Pipelines bereitgestellt werden koennen.

Probleme ohne IaC#

Ohne einen IaC-Ansatz stossen Teams auf eine Reihe von Problemen:

  • Snowflake-Server -- jeder manuell konfigurierte Server ist einzigartig, was die Reproduktion von Umgebungen erschwert
  • Fehlende Wiederholbarkeit -- es gibt keine Garantie, dass Staging identisch mit der Produktion ist
  • Konfigurationsdrift -- im Laufe der Zeit weichen Serverkonfigurationen vom beabsichtigten Zustand ab
  • Kein Audit-Trail -- es ist schwer nachzuvollziehen, wer wann was und warum geaendert hat
  • Langsame Bereitstellungen -- manuelle Konfiguration ist langsam und anfaellig fuer menschliche Fehler

Vorteile von IaC#

Die Einfuehrung eines IaC-Ansatzes loest diese Probleme:

  • Versionskontrolle -- die gesamte Infrastruktur in Git mit vollstaendiger Aenderungshistorie
  • Wiederholbarkeit -- identische Umgebungen mit einem einzigen Befehl erstellt
  • Automatisierung -- Bereitstellungen ohne menschliches Eingreifen
  • Dokumentation -- der Code selbst dient als Infrastrukturdokumentation
  • Code Review -- Infrastruktuaenderungen werden wie Anwendungscode ueberprueft
  • Schnelle Wiederherstellung -- Disaster Recovery reduziert sich auf das Ausfuehren eines Skripts

Terraform vs CloudFormation vs Pulumi#

Auf dem Markt gibt es mehrere IaC-Tools. Vergleichen wir die drei beliebtesten:

AWS CloudFormation#

CloudFormation ist das native IaC-Tool von AWS. Sein Hauptvorteil ist die tiefe Integration mit dem AWS-Oekosystem, aber es funktioniert ausschliesslich mit Amazon-Diensten. Konfigurationsdateien werden in JSON oder YAML geschrieben, was bei grossen Infrastrukturen unuebersichtlich wird.

Pulumi#

Pulumi ermoeglicht es, Infrastruktur mit gaengigen Programmiersprachen zu definieren -- TypeScript, Python, Go oder C#. Es ist eine grossartige Loesung fuer Teams, die Infrastruktur lieber in einer Sprache schreiben, die sie bereits kennen. Die Nachteile sind hoehere Komplexitaet und eine kleinere Community.

Terraform#

Terraform vereint die Vorteile beider Ansaetze. Es verwendet seine eigene Sprache HCL (HashiCorp Configuration Language), die deklarativ, lesbar und speziell fuer die Beschreibung von Infrastruktur konzipiert ist. Die wichtigsten Vorteile von Terraform sind:

  • Multi-Cloud -- eine Sprache fuer AWS, Azure, GCP und Hunderte weiterer Provider
  • Riesige Community -- Tausende von Modulen in der Terraform Registry
  • Ausgereiftes Oekosystem -- ein stabiles Tool mit langjaehriger Geschichte
  • Plan vor dem Deploy -- terraform plan zeigt genau, was sich aendern wird
  • State Management -- Verfolgung des aktuellen Zustands der Infrastruktur

HCL-Syntax -- Die Grundlagen#

HashiCorp Configuration Language (HCL) ist eine deklarative Sprache, die speziell fuer HashiCorp-Tools entwickelt wurde. Ihre Syntax ist intuitiv und leicht zu erlernen.

Dateistruktur#

Ein typisches Terraform-Projekt besteht aus mehreren Dateien:

# main.tf - Hauptressourcenkonfiguration
# variables.tf - Variablendefinitionen
# outputs.tf - Ausgabewerte
# providers.tf - Provider-Konfiguration
# terraform.tf - Backend- und erforderliche Provider-Konfiguration

Konfigurationsbloecke#

HCL basiert auf Konfigurationsbloecken. Jeder Block hat einen Typ, optionale Labels und einen Body:

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

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

# Datenblock
data "aws_ami" "ubuntu" {
  most_recent = true

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

  owners = ["099720109477"] # Canonical
}

# Variablenblock
variable "instance_type" {
  description = "EC2-Instanztyp"
  type        = string
  default     = "t3.micro"

  validation {
    condition     = contains(["t3.micro", "t3.small", "t3.medium"], var.instance_type)
    error_message = "Erlaubte Typen sind: t3.micro, t3.small, t3.medium."
  }
}

Provider -- AWS, Azure, GCP#

Provider sind Plugins, die es Terraform ermoeglichen, mit Cloud-Plattformen und anderen Diensten zu kommunizieren. Jeder Provider stellt Ressourcen (Resources) und Datenquellen (Data Sources) bereit.

Provider-Konfiguration#

# 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"
    }
  }
}

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

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

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

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

Provider-Aliase#

Wenn Sie mehrere Instanzen desselben Providers benoetigen (z.B. verschiedene AWS-Regionen):

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 und Data Sources#

Resources#

Resources sind die wichtigsten Bausteine in Terraform. Jede Resource repraesentiert ein Infrastrukturelement, das Terraform erstellt und verwaltet:

# VPC erstellen
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"
  }
}

# Subnets erstellen
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 ermoeglichen es, Informationen ueber bestehende Ressourcen abzurufen, die nicht von Terraform verwaltet werden:

# Aktuelle AWS-Kontoinformationen abrufen
data "aws_caller_identity" "current" {}

# Verfuegbare Availability Zones abrufen
data "aws_availability_zones" "available" {
  state = "available"
}

# Neuestes AMI abrufen
data "aws_ami" "amazon_linux" {
  most_recent = true
  owners      = ["amazon"]

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

# Data Source in einer Resource verwenden
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]
}

Variablen und Outputs#

Eingabevariablen#

Variablen ermoeglichen die Parametrisierung Ihrer Terraform-Konfiguration:

# variables.tf

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

  validation {
    condition     = contains(["dev", "staging", "prod"], var.environment)
    error_message = "Umgebung muss sein: dev, staging oder prod."
  }
}

variable "vpc_cidr" {
  description = "CIDR-Block fuer die VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "instance_count" {
  description = "Anzahl der EC2-Instanzen"
  type        = number
  default     = 2
}

variable "allowed_cidrs" {
  description = "Liste der erlaubten CIDRs in der Security Group"
  type        = list(string)
  default     = ["0.0.0.0/0"]
}

variable "tags" {
  description = "Map von Tags zur Zuweisung an Ressourcen"
  type        = map(string)
  default     = {}
}

variable "db_config" {
  description = "Datenbankkonfiguration"
  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
  }
}

Sensible Variablen#

Markieren Sie Variablen mit Geheimnissen als sensitive:

variable "db_password" {
  description = "Datenbankpasswort"
  type        = string
  sensitive   = true
}

Outputs#

Outputs ermoeglichen den Export von Werten aus Ihrer Konfiguration:

# outputs.tf

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

output "public_subnet_ids" {
  description = "Liste der oeffentlichen Subnet-IDs"
  value       = aws_subnet.public[*].id
}

output "instance_public_ips" {
  description = "Oeffentliche IP-Adressen der Instanzen"
  value       = aws_instance.app[*].public_ip
}

output "rds_endpoint" {
  description = "RDS-Datenbank-Endpunkt"
  value       = aws_db_instance.main.endpoint
  sensitive   = true
}

Terraform-Module#

Module ermoeglichen es, Terraform-Code zu organisieren und wiederzuverwenden. Ein Modul ist einfach ein Verzeichnis mit .tf-Dateien.

Modulstruktur#

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

Ein Modul erstellen#

# 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}"
  })
}

Ein Modul verwenden#

# 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
}

Module aus der Terraform Registry#

Sie koennen auch fertige Module aus der Registry verwenden:

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
}

State Management#

Terraform speichert den Zustand Ihrer Infrastruktur in einer terraform.tfstate-Datei. Diese Datei ist kritisch -- sie bildet die im Code definierten Ressourcen auf die realen Ressourcen in der Cloud ab.

Remote State#

In einer Teamumgebung muss der State remote gespeichert werden:

# 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 bietet State Locking und verhindert gleichzeitige Aenderungen:

# DynamoDB-Tabelle fuer State Locking erstellen
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#

Fuer Azure wird der State in Blob Storage gespeichert:

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#

Initialisiert das Projekt -- laedt Provider und Module herunter:

terraform init

terraform plan#

Plan zeigt, welche Aenderungen vorgenommen werden, ohne sie auszufuehren:

terraform plan -out=tfplan

# Plan mit einer Variablendatei
terraform plan -var-file="prod.tfvars" -out=tfplan

terraform apply#

Wendet Aenderungen an:

# Gespeicherten Plan anwenden
terraform apply tfplan

# Direktes Apply mit Bestaetigung
terraform apply -var-file="prod.tfvars"

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

terraform destroy#

Loescht alle Ressourcen:

# Mit Bestaetigung
terraform destroy -var-file="prod.tfvars"

# Bestimmte Ressource loeschen
terraform destroy -target=aws_instance.web_server

Beispiel: AWS-Infrastruktur bereitstellen#

Nachfolgend ein vollstaendiges Beispiel fuer die Erstellung einer AWS-Infrastruktur mit VPC, EC2, RDS und S3:

# main.tf - Vollstaendige AWS-Infrastruktur

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 ermoeglichen die Verwaltung mehrerer Umgebungen aus einer einzigen Konfiguration:

# Workspaces erstellen
terraform workspace new dev
terraform workspace new staging
terraform workspace new prod

# Workspace wechseln
terraform workspace select prod

# Workspaces auflisten
terraform workspace list

Verwendung von Workspaces im Code:

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]
  # ...
}

CI/CD-Integration#

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

Best Practices#

1. Provider-Versionen festlegen#

Pinnen Sie immer Provider-Versionen, um unerwartete Aenderungen zu vermeiden:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.31.0"  # Erlaubt nur Patch-Updates
    }
  }
}

2. State Locking#

Verwenden Sie in Teamumgebungen immer State Locking. DynamoDB (AWS) oder Blob Lease (Azure) verhindert gleichzeitige State-Aenderungen.

3. Kleine, komponierbare Module#

Teilen Sie Ihre Infrastruktur in kleine, unabhaengige Module auf. Ein Modul pro logischer Komponente (VPC, Datenbank, Compute).

4. Variablen und Locals verwenden#

Hardcodieren Sie niemals Werte. Verwenden Sie Variablen fuer Eingabeparameter und locals fuer berechnete Werte:

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

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

5. Formatierung und Validierung#

Fuehren Sie vor dem Commit immer Formatierung und Validierung aus:

terraform fmt -recursive
terraform validate

6. .tfvars fuer Umgebungen verwenden#

# 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. Bestehende Ressourcen importieren#

Terraform 1.5+ unterstuetzt Import-Bloecke:

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

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

Terraform Cloud#

Terraform Cloud ist die verwaltete Plattform von HashiCorp, die Folgendes bietet:

  • Remote State Management -- sichere State-Speicherung mit Verschluesselung
  • Remote Execution -- Plans und Applies werden auf HashiCorp-Servern ausgefuehrt
  • Private Module Registry -- eine private Modul-Registry fuer Ihre Organisation
  • Policy as Code -- Sentinel Policies zur Durchsetzung von Sicherheitsregeln
  • Team Management -- Zugriffskontrolle und Berechtigungen
  • Kostenschaetzung -- Kostenschaetzungen vor der Bereitstellung
  • Drift Detection -- automatische Erkennung von Aenderungen ausserhalb von Terraform
terraform {
  cloud {
    organization = "my-organization"

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

Zusammenfassung#

Terraform ist ein Tool, das die Verwaltung von IT-Infrastruktur grundlegend veraendert. Die wichtigsten Erkenntnisse sind:

  • Infrastructure as Code -- die gesamte Infrastruktur als versionierter Code
  • Multi-Cloud -- eine Sprache zur Verwaltung aller Plattformen
  • Module -- Wiederverwendung und Standardisierung von Konfigurationen
  • State Management -- praezise Verfolgung des Infrastrukturzustands
  • CI/CD -- vollstaendige Automatisierung von Infrastrukturbereitstellungen
  • Sicherheit -- State-Verschluesselung, State Locking, Policy as Code

Die Einfuehrung von Terraform in Ihrer Organisation ist eine Investition, die sich schnell durch erhoehte Wiederholbarkeit, schnellere Bereitstellungen und die Reduzierung menschlicher Fehler auszahlt.

Brauchen Sie Hilfe mit Terraform und Infrastruktur?#

Bei MDS Software Solutions Group sind wir auf das Design und die Bereitstellung von Cloud-Infrastruktur mit Terraform spezialisiert. Wir bieten:

  • Cloud-Architekturdesign (AWS, Azure, GCP)
  • Infrastructure-as-Code-Implementierung mit Terraform
  • Migration bestehender Infrastruktur zum IaC-Ansatz
  • CI/CD-Pipeline-Konfiguration fuer Infrastruktur
  • Terraform- und DevOps-Schulungen
  • Audit und Optimierung bestehender Terraform-Konfigurationen

Kontaktieren Sie uns, um Ihr Projekt zu besprechen!

Autor
MDS Software Solutions Group

Team von Programmierexperten, die sich auf moderne Webtechnologien spezialisiert haben.

Terraform - Infrastructure as Code in der Praxis. Ein vollstaendiger Leitfaden | MDS Software Solutions Group | MDS Software Solutions Group