As with any software, terraform also has hidden gems waiting to be discovered, even after you’ve obtained your associate certification. Some features aren’t always known until you need them, which is why we still have a lot to learn from the product. Today is one of those days! In this post, I will show how to deploy Multi-Region Resources using something called provider aliases.
The reason why the provider alias feature is not commonly used is that most users typically deploy resources in a single region at a time. Unless you have a setup that requires a DR configuration with regional failover or a distributed workload across several regions. The provider block, which is placed in the root module of a Terraform configuration, dictates the default location where all resources will be created.
To support multi-region deployment, you can include multiple configurations for a given provider by including multiple provider blocks with the same provider name, but different alias meta-arguments for each additional configuration. see Hashi corp's example below:
# Default provider configuration #region1 (un-aliased)
provider "aws" {
region = "us-east-1"
}
}
# Extra configuration for #region2 (“us-west-2”), reference this as `aws.west`.
provider "aws" {
alias = "west" <<--------------- our identifier
region = "us-west-2"
}
How to reference it from a resource block
To use extra provider configuration for a resource or data source, set its provider
argument to a <PROVIDER NAME>.<ALIAS>
defined earlier:
resource "aws_instance" "my_instance" {
provider = aws.west <<---- reference allowing the instance creation in us-west-2
…
}
Let’s consider a scenario where a HA firewall setup (active-active) requires 4 public IP addresses in two different regions. We’ll leverage provider aliases to achieve this multi-region deployment.
Toronto => primary site (default) while Montreal (aliased) => failover region
4 IPs per region will be deployed
Public IP for Firewall Primary VM Management Interface
Public IP for Firewall Secondary VM Management Interface
Floating Public IP for Firewall Untrust Interface
Floating Public IP for Firewall Untrust Interface inbound flow (frontend cluster ip)
Clone the Repository
This is my own GitHub repo, Pick an area on your file system and run the clone command
$ git clone https://github.com/brokedba/terraform-examples.git
You will find our configuration under a subdirectory called terraform-provider-oci/publicIPs
$ cd ~/terraform-examples/terraform-provider-oci/publicIPs
$ terraform init
Here’s a tree of the files composing our configuration
$ tree
.
|-- variables.tf ---> Resource variables needed for the deploy including locals
|-- publicip.tf ---> Our main public IP resource declaration
|-- output.tf ---> displays the IP resources detail at the end of the deploy
|-- terraform.tfvars.template ---> environment_variables needed to authenticate to OCI
Now let’s check how and where the aliases are defined and referenced
Here, I explicitly set an alias for the default configuration ‘primary’ but it’s not necessary. Only dr alias is needed.
# vi ./terraform-provider-oci/publicIPs/variables.tf
provider "oci" { # OPtional since it’s the default config
alias = "primary" <<--- Default region Toronto
tenancy_ocid = var.tenancy_ocid
user_ocid = var.user_ocid
fingerprint = var.fingerprint
private_key_path = var.private_key_path
region = var.region <<---- "ca-toronto-1"}
…
provider "oci" {alias = "dr" <<--- Alternative region Montreal
…
}
By using local variables, I stored the display names of all my public IPs. This allows me to leverage a single dynamic block and a for_each loop to create all the public IPs per region efficiently.
As explained before, alias reference is easy through a simple provider argument
resource "oci_core_public_ip" "dr_firewall_public_ip" {
provider = oci.dr <<---- reference allowing IP creation in Montreal region
for_each = local.ips.dr_site
compartment_id = var.tenancy_ocid
lifetime = "RESERVED"
#Optional
display_name = each.key
}
Under the working directory (terraform-provider-oci/publicIPs) update terraform.tfvars file
Run terraform plan (see below an example of a public IP resource block referencing the Montreal Region)
#Adjust terraform.tfvars.template with authentication parameters & rename it to terraform.tfvars$ terraform plan
.Terraform will perform the following actions:
# oci_core_public_ip.dr_firewall_public_ip["dr-mgmt-public_ip-vm-c"] will be created
+ resource "oci_core_public_ip" "dr_firewall_public_ip" {
+ assigned_entity_id = (known after apply)
+ assigned_entity_type = (known after apply)
+ availability_domain = (known after apply)
+ compartment_id = "ocid1.tenancy.oc1..aaaaaaaavxxxxxxxxxxxxx"
+ defined_tags = (known after apply)
+ display_name = "dr-mgmt-public_ip-vm-c"
+ freeform_tags = (known after apply)
+ id = (known after apply)
+ ip_address = (known after apply)
+ lifetime = "RESERVED"
+ public_ip_pool_id = (known after apply)
+ scope = (known after apply)
+ state = (known after apply)
+ time_created = (known after apply)
}..
# oci_core_public_ip.dr_firewall_public_ip["dr-mgmt-public_ip-vm-d"] will be created + resource "oci_core_public_ip" "dr_firewall_public_ip" {
...
# oci_core_public_ip.dr_firewall_public_ip["dr-untrust-floating-public_ip"]..
+ resource "oci_core_public_ip" "dr_firewall_public_ip" {
...
# oci_core_public_ip.dr_firewall_public_ip["dr-untrust-floating-public_ip_frontend_1"] ..
+ resource "oci_core_public_ip" "dr_firewall_public_ip" {
... --- OTHER Primary region resources
Plan: 8 to add, 0 to change, 0 to destroy.Changes to Outputs:
+ Montreal_public_ips = {
+ dr-mgmt-public_ip-vm-c = (known after apply)
+ dr-mgmt-public_ip-vm-d = (known after apply)
+ dr-untrust-floating-public_ip = (known after apply)
+ dr-untrust-floating-public_ip_frontend_1 = (known after apply)
}
+ Toronto_public_ips = {
+ mgmt-public_ip-vm-a = (known after apply)
+ mgmt-public_ip-vm-b = (known after apply)
+ untrust-floating-public_ip = (known after apply)
+ untrust-floating-public_ip_frontend_1 = (known after apply)
}
And here you 8 resources created among which 4 public IPs in both regions (Toronto/Montreal) with one config
#original output was truncated for more visibility$ terraform apply -–auto-approve
oci_core_public_ip.primary_firewall_public_ip["mgmt-public_ip-vm-a"]: Creating... oci_core_public_ip.primary_firewall_public_ip[untrust-floating-public_ip_frontend_1]:Creating.. oci_core_public_ip.primary_firewall_public_ip["mgmt-public_ip-vm-b"]: Creating... oci_core_public_ip.primary_firewall_public_ip["untrust-floating-public_ip"]: Creating... oci_core_public_ip.dr_firewall_public_ip["dr-mgmt-public_ip-vm-c"]: Creating... oci_core_public_ip.dr_firewall_public_ip["dr-mgmt-public_ip-vm-d"]: Creating... oci_core_public_ip.dr_firewall_public_ip["dr-untrust-floating-public_ip"]: Creating... oci_core_public_ip.dr_firewall_public_ip["dr-untrust-floating-public_ip_frontend_1"]:Creating.. Apply complete! Resources: 8 added, 0 changed, 0 destroyed. Outputs:
+ Montreal_public_ips = {
+ dr-mgmt-public_ip-vm-c = "name: dr-mgmt-public_ip-vm-c IP:155… OCID:xx"
+ dr-mgmt-public_ip-vm-d = "name: dr-mgmt-public_ip-vm-d IP:155…OCID:xx"
+ dr-untrust-floating-public_ip = name: dr-untrust-floating-public_ip IP:155…
+ dr-untrust-floating-public_ip_frontend_1 = "name: dr-untrust-floating-public_ip_frontend_1 …
}
+ Toronto_public_ips = {
+ mgmt-public_ip-vm-a = "name: mgmt-public_ip-vm-a...
+ mgmt-public_ip-vm-b = "name: mgmt-public_ip-vm-b ...
+ untrust-floating-public_ip = "name: untrust-floating-public_ip...
+ untrust-floating-public_ip_frontend_1 = "name: untrust-floating-public_ip_frontend_1..
}
To declare a configuration alias within a module in order to receive an alternate provider configuration from the parent module, you can add the aliases using the Configuration_aliases
the argument to the r provider’s required_providers
entry.
terraform {
required_version = ">= 1.0.3"
required_providers {
oci = {
source = "oracle/oci"
version = "4.105.0"
configuration_aliases = [ oci.primary, oci.dr ]
} }
}
Provider aliases in Terraform provide a powerful capability to deploy resources across multiple regions
This allows you to simplify your Terraform configuration and avoid duplicating code for each region
Provider aliases can also be used for targeting multiple Docker hosts, multiple Consul hosts, etc…
Sometimes a nonpopular feature doesn’t mean hard to implement as a quick look at the doc can get you going. Hope this was helpful