Terraform state beheren met Gitlab

Terraform state beheren met Gitlab

Dit artikel is een stap-voor-stap handleiding voor het gebruik van Terraform en Gitlab CI/CD. Een correct beheer van je Terraform state is cruciaal voor de integriteit van je infrastructuur setup. Op het einde van dit artikel zal je in staat zijn om je Terraform state bij te houden in je Gitlab repo.

Ik verwijs in dit artikel naar Terraform maar in de commando’s zal je zien dat ik de fork OpenTofu gebruik.

Wat is Gitlab CI/CD?

GitLab CI/CD is het onderdeel van GitLab dat gebruikt kan worden voor alle continue methoden van software ontwikkeling (Continuous Integration, Delivery en Deployment). Met GitLab CI/CD kunnen we code testen, bouwen en uitrollen zonder dat we hiervoor nog andere software nodig hebben.

Meer info over Gitlab CI/CD kan je hier terugvinden.

Wat is een Terraform state file?

Terraform slaat informatie over de infrastructuur die het beheert op in een statusbestand, genaamd terraform.tfstate. Dit Terraform state bestand houdt de huidige status van de infrastructuur bij, inclusief de resources die Terraform heeft ingericht en hun huidige configuraties. Het statusbestand is essentieel voor Terraform om wijzigingen in de infrastructuur te plannen en toe te passen.

Wanneer je met meerdere personen tegelijk werkt op dezelfde infrastructuur kan het handig zijn om de state file bij te houden in je repository, zo maakt iedereen te allen tijde gebruik van dezelfde configuratie. Daarnaast kan je historische changes tracken op de state file en bepalen wie welke toegang heeft tot deze file. Om deze redenen zullen we Gitlab gebruiken aangezien Gitlab een ingebakken functionaliteit heeft om dit type bestand op te slaan.

Voorwaarden om van start te gaan

  • Een Gitlab account op gitlab.com of op een eigen Gitlab server
  • Een AWS account, aangezien we hierop onze infrastructuur in dit voorbeeld uitrollen
  • Basiskennis van AWS, Gitlab en Terraform
  • AWS Access Key en Secret Key, deze kan je aanmaken via de IAM console > Users > jouw username > Security credentials > Create access key

Stap voor stap

1. Gitlab repository aanmaken

2. Repository clonen en provider.tf aanmaken

Clone de nieuwe repository nu op je lokale machine en maak in deze map de provider.tf file aan.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.60.0"
    }
  }
}

3. Genereer een Personal Access Token in Gitlab

Klik hiervoor op de avatar van je profiel en navigeer naar Access Tokens. Klik op Add new token maar let erop dat je zeker de API scope aanduidt bij het genereren van je token.

4. Maak backend.tf aan

Om de Terraform state te beheren via Gitlab moeten we een HTTP backend definiëren in een backend.tf file.

terraform {
  backend "http" {
    address        = "https://{GITLAB_INSTANCE}/api/v4/projects/{PROJECT_ID}/terraform/state/tfstate"
    lock_address   = "https://{GITLAB_INSTANCE}/api/v4/projects/{PROJECT_ID}/terraform/state/tfstate/lock"
    unlock_address = "https://{GITLAB_INSTANCE}/api/v4/projects/{PROJECT_ID}/terraform/state/tfstate/lock"
    username       = "dimitri"
    password       = "{GITLAB_PAT}"
    lock_method    = "POST"
    unlock_method  = "DELETE"
    retry_wait_min = 5
  }
}

Vervang in dit voorbeeld {GITLAB_INSTANCE} door jouw eigen Gitlab instance url, {PROJECT_ID} door jouw project ID en {GITLAB_PAT} door de token die je in stap 3 aangemaakt hebt.

5. Definieer variables.tf

locals {
  ami  = "ami-0346fd83e3383dcb4"
  type = "t2.micro"
}

variable "name_tag" {
  type        = string
  description = "Name of the EC2 instance"
  default     = "Test instance"
}

