DevOps Shack
Null Resource in Terraform
Definition:
A null_resource in Terraform allows you to define resources that don't inherently
create or manage any real infrastructure but can execute provisioners and triggers,
enabling you to run scripts or perform actions based on conditions in your Terraform
configuration.
Use Cases:
1. Executing Scripts Post-Deployment: After deploying infrastructure, you might want
to run a script to configure software, restart services, or perform other tasks.
2. Custom Logic Execution: When certain conditions change, you may want to execute
custom logic such as notifying a monitoring service, updating a configuration
management database, or triggering a Jenkins job.
3. Handling External Dependencies: If you need to run tasks that involve external
systems not directly managed by Terraform.
Example:
resource "null_resource" "example" {
triggers = {
always_run = "${timestamp()}"
}
provisioner "local-exec" {
command = "echo 'This is a null resource execution'"
}
}
Dynamic Block in Terraform
Definition:
A dynamic block in Terraform allows you to generate multiple nested blocks inside a
resource based on a collection (like a list or map). This feature helps you manage
resources more efficiently by reducing repetitive code.
Use Cases:
1. Variable Number of Sub-Resources: When the number of sub-resources or
configurations is not fixed and depends on input variables or data sources.
2. Complex Configurations: When dealing with complex resources that require
multiple nested blocks, such as security groups, IAM policies, or network rules.
3. Reusable and Scalable Modules: When creating modules that need to handle
varying configurations without hardcoding them.
Example:
variable "security_group_rules" {
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
}))
default = []
}
resource "aws_security_group" "example" {
name = "example_sg"
dynamic "ingress" {
for_each = var.security_group_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
Differences and When to Use Each
Null Resource:
• Purpose: Used for executing arbitrary scripts or commands not tied to a specific
resource type.
• Trigger Mechanism: Uses the triggers argument to determine when to re-run
provisioners.
• Use Cases: When you need to execute actions post-deployment, integrate with
external systems, or add custom logic that doesn't directly manage infrastructure.
Dynamic Block:
• Purpose: Used to dynamically generate nested blocks within a resource, making
configurations more flexible and manageable.
• Trigger Mechanism: Uses for_each to iterate over collections and generate nested
blocks.
• Use Cases: When dealing with variable sub-resources, handling complex resource
configurations, or creating reusable and scalable Terraform modules.
Real-Time Use Cases
Null Resource:
1. Running a Custom Script Post Deployment:
o After deploying a VM, use a null_resource to run a script that installs and
configures additional software.
2. resource "null_resource" "post_deploy" {
3. provisioner "remote-exec" {
4. connection {
5. type = "ssh"
6. user = "ubuntu"
7. private_key = file("~/.ssh/id_rsa")
8. }
9. script = "scripts/post_deploy.sh"
10. }
}
11. Triggering a Jenkins Job:
o Use a null_resource to trigger a Jenkins job after deploying infrastructure.
12. resource "null_resource" "trigger_jenkins" {
13. triggers = {
14. build_id = "${uuid()}"
15. }
16.
17. provisioner "local-exec" {
18. command = "curl -X POST
'http://jenkins.example.com/job/build/buildWithParameters?token=TOKEN
'"
19. }
}
Dynamic Block:
1. Configuring Security Group Rules:
o Dynamically create security group rules based on input variables.
2. variable "sg_rules" {
3. type = list(object({
4. from_port = number
5. to_port = number
6. protocol = string
7. cidr_blocks = list(string)
8. }))
9. default = []
10. }
11.
12. resource "aws_security_group" "dynamic_sg" {
13. name = "dynamic_sg"
14.
15. dynamic "ingress" {
16. for_each = var.sg_rules
17. content {
18. from_port = ingress.value.from_port
19. to_port = ingress.value.to_port
20. protocol = ingress.value.protocol
21. cidr_blocks = ingress.value.cidr_blocks
22. }
23. }
}
24. Creating Multiple S3 Buckets:
o Use a dynamic block to create multiple S3 buckets based on a list of names.
25. variable "bucket_names" {
26. type = list(string)
27. default = ["bucket1", "bucket2"]
28. }
29.
30. resource "aws_s3_bucket" "dynamic_buckets" {
31. for_each = toset(var.bucket_names)
32. bucket = each.key
}