Terug naar blog
infrastructuredevopsterraformpulumiiaccloud

Infrastructure as Code voor je SaaS: van handmatig beheer naar geautomatiseerde infrastructuur

Door SaaS Masters31 maart 202612 min leestijd

Steeds meer SaaS-teams ontdekken dat handmatig servers configureren een bottleneck wordt zodra je groeit. Infrastructure as Code (IaC) lost dit op door je hele infrastructuur — servers, databases, netwerken, DNS — te beschrijven als code die je kunt reviewen, versiebeheren en automatisch uitrollen.

In dit artikel duiken we diep in IaC voor SaaS-producten: waarom je het nodig hebt, welke tools er zijn, en hoe je stap voor stap je eerste IaC-pipeline opzet.

Waarom Infrastructure as Code voor je SaaS?

Het probleem met handmatig beheer

Stel je voor: je SaaS draait op drie servers. Je DevOps-engineer heeft ze maanden geleden ingericht via de AWS-console. Nu moet je een vierde server toevoegen voor een grote klant. Maar niemand weet precies welke instellingen er zijn gebruikt. Klinkt bekend?

Dit is het snowflake server-probleem: elke server is uniek, handmatig geconfigureerd, en onmogelijk exact te reproduceren. Dit leidt tot:

  • Configuration drift: servers wijken langzaam af van elkaar
  • Geen audit trail: wie heeft wat wanneer gewijzigd?
  • Trage disaster recovery: als een server crasht, duurt het uren of dagen om alles opnieuw in te richten
  • Onboarding-problemen: nieuwe teamleden snappen de infrastructuur niet

De voordelen van IaC

Met IaC beschrijf je je infrastructuur declaratief:

