github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/website/docs/language/modules/develop/providers.mdx (about)

     1  ---
     2  page_title: Providers Within Modules - Configuration Language
     3  description: >-
     4    Use providers within Terraform modules. Learn about version constraints, aliases, implicit inheritance, and passing providers to Terraform modules.
     5  ---
     6  
     7  # Providers Within Modules
     8  
     9  [inpage-providers]: #providers-within-modules
    10  
    11  In a configuration with multiple modules, there are some special considerations
    12  for how resources are associated with provider configurations.
    13  
    14  Each resource in the configuration must be associated with one provider
    15  configuration. Provider configurations, unlike most other concepts in
    16  Terraform, are global to an entire Terraform configuration and can be shared
    17  across module boundaries. Provider configurations can be defined only in a
    18  root Terraform module.
    19  
    20  Providers can be passed down to descendent modules in two ways: either
    21  _implicitly_ through inheritance, or _explicitly_ via the `providers` argument
    22  within a `module` block. These two options are discussed in more detail in the
    23  following sections.
    24  
    25  A module intended to be called by one or more other modules must not contain
    26  any `provider` blocks. A module containing its own provider configurations is
    27  not compatible with the `for_each`, `count`, and `depends_on` arguments that
    28  were introduced in Terraform v0.13. For more information, see
    29  [Legacy Shared Modules with Provider Configurations](#legacy-shared-modules-with-provider-configurations).
    30  
    31  Provider configurations are used for all operations on associated resources,
    32  including destroying remote objects and refreshing state. Terraform retains, as
    33  part of its state, a reference to the provider configuration that was most
    34  recently used to apply changes to each resource. When a `resource` block is
    35  removed from the configuration, this record in the state will be used to locate
    36  the appropriate configuration because the resource's `provider` argument
    37  (if any) will no longer be present in the configuration.
    38  
    39  As a consequence, you must ensure that all resources that belong to a
    40  particular provider configuration are destroyed before you can remove that
    41  provider configuration's block from your configuration. If Terraform finds
    42  a resource instance tracked in the state whose provider configuration block is
    43  no longer available then it will return an error during planning, prompting you
    44  to reintroduce the provider configuration.
    45  
    46  ## Provider Version Constraints in Modules
    47  
    48  Although provider _configurations_ are shared between modules, each module must
    49  declare its own [provider requirements](/terraform/language/providers/requirements), so that
    50  Terraform can ensure that there is a single version of the provider that is
    51  compatible with all modules in the configuration and to specify the
    52  [source address](/terraform/language/providers/requirements#source-addresses) that serves as
    53  the global (module-agnostic) identifier for a provider.
    54  
    55  To declare that a module requires particular versions of a specific provider,
    56  use a `required_providers` block inside a `terraform` block:
    57  
    58  ```hcl
    59  terraform {
    60    required_providers {
    61      aws = {
    62        source  = "hashicorp/aws"
    63        version = ">= 2.7.0"
    64      }
    65    }
    66  }
    67  ```
    68  
    69  A provider requirement says, for example, "This module requires version v2.7.0
    70  of the provider `hashicorp/aws` and will refer to it as `aws`." It doesn't,
    71  however, specify any of the configuration settings that determine what remote
    72  endpoints the provider will access, such as an AWS region; configuration
    73  settings come from provider _configurations_, and a particular overall Terraform
    74  configuration can potentially have
    75  [several different configurations for the same provider](/terraform/language/providers/configuration#alias-multiple-provider-configurations).
    76  
    77  ## Provider Aliases Within Modules
    78  
    79  To declare multiple configuration names for a provider within a module, add the
    80  `configuration_aliases` argument:
    81  
    82  ```hcl
    83  terraform {
    84    required_providers {
    85      aws = {
    86        source  = "hashicorp/aws"
    87        version = ">= 2.7.0"
    88        configuration_aliases = [ aws.alternate ]
    89      }
    90    }
    91  }
    92  ```
    93  
    94  The above requirements are identical to the previous, with the addition of the
    95  alias provider configuration name `aws.alternate`, which can be referenced by
    96  resources using the `provider` argument.
    97  
    98  If you are writing a shared Terraform module, constrain only the minimum
    99  required provider version using a `>=` constraint. This should specify the
   100  minimum version containing the features your module relies on, and thus allow a
   101  user of your module to potentially select a newer provider version if other
   102  features are needed by other parts of their overall configuration.
   103  
   104  ## Implicit Provider Inheritance
   105  
   106  For convenience in simple configurations, a child module automatically inherits
   107  [default provider configurations](/terraform/language/providers/configuration#default-provider-configurations) from its parent. This means that
   108  explicit `provider` blocks appear only in the root module, and downstream
   109  modules can simply declare resources for that provider and have them
   110  automatically associated with the root provider configurations.
   111  
   112  For example, the root module might contain only a `provider` block and a
   113  `module` block to instantiate a child module:
   114  
   115  ```hcl
   116  provider "aws" {
   117    region = "us-west-1"
   118  }
   119  
   120  module "child" {
   121    source = "./child"
   122  }
   123  ```
   124  
   125  The child module can then use any resource from this provider with no further
   126  provider configuration required:
   127  
   128  ```hcl
   129  resource "aws_s3_bucket" "example" {
   130    bucket = "provider-inherit-example"
   131  }
   132  ```
   133  
   134  We recommend using this approach when a single configuration for each provider
   135  is sufficient for an entire configuration.
   136  
   137  ~> **Note:** Only provider configurations are inherited by child modules, not provider source or version requirements. Each module must [declare its own provider requirements](/terraform/language/providers/requirements). This is especially important for non-HashiCorp providers.
   138  
   139  In more complex situations there may be
   140  [multiple provider configurations](/terraform/language/providers/configuration#alias-multiple-provider-configurations),
   141  or a child module may need to use different provider settings than
   142  its parent. For such situations, you must pass providers explicitly.
   143  
   144  ## Passing Providers Explicitly
   145  
   146  When child modules each need a different configuration of a particular
   147  provider, or where the child module requires a different provider configuration
   148  than its parent, you can use the `providers` argument within a `module` block
   149  to explicitly define which provider configurations are available to the
   150  child module. For example:
   151  
   152  ```hcl
   153  # The default "aws" configuration is used for AWS resources in the root
   154  # module where no explicit provider instance is selected.
   155  provider "aws" {
   156    region = "us-west-1"
   157  }
   158  
   159  # An alternate configuration is also defined for a different
   160  # region, using the alias "usw2".
   161  provider "aws" {
   162    alias  = "usw2"
   163    region = "us-west-2"
   164  }
   165  
   166  # An example child module is instantiated with the alternate configuration,
   167  # so any AWS resources it defines will use the us-west-2 region.
   168  module "example" {
   169    source    = "./example"
   170    providers = {
   171      aws = aws.usw2
   172    }
   173  }
   174  ```
   175  
   176  The `providers` argument within a `module` block is similar to
   177  [the `provider` argument](/terraform/language/meta-arguments/resource-provider)
   178  within a resource, but is a map rather than a single string because a module may
   179  contain resources from many different providers.
   180  
   181  The keys of the `providers` map are provider configuration names as expected by
   182  the child module, and the values are the names of corresponding configurations
   183  in the _current_ module.
   184  
   185  Once the `providers` argument is used in a `module` block, it overrides all of
   186  the default inheritance behavior, so it is necessary to enumerate mappings
   187  for _all_ of the required providers. This is to avoid confusion and surprises
   188  that may result when mixing both implicit and explicit provider passing.
   189  
   190  Additional provider configurations (those with the `alias` argument set) are
   191  _never_ inherited automatically by child modules, and so must always be passed
   192  explicitly using the `providers` map. For example, a module
   193  that configures connectivity between networks in two AWS regions is likely
   194  to need both a source and a destination region. In that case, the root module
   195  may look something like this:
   196  
   197  ```hcl
   198  provider "aws" {
   199    alias  = "usw1"
   200    region = "us-west-1"
   201  }
   202  
   203  provider "aws" {
   204    alias  = "usw2"
   205    region = "us-west-2"
   206  }
   207  
   208  module "tunnel" {
   209    source    = "./tunnel"
   210    providers = {
   211      aws.src = aws.usw1
   212      aws.dst = aws.usw2
   213    }
   214  }
   215  ```
   216  
   217  The subdirectory `./tunnel` must then declare the configuration aliases for the
   218  provider so the calling module can pass configurations with these names in its `providers` argument:
   219  
   220  ```hcl
   221  terraform {
   222    required_providers {
   223      aws = {
   224        source  = "hashicorp/aws"
   225        version = ">= 2.7.0"
   226        configuration_aliases = [ aws.src, aws.dst ]
   227      }
   228    }
   229  }
   230  ```
   231  
   232  Each resource should then have its own `provider` attribute set to either
   233  `aws.src` or `aws.dst` to choose which of the two provider configurations to
   234  use.
   235  
   236  ## Legacy Shared Modules with Provider Configurations
   237  
   238  In Terraform v0.10 and earlier there was no explicit way to use different
   239  configurations of a provider in different modules in the same configuration,
   240  and so module authors commonly worked around this by writing `provider` blocks
   241  directly inside their modules, making the module have its own separate
   242  provider configurations separate from those declared in the root module.
   243  
   244  However, that pattern had a significant drawback: because a provider
   245  configuration is required to destroy the remote object associated with a
   246  resource instance as well as to create or update it, a provider configuration
   247  must always stay present in the overall Terraform configuration for longer
   248  than all of the resources it manages. If a particular module includes
   249  both resources and the provider configurations for those resources then
   250  removing the module from its caller would violate that constraint: both the
   251  resources and their associated providers would, in effect, be removed
   252  simultaneously.
   253  
   254  Terraform v0.11 introduced the mechanisms described in earlier sections to
   255  allow passing provider configurations between modules in a structured way, and
   256  thus we explicitly recommended against writing a child module with its own
   257  provider configuration blocks. However, that legacy pattern continued to work
   258  for compatibility purposes -- though with the same drawback -- until Terraform
   259  v0.13.
   260  
   261  Terraform v0.13 introduced the possibility for a module itself to use the
   262  `for_each`, `count`, and `depends_on` arguments, but the implementation of
   263  those unfortunately conflicted with the support for the legacy pattern.
   264  
   265  To retain the backward compatibility as much as possible, Terraform v0.13
   266  continues to support the legacy pattern for module blocks that do not use these
   267  new features, but a module with its own provider configurations is not
   268  compatible with `for_each`, `count`, or `depends_on`. Terraform will produce an
   269  error if you attempt to combine these features. For example:
   270  
   271  ```
   272  Error: Module does not support count
   273  
   274    on main.tf line 15, in module "child":
   275    15:   count = 2
   276  
   277  Module "child" cannot be used with count because it contains a nested provider
   278  configuration for "aws", at child/main.tf:2,10-15.
   279  
   280  This module can be made compatible with count by changing it to receive all of
   281  its provider configurations from the calling module, by using the "providers"
   282  argument in the calling module block.
   283  ```
   284  
   285  To make a module compatible with the new features, you must remove all of the
   286  `provider` blocks from its definition.
   287  
   288  If the new version of the module declares `configuration_aliases`, or if the
   289  calling module needs the child module to use different provider configurations
   290  than its own default provider configurations, the calling module must then
   291  include an explicit `providers` argument to describe which provider
   292  configurations the child module will use:
   293  
   294  ```hcl
   295  provider "aws" {
   296    region = "us-west-1"
   297  }
   298  
   299  provider "aws" {
   300    region = "us-east-1"
   301    alias  = "east"
   302  }
   303  
   304  module "child" {
   305    count = 2
   306    providers = {
   307      # By default, the child module would use the
   308      # default (unaliased) AWS provider configuration
   309      # using us-west-1, but this will override it
   310      # to use the additional "east" configuration
   311      # for its resources instead.
   312      aws = aws.east
   313    }
   314  }
   315  ```
   316  
   317  Since the association between resources and provider configurations is
   318  static, module calls using `for_each` or `count` cannot pass different
   319  provider configurations to different instances. If you need different
   320  instances of your module to use different provider configurations then you
   321  must use a separate `module` block for each distinct set of provider
   322  configurations:
   323  
   324  ```hcl
   325  provider "aws" {
   326    alias  = "usw1"
   327    region = "us-west-1"
   328  }
   329  
   330  provider "aws" {
   331    alias  = "usw2"
   332    region = "us-west-2"
   333  }
   334  
   335  provider "google" {
   336    alias       = "usw1"
   337    credentials = "${file("account.json")}"
   338    project     = "my-project-id"
   339    region      = "us-west1"
   340    zone        = "us-west1-a"
   341  }
   342  
   343  provider "google" {
   344    alias       = "usw2"
   345    credentials = "${file("account.json")}"
   346    project     = "my-project-id"
   347    region      = "us-west2"
   348    zone        = "us-west2-a"
   349  }
   350  
   351  module "bucket_w1" {
   352    source    = "./publish_bucket"
   353    providers = {
   354      aws.src    = aws.usw1
   355      google.src = google.usw1
   356    }
   357  }
   358  
   359  module "bucket_w2" {
   360    source    = "./publish_bucket"
   361    providers = {
   362      aws.src    = aws.usw2
   363      google.src = google.usw2
   364    }
   365  }
   366  ```