I have a terraform plan that defines most of my BQ environment.
I'm working on a cross-region deployment which will replicate some of my tables to multiple regions.
Rather than copy pasting the same module in every place that I need it at, I'd like to define the module in one place and just call that on every configuration that needs it.
Example I have the following file structure
./cross_region_tables
-> tables.tf
./foo
-> tables.tf
./bar
-> tables.tf
I'd like to define some_module in ./cross_region_tables/tables.tf like so
output "some_module" {
x = something
region = var.region
}
Then I'd like just call some_module from ./foo/tables.tf
The problem is that I don't know how to call this specific module, since ./cross_region_tables/tables.tf will contain several table definitions (as output objects). I know how to import a child module, but I don't know how to call a specific output within that child module
I've solved the issue by adding a module object to the child module with a variable for the region, then calling the child from each regional configuration and passing the region as a variable.
in child folder main.tf:
variable "region" = {}
module "foo" {
x = "something"
y = "something_else"
region = var.region
}
in regional folder for regionX
variable "region" = {
default = regionX
}
module "child" {
source = "../path/to/child"
region = var.region
}
in regional folder for regionY
variable "region" = {
default = regionY
}
module "child" {
source = "../path/to/child"
region = var.region
}
repeat for as many regions as necessary.
You can pass the provider to your modules and each provider with a different
region...
That is well documented here:
https://www.terraform.io/language/modules/develop/providers#passing-providers-explicitly
# The default "aws" configuration is used for AWS resources in the root
# module where no explicit provider instance is selected.
provider "aws" {
region = "us-west-1"
}
# An alternate configuration is also defined for a different
# region, using the alias "usw2".
provider "aws" {
alias = "usw2"
region = "us-west-2"
}
# An example child module is instantiated with the alternate configuration,
# so any AWS resources it defines will use the us-west-2 region.
module "example" {
source = "./example"
providers = {
aws = aws.usw2
}
}
The other part is what you mentioned:
The problem is that I don't know how to call this specific module, since ./cross_region_tables/tables.tf will contain several table definitions
Resources within that module (cross_region_tables) can be turned off/on with variables
Related
I have a use case where I need two AWS providers for different resources. The default aws provider is configured in the main module which uses another module that defines the additional aws provider.
By default, I'd like both providers to use the same AWS credentials unless explicitly overridden.
I figured I could do something like this. In the main module:
locals {
foo_cloud_access_key = aws.access_key
foo_cloud_secret_key = aws.secret_key
}
variable "foo_cloud_access_key" {
type = string
default = local.foo_cloud_access_key
}
variable "foo_cloud_secret_key" {
type = string
default = local.foo_cloud_secret_key
}
where variables foo_cloud_secret_key and foo_cloud_access_key would then be passed down to the child module like this:
module foobar {
...
foobar_access_key = var.foo_cloud_access_key
foobar_secret_key = var.foo_cloud_secret_key
...
}
Where module foobar would then configure its additional was provide with these variables:
provider "aws" {
alias = "foobar_aws"
access_key = var.foobar_access_key
secret_key = var.foobar_secret_key
}
When I run the init terraform spits out this error (for both variables):
Error: Variables not allowed
on variables.tf line 66, in variable "foo_cloud_access_key":
66: default = local.foo_cloud_access_key
Variables may not be used here.
Is it possible to achieve something like this in terraform or is there any other way to go about this?
Having complex, computed default values of variables is possible, but only with a workaround:
define a dummy default value for the variable, e.g. null
define a local variable, its value is either the value of the variable or the actual default value
variable "something" {
default = null
}
locals {
some_computation = ... # based on whatever data you want
something = var.something == null ? local.some_computation : var.something
}
And then only only use local.something instead of var.something in the rest of the terraform files.
I had a main.tf custom TF module, the module will deploy a lists of another custom modules. I can deploy single region by passing single value into main.tf, but when I tried multi-region which passing an array of regions and loop in main.tf's custom module. It doesn't work.
main.tf
locals {
regions = ["asia-east2", "asia-southeast1"]
}
module "main" {
source = "./modules/main"
for_each = local.regions
region = each.value
}
Error:
The name "for_each" is reserved for use in a future version of Terraform.`
Notes
I know Terraform 0.12 doesn't support this feature. Is there a way to loop through multi-region without passing down into child module's resource level ?
I know Terraform 0.12 doesn't support this feature. Is there a way to loop through multi-region without passing down into child module's resource level ?
No. There is no way to loop through local.regions and pass them into modules in Terraform 0.12 because count and for_each for modules were added in Terraform 0.13.
Your only option is to make multiple references.
locals {
regions = ["asia-east2", "asia-southeast1"]
}
module "main_0" {
source = "./modules/main"
region = local.regions[0]
}
module "main_1" {
source = "./modules/main"
region = local.regions[1]
}
I am learning terraform modules.
I've created module for Google Provider.
provider "google" {
credentials = "${var.credentials}"
project = "${var.project_id}"
region = "${var.region}"
zone = "${var.zone}"
}
I want to pass credential file path form the module consuming above.
Here is the consumer module.
main.tf
module "google" {
source = "../modules/google-provider"
project_id = "${var.project_id}"
credentials = "${var.credentials}"
}
variables.tf
variable "credentials" {
default = "${file("cred.json")}"
}
This is the error I am getting:
Error: variable "credentials": default may not contain interpolations
I read this stackoverflow comment but did not understand how it will work.
Thank you for the help in advance.
from the docs,
When you declare variables in the root module of your configuration,
you can set their values using CLI options and environment variables.
When you declare them in child modules, the calling module should pass
values in the module block.
In your case,
#This is your calling module, hence you need to pass variables to child module from here
module "google" {
source = "../modules/google-provider"
passed_project_id_to_child = "${var.project_id}"
passed_credentials_to_child = "${var.credentials}"
}
UPDATE: for some reasons, terraform is not allowing you to read file with interpolation syntax create a data source of type local_file docs
data "local_file" "credJSON" {
filename = "./cred.json"
}
then you will need to do something like this in your module's configuration file or you can also create a seperate file for that too,
variable passed_project_id_to_child{
default = "${jsonencode(data.credJSON.content).projectId}"
}
variable passed_credentials_to_child{}
provider "google" {
credentials = "${var.passed_project_id_to_child}"
project = "${var.passed_project_id_to_child}"
region = "${var.region}"
zone = "${var.zone}"
}
Hopefully this works.
Read more here
I have defined the following Terraform module:
module "lambda" {
source = "../lambda"
region = "us-west-1"
account = "${var.account}"
}
How can I take advantage from the module name to set the source parameter with an interpolation? I wish something like:
module "lambda" {
source = "../${this.name}"
region = "us-west-1"
account = "${var.account}"
}
locals {
module = basename(abspath(path.module))
}
{
...
some-id = local.module
...
}
I think is not possible. There's a self that allows you to reference attributes within your resource, but the identifier is not an attribute. Also, self is only allowed within provisioners.
I guess the only way to accomplish what you want is templating the .tf files, like:
module {{ my-module}} {
source = "../{{ my-module }}"
region = "us-west-1"
account = "${var.account}"
but you should render the templates before terraform init. It's straightforward to setup in a CI pipeline, but I find it cumbersome when working locally.
I have inherited a project with two files:
a.tf
dir_name/b.tf
and each contains:
variable "region" {
default = "us-east-1"
}
Is there any reason why I can't delete the variable definition from dir_name/b.tf as it seems to be already defined?
UPDATE
a.tf contains a module definition that goes like this:
module "dir_name" {
source = "./dir_name"
}
No, you can't remove one or the other. Terraform works on the module level, where each module has an explicit set of input variables and output attributes; variables can't be passed implicitly from one script to a module.
If you're passing variables from one place to another, it does currently result in a lot of repetition, eg:
module "dir_name" {
source = "./dir_name"
region = "${var.region}"
}