# Terraform voorbeeld: een PostgreSQL database op AWS RDS
resource "aws_db_instance" "main" {
  identifier     = "saas-production-db"
  engine         = "postgres"
  engine_version = "16.2"
  instance_class = "db.r6g.large"
  
  allocated_storage     = 100
  max_allocated_storage = 500
  storage_encrypted     = true
  
  db_name  = "saas_production"
  username = "app_user"
  password = var.db_password
  
  multi_az               = true
  backup_retention_period = 14
  
  vpc_security_group_ids = [aws_security_group.database.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name
  
  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

Dit geeft je:

  1. Reproduceerbaarheid: exact dezelfde infrastructuur in dev, staging en productie
  2. Versiebeheer: elke wijziging gaat door een pull request
  3. Snelle disaster recovery: terraform apply en je bent weer online
  4. Documentatie: de code ís je documentatie
  5. Compliance: auditors kunnen precies zien wat er draait en wanneer het is gewijzigd

De grote drie: Terraform, Pulumi en AWS CDK

Terraform (HashiCorp)

Terraform is de industriestandaard. Het gebruikt HCL (HashiCorp Configuration Language), een declaratieve taal die specifiek ontworpen is voor infrastructuur.

Voordelen:

  • Enorm ecosysteem met providers voor AWS, GCP, Azure, Cloudflare, Vercel, en honderden andere services
  • Mature en stabiel, enorme community
  • terraform plan laat je precies zien wat er gaat veranderen vóórdat je iets uitvoert

Nadelen:

  • HCL heeft beperkingen bij complexe logica (loops, conditionals)
  • State management vereist zorgvuldig beheer
  • Geen echte programmeertaal — soms frustrerend
# Meerdere omgevingen met Terraform workspaces
variable "environment" {
  type = string
}

locals {
  instance_sizes = {
    development = "db.t3.micro"
    staging     = "db.t3.small"
    production  = "db.r6g.large"
  }
}

resource "aws_db_instance" "main" {
  instance_class = local.instance_sizes[var.environment]
  # ... rest van de configuratie
}

Pulumi

Pulumi laat je infrastructuur beschrijven in echte programmeertalen: TypeScript, Python, Go of C#. Voor SaaS-teams die al veel TypeScript schrijven, voelt dit natuurlijk aan.

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();
const environment = pulumi.getStack(); // dev, staging, prod

const instanceSizes: Record<string, string> = {
  dev: "db.t3.micro",
  staging: "db.t3.small",
  prod: "db.r6g.large",
};

const database = new aws.rds.Instance("main-db", {
  identifier: \`saas-\${environment}-db\`,
  engine: "postgres",
  engineVersion: "16.2",
  instanceClass: instanceSizes[environment],
  allocatedStorage: environment === "prod" ? 100 : 20,
  storageEncrypted: true,
  multiAz: environment === "prod",
  backupRetentionPeriod: environment === "prod" ? 14 : 1,
});

export const dbEndpoint = database.endpoint;

Voordelen:

  • Echte programmeertaal met volledige IDE-support
  • Makkelijke abstracties en hergebruik via functies en klassen
  • Dezelfde taal als je applicatie (TypeScript)

Nadelen:

  • Kleiner ecosysteem dan Terraform
  • Meer keuzedruk (welke taal? welk pattern?)

AWS CDK

Als je exclusief op AWS zit, is CDK (Cloud Development Kit) een sterke keuze. Het genereert CloudFormation templates vanuit TypeScript of Python.

Voordelen:

  • Diepe AWS-integratie, L2/L3 constructs met sensible defaults
  • Goed voor pure AWS-shops

Nadelen:

  • Alleen AWS (geen multi-cloud)
  • CloudFormation-beperkingen onder de motorkap

Welke kies je?

SituatieAanbeveling
Multi-cloud of meerdere SaaS-toolsTerraform
TypeScript-team, complexe logica nodigPulumi
100% AWS, team kent CloudFormationAWS CDK
Klein team, snel beginnenTerraform

Je eerste IaC-project opzetten

Stap 1: State storage configureren

IaC-tools houden een state file bij: een overzicht van wat er in de echte wereld bestaat en hoe dat mapt op je code. Deze state moet veilig en gedeeld opgeslagen worden.

# backend.tf — remote state in S3 met DynamoDB locking
terraform {
  backend "s3" {
    bucket         = "mycompany-terraform-state"
    key            = "saas-platform/terraform.tfstate"
    region         = "eu-west-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
  }
}

Tip: Maak de state bucket en DynamoDB table handmatig of met een apart bootstrap-script aan. Dit is het enige stukje infrastructuur dat je niet met Terraform zelf beheert.

Stap 2: Projectstructuur

Een bewezen structuur voor SaaS-projecten:

infrastructure/
├── modules/
│   ├── networking/        # VPC, subnets, security groups
│   ├── database/          # RDS, Redis
│   ├── compute/           # ECS, Lambda, EC2
│   ├── cdn/               # CloudFront, S3 buckets
│   └── monitoring/        # CloudWatch, alerting
├── environments/
│   ├── dev/
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── terraform.tfvars
│   ├── staging/
│   └── production/
├── global/                # IAM, Route53, shared resources
└── scripts/
    └── bootstrap.sh       # Eenmalige setup

Stap 3: Modules bouwen

Modules zijn herbruikbare bouwblokken. Hier een voorbeeld van een database-module:

# modules/database/main.tf
variable "environment" { type = string }
variable "vpc_id" { type = string }
variable "subnet_ids" { type = list(string) }
variable "instance_class" { type = string }

resource "aws_db_subnet_group" "main" {
  name       = "${var.environment}-db-subnet"
  subnet_ids = var.subnet_ids
}

resource "aws_security_group" "database" {
  name_prefix = "${var.environment}-db-"
  vpc_id      = var.vpc_id

  ingress {
    from_port   = 5432
    to_port     = 5432
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"]
  }
}

resource "aws_db_instance" "main" {
  identifier     = "${var.environment}-saas-db"
  engine         = "postgres"
  instance_class = var.instance_class
  # ... configuratie
}

output "endpoint" {
  value = aws_db_instance.main.endpoint
}

Stap 4: CI/CD-integratie

Integreer IaC in je deployment pipeline zodat infrastructuurwijzigingen dezelfde review- en testcyclus doorlopen als applicatiecode:

# .github/workflows/infrastructure.yml
name: Infrastructure

on:
  pull_request:
    paths: ['infrastructure/**']
  push:
    branches: [main]
    paths: ['infrastructure/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      
      - name: Terraform Init
        run: terraform init
        working-directory: infrastructure/environments/production
      
      - name: Terraform Plan
        run: terraform plan -no-color -out=tfplan
        working-directory: infrastructure/environments/production
      
      - name: Comment PR with Plan
        uses: actions/github-script@v7
        with:
          script: |
            // Post plan output als PR comment

  apply:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      
      - name: Terraform Apply
        run: terraform apply -auto-approve
        working-directory: infrastructure/environments/production

Best practices voor SaaS-teams

1. Gebruik drift detection

Soms wijzigt iemand handmatig iets in de console. Detecteer dit automatisch:

# Draai dagelijks via cron
terraform plan -detailed-exitcode
# Exit code 2 = er zijn wijzigingen gedetecteerd
if [ $? -eq 2 ]; then
  # Stuur alert naar Slack
  curl -X POST "$SLACK_WEBHOOK" \
    -d '{"text":"⚠️ Infrastructure drift gedetecteerd! Check Terraform plan."}'
fi

2. Tag alles

Tags zijn essentieel voor kostenbeheer en organisatie:

locals {
  common_tags = {
    Project     = "saas-platform"
    Environment = var.environment
    ManagedBy   = "terraform"
    Team        = "platform"
    CostCenter  = "engineering"
  }
}

3. Gebruik prevent_destroy voor kritieke resources

resource "aws_db_instance" "main" {
  # ...
  lifecycle {
    prevent_destroy = true
  }
}

4. Secrets nooit in je code

Gebruik een secrets manager en refereer ernaar:

data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "saas/production/db-password"
}

resource "aws_db_instance" "main" {
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
}

5. Implementeer policy-as-code

Tools als Open Policy Agent (OPA) of Sentinel laten je afdwingen dat infrastructuur aan je standaarden voldoet:

# policy/require-encryption.rego
package terraform

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_db_instance"
  not resource.change.after.storage_encrypted
  msg := "Database instances moeten versleuteld zijn"
}

Van nul naar productie: een realistisch stappenplan

Je hoeft niet alles in één keer te doen. Dit is een pragmatisch pad:

Week 1-2: Begin met je database en netwerk in Terraform. Laat de rest nog handmatig.

Week 3-4: Voeg compute resources toe (ECS tasks, Lambda's). Zet remote state op.

Maand 2: Integreer in CI/CD. Voeg monitoring-resources toe. Begin met drift detection.

Maand 3+: Voeg policy-as-code toe. Documenteer modules. Train het team.

De sleutel is incrementeel adopteren. Probeer niet je hele infrastructuur in één keer te migreren — dat is een recept voor frustratie.

Conclusie

Infrastructure as Code is geen luxe meer voor SaaS-teams — het is een noodzaak zodra je voorbij de MVP-fase bent. Het verschil tussen teams die weken besteden aan infrastructuurproblemen en teams die in minuten nieuwe omgevingen opspinnen, is bijna altijd IaC.

Begin klein: pak je database of je netwerkconfiguratie. Beschrijf het in Terraform of Pulumi. Commit het naar Git. En bouw van daaruit verder.

De investering betaalt zich terug bij je eerste incident, je eerste nieuwe teamlid, of je eerste grote klant die een aparte omgeving nodig heeft. En dan ben je blij dat je het hebt gedaan.