output "public_ip" {
  value       = aws_instance.demo_vm.public_ip
  description = "Public IP Address of EC2 instance"
}

output "instance_id" {
  value       = aws_instance.demo_vm.id
  description = "Instance ID"
}

Check zeker of je de juiste ami gebruikt, in dit voorbeeld wordt de infrastucture aangemaakt in eu-central-1.

6. Maak je main.tf aan

Nu definiëren we onze main configuratie, pas gerust waardes aan waar nodig, maar hou dan ook rekening met je variables.tf file.

provider "aws" {
  region = "eu-central-1"
}

# Create our VPC
resource "aws_vpc" "default" {
  cidr_block = "10.0.0.0/16"
}

# Create an internet gateway to give our subnet access to the outside world
resource "aws_internet_gateway" "default" {
  vpc_id = aws_vpc.default.id
}

# Grant the VPC internet access on its main route table
resource "aws_route" "internet_access" {
  route_table_id         = aws_vpc.default.main_route_table_id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.default.id
}

# Create a subnet to launch our instances into
resource "aws_subnet" "default" {
  vpc_id                  = aws_vpc.default.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
}

# Our default security group to access the instances
resource "aws_security_group" "default" {
  name        = "Project - OpenTofu"
  description = "Project - OpenTofu"
  vpc_id      = aws_vpc.default.id

  # SSH access from anywhere
  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTP access from the VPC
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/16"]
  }

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

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

resource "aws_instance" "demo_vm" {
  ami           = local.ami
  instance_type = local.type
  tags = {
    Name = var.name_tag
  }
  root_block_device {
    volume_size = "40"
    volume_type = "standard"
  }
  ebs_block_device {
    device_name           = "/dev/sdb"
    volume_size           = "80"
    volume_type           = "standard"
    delete_on_termination = false
  }
  vpc_security_group_ids = ["${aws_security_group.default.id}"]
  subnet_id              = aws_subnet.default.id
}

resource "aws_s3_bucket" "state_bucket" {
  bucket = "terraform-states-steyaert"
  tags = {
    Name = "State Bucket"
  }
}

Bovenstaande code zal de vpc, subnet, security group, internet gateway, S3 & instance resources aanmaken.

7. Tijd voor actie

Nu we alle code klaar hebben staan is het tijd geworden om de config uit te rollen, vergeet zeker niet om je code te pushen naar je Gitlab repository. We beginnen door, in de map waarin deze files zitten, het commando tofu init (of terraform init) uit te voeren. Hierbij wordt onze Gitlab backend direct geïnitieerd.

Zodra dit klaar is voeren we tofu plan (of terraform plan) uit om de rollout voor te bereiden.

Als hier geen fouten optreden dan kunnen we onze infrastructuur uitrollen via tofu apply (of terraform apply).

8. Terraform state in Gitlab

We gaan nu controleren of onze state file correct opgeslaan werd in Gitlab. Navigeer hiervoor in je repository naar Operate > Terraform states. Hier zie je de recentste versie van je state file staan.

9. Infrastructuur check

Uiteraard willen we controleren of onze infrastructuur wel correct uitgerold is.

10. Opkuis

We kunnen nu optioneel onze uitgerolde infrastructuur verwijderen door het commando tofu destroy (of terraform destroy) uit te voeren.

Infrastructuur beheren met Terraform en Gitlab

We hebben nog maar het topje van de ijsberg verkend in dit artikel, maar de mogelijkheden gaan nog zoveel verder. Heb je zelf infrastructuur die je op een geautomatiseerde manier wil uitrollen of ben je op zoek naar een technisch onderlegde partner voor een samenwerking? Stuur mij dan gerust een berichtje, we kunnen dan een koffie gaan drinken.

Jouw feedback

Chatten
1
💬 Meer info?
Hallo 👋🏼
Kan ik een vraag voor jou beantwoorden?