IaC
Een introductie tot dynamic blocks in Terraform
Een introductie tot dynamic blocks in Terraform
Met Terraform, een populaire Infrastructure as Code (IaC)-tool, definieer en beheer je je infrastructuur declaratief. Een van de krachtige functies van Terraform is het dynamic block. Daarmee maak je je configuratie modulairder, herbruikbaarder en beter te onderhouden. In dit artikel bekijken we wat dynamic blocks zijn, hoe ze werken en laten we met een praktisch voorbeeld zien hoe je ze gebruikt.
Wat zijn dynamic blocks?
Met dynamic blocks in Terraform genereer je programmatisch geneste blocks binnen een resource of module, op basis van een verzameling invoerdata. Dat is vooral handig als je een wisselend aantal vergelijkbare geneste blocks nodig hebt op basis van dynamische invoer, bijvoorbeeld meerdere security group rules, tags of andere configuratie-onderdelen.
Onderdelen van een dynamic block
Een dynamic block in Terraform bestaat uit drie onderdelen:
- dynamic keyword: dit keyword wordt gevolgd door de naam van het block dat je dynamisch wilt genereren.
- for_each argument: dit argument bepaalt de verzameling (list, map of set) waarover het dynamic block itereert.
- content block: dit geneste block bevat de eigenlijke configuratie die voor elk item in de for_each-verzameling wordt herhaald.
dynamic keyword
Met het dynamic keyword declareer je een dynamic block. Erachter zet je de naam van het block dat je dynamisch wilt genereren. Die naam moet overeenkomen met het type genest block dat je binnen de resource of module aanmaakt.
Voorbeeld:
dynamic "ingress" {
# Configuration here
}
In dit voorbeeld is het ingress-block wat we dynamisch willen aanmaken binnen een resource, zoals een security group.
for_each argument
Het for_each-argument bepaalt de verzameling waarover het dynamic block itereert. Dit kan een list, map of set zijn. Voor elk item in de verzameling wordt een bijbehorend genest block gegenereerd.
Voorbeeld:
dynamic "ingress" {
for_each = var.ingress_rules
# Content here
}
content block
Het content-block bevat de eigenlijke configuratie die voor elk item in de for_each-verzameling wordt herhaald. Binnen het content-block verwijs je naar het huidige item uit de for_each-verzameling via block_name.value.
Voorbeeld:
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
In dit voorbeeld wordt voor elk item in var.ingress_rules een nieuw ingress-block aangemaakt, met de attributen description, from_port, to_port, protocol en cidr_blocks ingevuld op basis van de waarden van het huidige item (ingress.value).
- Het dynamic “ingress”-block itereert over de lijst var.ingress_rules.
- Voor elk item in de lijst genereert het een ingress-block met de opgegeven attributen.
Praktisch voorbeeld: AWS Security Group rules
Laten we met een praktisch voorbeeld kijken hoe dynamic blocks werken. Stel dat je meerdere ingress rules voor een AWS Security Group moet aanmaken op basis van een lijst met poorten. Elke ingress rule met de hand definiëren is omslachtig en foutgevoelig. Met dynamic blocks automatiseer je dit.
Statische aanpak
Zonder dynamic blocks definieer je elke ingress rule handmatig:
resource "aws_security_group" "webserver" {
name = "sg for webserver"
description = "Allow inbound traffic"
vpc_id = aws_vpc.testvpc.id
ingress {
description = "Allow inbound ssh traffic"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow inbound http traffic"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "Allow inbound https traffic"
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 = {
Name = "webserver_sg"
}
}
Deze aanpak is eenvoudig, maar schaalt slecht zodra je veel ingress rules beheert of de regels vaak wijzigen.
Dynamische aanpak
Met dynamic blocks definieer je de ingress rules dynamisch op basis van invoerdata. Definieer eerst een variabele voor de ingress rules:
variable "ingress_rules" {
type = list(object({
description = string
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
}
Vervolgens maken we een bestand terraform.tfvars aan met de waarden voor de variabele ingress_rules:
ingress_rules = [
{
description = "Allow inbound ssh traffic"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
description = "Allow inbound http traffic"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
},
{
description = "Allow inbound https traffic"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
]
Gebruik daarna het dynamic block om de ingress rules te genereren op basis van de invoerdata:
resource "aws_security_group" "webserver" {
name = "sg for webserver"
description = "Allow inbound traffic"
vpc_id = aws_vpc.testvpc.id
dynamic "ingress" {
for_each = var.ingress_rules
content {
description = ingress.value.description
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "allow_ssh_http_https"
}
}
In dit voorbeeld:
var.ingress_rules is de verzameling waarover het dynamic block itereert. Elk item in var.ingress_rules wordt gebruikt om een apart ingress-block te genereren.
- Het dynamic “ingress”-block itereert over de lijst var.ingress_rules.
- Voor elk item in de lijst genereert het een ingress-block met de opgegeven attributen.
Geneste dynamic blocks in Terraform
Met geneste dynamic blocks in Terraform genereer je dynamisch geneste configuraties binnen een ander dynamic block. Dat is vooral handig bij resources met meerdere lagen geneste configuratie, zoals AWS Load Balancers met listeners en bijbehorende actions. Kernbegrippen van geneste dynamic blocks
- Buitenste dynamic block: dit block itereert over een verzameling en maakt een reeks geneste blocks aan.
- Binnenste dynamic block: dit block zit genest in het buitenste dynamic block en itereert over een andere verzameling. Zo ontstaan extra geneste blocks binnen elk item dat het buitenste block genereert.
Laten we de geneste dynamic blocks in het onderstaande AWS Load Balancer-voorbeeld stap voor stap bekijken.
Voorbeeld:
AWS Load Balancer met geneste dynamic blocks
Bekijk de volgende Terraform-configuratie, die een AWS Application Load Balancer aanmaakt met meerdere listeners en hun default actions, allemaal dynamisch.
resource "aws_lb" "example" {
name = "example-lb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.lb_sg.id]
subnets = [aws_subnet.az1.id]
# outer Dynamic block:
dynamic "listener" {
for_each = var.listeners
content {
port = listener.value.port
protocol = listener.value.protocol
# inner Dynamic block:
dynamic "default_action" {
for_each = listener.value.default_actions
content {
type = default_action.value.type
target_group_arn = default_action.value.target_group_arn
}
}
}
}
}
Uitleg per onderdeel
1. Resource-definitie
De aws_lb-resource maakt een AWS Application Load Balancer aan.
resource "aws_lb" "example" {
name = "example-lb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.lb_sg.id]
subnets = [aws_subnet.az1.id]
- name: de naam van de load balancer.
- internal: bepaalt of de load balancer intern is of aan het internet hangt.
- load_balancer_type: zet het type load balancer op application.
- security_groups: koppelt security groups aan de load balancer.
- subnets: de subnets waarin de load balancer wordt uitgerold.
2. Buitenste dynamic block (listener)
Het buitenste dynamic block maakt meerdere listener-blocks aan op basis van de variabele var.listeners.
dynamic "listener" {
for_each = var.listeners
content {
port = listener.value.port
protocol = listener.value.protocol
- for_each = var.listeners: itereert over de variabele listeners, een lijst met objecten die de listener-configuraties beschrijven.
- content { … }: definieert de attributen van elke listener en vult port en protocol dynamisch in op basis van het huidige item in var.listeners.
3. Binnenste dynamic block (default_action)
Binnen elk dynamisch gegenereerd listener-block maakt het binnenste dynamic block meerdere default_action-blocks aan.
dynamic "default_action" {
for_each = listener.value.default_actions
content {
type = default_action.value.type
target_group_arn = default_action.value.target_group_arn
}
}
- for_each = listener.value.default_actions: itereert over de default_actions van elke listener, een lijst met objecten die de default actions beschrijven.
- content { … }: definieert de attributen van elke default action en vult type en target_group_arn dynamisch in op basis van het huidige item in default_actions.
Voorbeeld van invoervariabelen
Om te laten zien hoe de dynamic blocks werken, definiëren we voorbeeldvariabelen voor listeners en default_actions.
Variabele-definities
variable "listeners" {
type = list(object({
port = number
protocol = string
default_actions = list(object({
type = string
target_group_arn = string
}))
}))
}
Terraform.tfvars met de waarden voor de variabele listeners:
listeners = [
{
port = 80
protocol = "HTTP"
default_actions = [
{
type = "forward"
target_group_arn = "arn:aws:elasticloadbalancing:eu-central-1:123123123123:targetgroup/target-group-1"
}
]
},
{
port = 443
protocol = "HTTPS"
default_actions = [
{
type = "forward"
target_group_arn = "arn:aws:elasticloadbalancing:eu-central-1:123123123123:targetgroup/target-group-2"
}
]
}
]
Resulterende Terraform-configuratie
Met de geneste dynamic blocks genereert Terraform op basis van de opgegeven invoervariabelen de volgende configuratie:
resource "aws_lb" "example" {
name = "example-lb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.lb_sg.id]
subnets = [aws_subnet.az1.id]
listener {
port = 80
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = "arn:aws:elasticloadbalancing:eu-central-1:123123123123:targetgroup/target-group-1"
}
}
listener {
port = 443
protocol = "HTTPS"
default_action {
type = "forward"
target_group_arn = "arn:aws:elasticloadbalancing:eu-central-1:123123123123:targetgroup/target-group-2"
}
}
}
Voordelen van dynamic blocks
- Minder dubbele code: je schrijft de inhoud van het block één keer en genereert het meerdere keren op basis van invoerdata.
- Beter te onderhouden: configuraties beheren en bijwerken wordt eenvoudiger, omdat je alleen de invoerdata hoeft aan te passen.
- Flexibiliteit: je maakt blocks dynamisch aan op basis van wisselende invoer, waardoor je configuraties zich makkelijker aanpassen aan veranderingen.
Toepassingen van dynamic blocks
- Security group rules: maak dynamisch meerdere security group rules aan op basis van een lijst toegestane poorten.
- Tags: genereer dynamisch tags voor resources op basis van invoervariabelen of data sources.
- Network interfaces: configureer dynamisch meerdere network interfaces voor instances of andere resources.
- Auto-scaling policies: definieer dynamisch meerdere auto-scaling policies op basis van wisselende criteria.
- IAM roles: gebruik dynamic blocks om meerdere inline_policy- of assume_role_policy-blocks aan te maken.
- Load balancers: gebruik dynamic blocks om meerdere listener- of listener_rule-blocks te definiëren.
Conclusie
Dynamic blocks in Terraform zijn een krachtige manier om je infrastructuurconfiguraties te stroomlijnen en te verbeteren. Met dynamic blocks schrijf je schonere, beter te onderhouden en flexibelere Terraform-code. Of je nu security group rules, tags of andere herhaalbare geneste blocks beheert: dynamic blocks helpen je je infrastructure as code te automatiseren en op te schalen.
Probeer dynamic blocks eens in je eigen Terraform-projecten en ervaar de voordelen van een efficiëntere en modulaire configuratie. Veel programmeerplezier!
Table of contents
Related blogs
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.
No related posts found.