Terraform module secret - terraform

I'll trying to create my first module terraform based on a provider who need an api_key.
provider "foo" {
api_key = secretapikey
}
I don't wan't to expose this secret (and configuration) in projects that will use this module.
This is my current implementation, but all projects must re-declare these variables and set the associated environment variables
# module file /module/foo
provider "foo" {
api_key = var.api_key
}
variable "api_key" {
sensitive = true
}
In the project that will use the module I have to define the variable again (+ export)
# project in other repository that use the module
# export TF_VAR_api_key="secretapikey"
variable "api_key" {
sensitive = true
}
module "bar" {
source = "../module/foo"
name = "toto"
api_key= var.api_key
}
How can I improve this ?

Related

Paramaterize terraform key names?

I'm currently using terraform to write secrets to AWS Secrets Manager and I'm not able to write them via the UI.
I want to be able to parameterize the secret keys. Currently I have written something like this, with the environment
variable "secrets" {
default = {
dev_project_db = "database"
prod_project_db = "database"
qa_project_db = "database"
}
type = map(string)
}
Instead I want to do something like this (for when the value is the same between environments)
variable "secrets" {
default = {
{environment}_{project}_db = "database"
}
type = map(string)
}
Currently I'm calling it in python like this. Would be nice to shorten the list that I need to write.
connection= mysql.connector.connect(host=secrets['{}_{}_host'.format(environment,NAME_OF_PROJECT)],

How to re-use a terraform module in multiple configurations?

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

terraform variable default value interpolation from locals

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.

How to pass down file path to module?

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

How can I get the Terraform module name programmatically?

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.

Resources