How to Deploy Multi-Region Resources with Terraform: Example (OCI Public IPs)

Share on:




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.


Why multi region deploy isn’t that common?

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.


Understanding Provider Aliases

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-argument for each additional configuration. see Hashicorp’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


Practical Scenario: Deploying Public IPs in Multiple Regions in OCI

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

You will find our configuration under a subdirectory called terraform-provider-oci/publicIPs

  • Cd Into the subdirectory where our configuration resides and run the init
$ cd   ~/terraform-examples/terraform-provider-oci/publicIPs
$ terraform init
  • Here’s a tree of the files composing our configuration

$ tree
  |--      ---> Resource variables needed for the deploy including locals  
  |--       ---> Our main public IP resource declaration  
  |--         ---> 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  


Provider Block

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/ 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 …


Resource-based Reference

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"
    display_name = each.key


1. Execution Plan (Plan)

  • 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)


2. Deployment (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..


How about Terraform Modules?

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

Share on:

More from this Author

Terraform Pipelines for Dummies Part 1 Run a Terraform Configuration in GitLabCI

Terraform Pipelines for Dummies Part 1: Run a Terraform Configuration in GitLabCI

Introduction Automating infrastructure provisioning with Terraform is nothing for many, but to truly harness IaC power, seamless integration with ... Read More

Farewell to ClickOps OCI CLI Seamless DataGuard Replication for ExaC@C (1)

Farewell to ClickOps: OCI CLI Seamless Data Guard Replication for ExaC@C

Introduction Since the very beginning, everyone was introduced to Cloud Services through the console as it’s very quick. But the cloud CLI tooling ... Read More

Back to Top