terraformhashicorpterraform-modules

Terraform "required_providers" block, configuration_aliases argument


I think of myself as a terraform intermediate that is looking to fine grain its knowledge.

After reading probably 20 times the following documentation: https://developer.hashicorp.com/terraform/language/providers/configuration

Researching online, and exercising locally. I am not able to understand the function of this highlighted argument

From the docs šŸ‘‡šŸ»

terraform {
  required_providers {
    mycloud = {
      source  = "mycorp/mycloud"
      version = "~> 1.0"
      *configuration_aliases = [ mycloud.alternate ]*
    }
  }
}

What exactly does "configuration_aliases" do?

It is clear to me that the "provider" block is used to configure the provider with data needed in order to make the API calls. It is clear to me what a provider alias does and that you can call it (to call for the creation of the same resource using different configuration). But I cannot understand how to use the the configuration_aliases in the required providers.

As of now what I drafted in my pseudo-code is this (it is all in one for simplicity):

main.tf

terraform {
  required_providers {
    restapi = {
      source  = "Mastercard/restapi"
      version = "1.18.2"
    }
  }
}
        
provider "restapi" {
  alias                = "main"
  uri                  = "https://my-cortex.io"
  debug                = true
  write_returns_object = true
  headers = {
    "Content-Type"  = "application/json"
    "Authorization" = "Basic ${base64encode("api-key")}"
  }
}
    
module "create_bucket" {
  source = "./modules/create_bucket"
}

main.tf - module create_bucket

terraform {
  required_providers {
    restapi = {
      source                = "Mastercard/restapi"
      version               = "1.18.2"
      configuration_aliases = [ restapi.main ]
    }
  }
}
        
resource "restapi_object" "s3_bucket_creation_all_off" {
  path = "/api/v1/project/7a274931/services/object-storage/buckets"
  data = <<EOF
    {
       "region": "3cc5e278",
       "label": "to_delete",
       "gb_allocated": 1024,
       "tier": "standard",
       "versioning": false,
       "data_preservation": "none"
    }
EOF
  id_attribute = "result/id" #just to assign a value
  object_id = "update_result" #just to assigna a value
}

my directory composition

When I run the above code, what I get is the following error:

Error: Missing required provider configuration ā”‚ ā”‚ on main.tf line 1: ā”‚ 1: module "create_bucket" { ā”‚ ā”‚ The child module requires an additional configuration for provider ā”‚ mastercard/restapi, with the local name "restapi.main". ā”‚ ā”‚ Refer to the module's documentation to understand the intended ā”‚ purpose of this additional provider configuration, and then add an ā”‚ entry for restapi.main in the "providers" meta-argument in the ā”‚ module block to choose which provider configuration the module ā”‚ should use for that purpose.

What am I missing? I am declaring an alias in the root config then calling the alias inside the required provider inside the module in order to pass a config to the required_provider block so that I don't have to declare the provider block inside the module which is bad practice.

TL;DR : configuration_aliases in terraform required providers block. In simple words what does it do? How do you correctly invoke it?


Solution

  • Configuration aliases are usually used when there is a need to deploy a set of resources in more than one region (most commonly related to different cloud providers like AWS, GCP, Azure, etc). This is needed because you cannot have more than one provider block using the same name. Think for example about an API you would need to deploy in the EU and in the USA. Now, in your example, you are using a root module which is using this provider block:

    provider "restapi" {
      alias                = "main"
      uri                  = "https://my-cortex.io"
      debug                = true
      write_returns_object = true
      headers = {
        "Content-Type"  = "application/json"
         "Authorization" = "Basic ${base64encode("api-key")}"
      }
    }
    

    Then, you are calling the child module like this:

    module "create_bucket" {
      source = "./modules/create_bucket"
    }
    

    However, the child module is very specific about the provider it expects:

    The child module requires an additional configuration for provider ā”‚ mastercard/restapi, with the local name "restapi.main".

    Or, in terraform code:

    terraform {
      required_providers {
        restapi = {
          source                = "Mastercard/restapi"
          version               = "1.18.2"
          configuration_aliases = [ restapi.main ]
        }
      }
    }
    

    Which means that when calling this module in particular, you need to provide the configuration alias it expects. So something like this should work:

    module "create_bucket" {
      providers = {
        restapi.main = restapi.main # since this is what you used as an alias for the provider in the *root* module
      }
      source = "./modules/create_bucket"
    }
    

    Finally, the entire code block for the root module should look like this:

    terraform {
      required_providers {
        restapi = {
          source  = "Mastercard/restapi"
          version = "1.18.2"
        }
      }
    }
    
    provider "restapi" {
      alias                = "main"
      uri                  = "https://my-cortex.io"
      debug                = true
      write_returns_object = true
      headers = {
        "Content-Type"  = "application/json"
        "Authorization" = "Basic ${base64encode("api-key")}"
      }
    }
    
    module "create_bucket" {
      providers = {
        # child module provider name = root module provider name you are passing 
        restapi.main                 = restapi.main
      }  
      source = "./modules/create_bucket"
    }
    

    So, the short version: the left-hand side is the provider name in the child module (i.e., the module you are calling) while the right-hand side is the provider configuration in the root module (i.e., the module you are calling the child module from). This way you are assigning the root module provider configuration to the child module provider configuration. This is also displayed in the error message:

    ā”‚ Refer to the module's documentation to understand the intended ā”‚ purpose of this additional provider configuration, and then add an ā”‚ entry for restapi.main in the "providers" meta-argument in the ā”‚ module block to choose which provider configuration the module ā”‚ should use for that purpose.

    Finally, as this seems like a pet-project, you actually don't need to use configuration aliases at all in this case, since you have only one provider configuration in both root and child modules. You could have easily gotten away with either of the next two:

    terraform {
      required_providers {
        restapi = {
          source  = "Mastercard/restapi"
          version = "1.18.2"
        }
      }
    }
    
    provider "restapi" {
      uri                  = "https://my-cortex.io"
      debug                = true
      write_returns_object = true
      headers = {
        "Content-Type"  = "application/json"
        "Authorization" = "Basic ${base64encode("api-key")}"
      }
    }
    
    module "create_bucket" {
      providers = {
        restapi.main = restapi
      }  
      source = "./modules/create_bucket"
    }
    

    This still uses the configuration alias in the child module. The second example is without configuration alias:

    terraform {
      required_providers {
        restapi = {
          source  = "Mastercard/restapi"
          version = "1.18.2"
        }
      }
    }
    
    provider "restapi" {
      uri                  = "https://my-cortex.io"
      debug                = true
      write_returns_object = true
      headers = {
        "Content-Type"  = "application/json"
        "Authorization" = "Basic ${base64encode("api-key")}"
      }
    }
    
    module "create_bucket" {
      source = "./modules/create_bucket"
    }
    

    And then the child module would look like:

    terraform {
      required_providers {
        restapi = {
          source  = "Mastercard/restapi"
          version = "1.18.2"
        }
      }
    }
        
    resource "restapi_object" "s3_bucket_creation_all_off" {
      path = "/api/v1/project/7a274931/services/object-storage/buckets"
      data = <<EOF
        {
            "region": "3cc5e278",
            "label": "to_delete",
            "gb_allocated": 1024,
            "tier": "standard",
            "versioning": false,
            "data_preservation": "none"
        }
    EOF
      id_attribute = "result/id" #just to assign a value
      object_id = "update_result" #just to assigna a value
    }