Terraform - Azure Service Bus Queue Deployment
In the previous post, we started a series on using Terraform to deploy Azure Service Bus resources. We covered how to deploy namespaces in Azure Service Bus.
Now, in this second post, we will look at Azure Service Bus queues, a fundamental component for efficient message handling. Queues are essential for ensuring orderly and reliable communication between different parts of your application. They temporarily store messages in a ‘first in, first out’ manner, ensuring that messages are processed in the order they are received. This is particularly valuable in scenarios where the order of operations and message delivery guarantees are crucial.
As always, I’ll show you step by step how to set them up with Terraform.
By the end, you’ll know how to manage Service Bus queues effectively using Terraform. Let’s get started.
Prerequisites #
- You need Terraform CLI on your local machine, if you’re new to using Terraform to deploy Microsoft Azure resources, then I recommend you check out this link.
- A text editor or IDE of your choice (Visual Studio Code with terraform extension is my recommendation)
Declare Azure Provider in Terraform #
The provider.tf file in Terraform is used to specify and configure the providers used in your Terraform configuration. A provider is a service or platform where the resources will be managed. This could be a cloud provider like Microsoft Azure, AWS, Google Cloud, etc.
This file is important because it tells Terraform which provider’s API to use when creating, updating, and deleting resources. Without it, Terraform wouldn’t know where to manage your resources.
provider "azurerm" {
features {}
}
Deploy Azure Resources Using Terraform #
In the case of Azure Service Bus queues, the main.tf Terraform file includes several key components tailored to set up Service Bus queues effectively:
- Resource Block for Azure Service Bus Queues: This is the core part where Azure Service Bus queues are actually created and configured. The block uses the for_each construct to iterate over each queue defined in var.servicebus_queues, creating a Service Bus queue for each. It maps each property from the variable like enable_partitioning, max_size_in_megabytes, lock_duration, etc., to the corresponding properties of the Azure Service Bus queue resource.
# Resource definition for Azure Service Bus Queues
resource "azurerm_servicebus_queue" "queue" {
# Loop through each queue in the servicebus_queues variable
for_each = { for sbq in var.servicebus_queues : sbq.name => sbq }
# Assign values from each queue object to the respective Service Bus Queue properties
name = each.value.name
namespace_id = each.value.namespace_id
enable_partitioning = each.value.enable_partitioning
max_size_in_megabytes = each.value.max_size_in_megabytes
lock_duration = each.value.lock_duration
max_message_size_in_kilobytes = each.value.max_message_size_in_kilobytes != null ? each.value.max_message_size_in_kilobytes : null
requires_duplicate_detection = each.value.requires_duplicate_detection
requires_session = each.value.requires_session
default_message_ttl = each.value.default_message_ttl != null ? each.value.default_message_ttl : null
dead_lettering_on_message_expiration = each.value.dead_lettering_on_message_expiration
duplicate_detection_history_time_window = each.value.duplicate_detection_history_time_window != null ? each.value.duplicate_detection_history_time_window : null
max_delivery_count = each.value.max_delivery_count
status = each.value.status
enable_batched_operations = each.value.enable_batched_operations
auto_delete_on_idle = each.value.auto_delete_on_idle != null ? each.value.auto_delete_on_idle : null
enable_express = each.value.enable_express
forward_to = each.value.forward_to != null ? each.value.forward_to : null
forward_dead_lettered_messages_to = each.value.forward_dead_lettered_messages_to != null ? each.value.forward_dead_lettered_messages_to : null
}
Declaration of input variables #
The variables.tf file in Terraform defines the variables you will use in the main.tf file for setting up Azure Service Bus queues. These variables enhance the flexibility and reusability of your code. In this case, the variables defined in the variables.tf include:
- servicebus_queues: This variable is a list of objects, where each object represents the configuration for an Azure Service Bus queue. It includes various properties for each queue, such as namespace_id, name, enable_partitioning, and others. This structure allows for defining multiple queues with their specific configurations in a structured and reusable manner.
- Validations: Within the variable definitions, there are validations to ensure that the input values meet specific criteria, they act as a first line of defense against configuration errors, enhancing the overall robustness of your infrastructure as code.
# Variable definition for Service Bus Queues
variable "servicebus_queues" {
description = "List of Service Bus Queues to be created"
type = list(object({
namespace_id = string
name = string
enable_partitioning = bool
max_size_in_megabytes = number
lock_duration = string
max_message_size_in_kilobytes = number # Optional, default to null
requires_duplicate_detection = bool # Optional, default to false
requires_session = bool # Optional, default to false
default_message_ttl = string # Optional, default to null
dead_lettering_on_message_expiration = bool # Optional, default to false
duplicate_detection_history_time_window = string # Optional, default to null
max_delivery_count = number # Optional, default to 10
status = string # Optional, default to "Active"
enable_batched_operations = bool # Optional, default to true
auto_delete_on_idle = string # Optional, default to null
enable_express = bool # Optional, default to false for Basic/Standard, true for Premium
forward_to = string # Optional, default to null
forward_dead_lettered_messages_to = string # Optional, default to null
}))
default = []
validation {
condition = alltrue([for q in var.servicebus_queues : can(regex("^[a-zA-Z0-9-]{3,50}$", q.name))])
error_message = "All queue names must be between 3 and 50 characters and can only contain letters, numbers, and hyphens."
}
validation {
condition = alltrue([for q in var.servicebus_queues : contains([1024, 2048, 3072, 4096, 5120], q.enable_partitioning ? q.max_size_in_megabytes / 16 : q.max_size_in_megabytes)])
error_message = "All max_size_in_megabytes values must be one of 1024, 2048, 3072, 4096, 5120 megabytes. If enable_partitioning is set to true, the specified queue size will be scaled by a factor of 16."
}
validation {
condition = alltrue([
for q in var.servicebus_queues :
(q.lock_duration == null || can(regex("^PT([0-5]?[0-9]M)?$", q.lock_duration))) &&
(q.auto_delete_on_idle == null || can(regex("^PT(([5-9]M)|([1-5]?[0-9][0-9]M)|([6-9]M[0-5]?[0-9]S)|([1-5]?[0-9][0-9]M[0-5]?[0-9]S)?)$", q.auto_delete_on_idle))) &&
(q.default_message_ttl == null || can(regex("^PT([0-5]?[0-9]M([0-5]?[0-9]S)?)?$", q.default_message_ttl))) &&
(q.duplicate_detection_history_time_window == null || can(regex("^PT([0-5]?[0-9]M([0-5]?[0-9]S)?)?$", q.duplicate_detection_history_time_window)))
])
error_message = "The values for lock_duration, auto_delete_on_idle, default_message_ttl, and duplicate_detection_history_time_window must be either null or in ISO 8601 format."
}
validation {
condition = alltrue([
for q in var.servicebus_queues :
q.status == null ||
(q.status != null && (q.status == "Active" || q.status == "Creating" || q.status == "Deleting" || q.status == "Disabled" || q.status == "ReceiveDisabled" || q.status == "Renaming" || q.status == "SendDisabled" || q.status == "Unknown"))
])
error_message = "The status must be either null or one of the following values: Active, Creating, Deleting, Disabled, ReceiveDisabled, Renaming, SendDisabled, Unknown."
}
}
Declaration of output values #
The output.tf file in Terraform extracts and displays information about the resources created or managed by your Terraform configuration. These outputs are defined using the output keyword and can be used to return information that can be useful for the user, for other Terraform configurations, or for programmatically using the information in scripts or other tools.
In this specific example, the output.tf file is configured to return information about the Azure Service Bus queues that were created. This is accomplished using the output keyword in Terraform:
- A for loop is used within the output block to iterate over each Service Bus queue created in the azurerm_servicebus_queue resource.
- For each queue, the output collects and displays the queue_id and queue_name.
# Output definition for Service Bus Queue details
output "servicebus_queue_details" {
value = {
for sbq in azurerm_servicebus_queue.queue : sbq.name => {
queue_id = sbq.id # The unique identifier of the queue
queue_name = sbq.name # The name of the queue
}
}
}
Executing the Terraform Deployment #
Now that you’ve declared the resources correctly, it’s time to take the following steps to deploy them in your Azure environment.
-
Initialization: To begin, execute the terraform init command. This will initialize your working directory that holds the .tf files and download the provider specified in the provider.tf file, and configure the Terraform backend. If you want to know how, check this link.
-
Planning: Next, execute the terraform plan. This command creates an execution plan and shows Terraform’s actions to achieve the desired state defined in your .tf files. This gives you a chance to review the changes before applying them.
-
Apply: When you’re satisfied with the plan, execute the terraform apply command. This will implement the required modifications to attain the intended infrastructure state. Before making any changes, you will be asked to confirm your decision.
-
Inspection: After applying the changes, you can use terraform show command to see the current state of your infrastructure.
-
Destroy (optional): when a project is no longer needed or when resources have become outdated. You can use the terraform destroy command. This will remove all the resources that Terraform has created.
References and useful links #
Thank you for taking the time to read my post. I sincerely hope that you find it helpful.