Terraform - Variable defined in "*.auto.tfvars" file, but still cannot be discovered - terraform

I have the following directory Structure:
.
├── ./first_terraform.tf
├── ./modules
│   └── ./modules/ssh_keys
│   ├── ./modules/ssh_keys/main.tf
│   ├── ./modules/ssh_keys/outputs.tf
│   └── ./modules/ssh_keys/variables.tf
├── ./terraform.auto.tfvars
├── ./variables.tf
I am trying to pass a variable ssh_key to my child module defined as main.tf inside ./modules/ssh_keys/main.tf
resource "aws_key_pair" "id_rsa_ec2" {
key_name = "id_rsa_ec2"
public_key = file(var.ssh_key)
}
I also have this variable defined both at root and child level variables.tf file. For the value, I have set it in terraform.auto.tfvars as below
# SSH Key
ssh_key = "~/.ssh/haha_key.pub"
I also have a variable defined in root level and child level variables.tf file:
variable "ssh_key" {
type = string
description = "ssh key for EC2 login and checks"
}
My root terraform configuration has this module declared as:
module "ssh_keys" {
source = "./modules/ssh_keys"
}
I first did a terraform init -upgrade on my root level. Then ran terraform refresh and got hit by the following error.
Error: Missing required argument
on first_terraform.tf line 69, in module "ssh_keys":
69: module "ssh_keys" {
The argument "ssh_key" is required, but no definition was found.
Just for reference, line 69 in my root level configuration is where the module declaration has been made. I don't know what I have done wrong here. It seems I have all the variables declared, so am I missing some relationship between root/child module variable passing etc.?
Any help is appreciated! Thanks

I Think I know what I did wrong.
Terraform Modules - as per the documentation requires parents to pass on variables as part of invocation. For example:
module "foo" {
source = "./modules/foo"
var1 = value
var2 = value
}
The above var1, var2 can come from either auto.tfvars file, environment variables (recommended) or even command line -var-file calls. In fact, this is what Terraform calls "Calling a Child Module" here
Once I did that, everything worked like a charm! I hope I did find the correct way of doing things.

Related

Terraform not declaring tfvars

I am new to Terraform and I am writing a script. Following is my directory structure
folder
---.terraform
---..terraform.lock.hcl
---main.tf
---terraform.tfvars
---variables.tf
Following is my content on terraform.tfvars.
environment = "development"
Following is my content on main.tf.
tags = {
environment = var.environment
}
But the values are not updating. Following is the error:
╷
│ Warning: Value for undeclared variable
│
│ The root module does not declare a variable named "environment" but a value was found in file "terraform.tfvars". If you meant to use this value, add a "variable" block to the configuration.
│
│ To silence these warnings, use TF_VAR_... environment variables to provide certain "global" settings to all configurations in your organization. To reduce the verbosity of these warnings, use the
│ -compact-warnings option.
╵
╷
│ Warning: Value for undeclared variable
│
│ The root module does not declare a variable named "admin_username" but a value was found in file "terraform.tfvars". If you meant to use this value, add a "variable" block to the configuration.
│
│ To silence these warnings, use TF_VAR_... environment variables to provide certain "global" settings to all configurations in your organization. To reduce the verbosity of these warnings, use the
│ -compact-warnings option.
╵
╷
│ Warning: Values for undeclared variables
│
│ In addition to the other similar warnings shown, 1 other variable(s) defined without being declared.
╵
╷
│ Error: Reference to undeclared input variable
│
│ on main.tf line 22, in resource "azurerm_resource_group" "tf_example_rg":
│ 22: environment = var.environment
│
│ An input variable with the name "environment" has not been declared. This variable can be declared with a variable "environment" {} block.
As I am using terraform.tfvars I don't need to give the filename on CLI. I think I am doing everything right but it's yet not working.
You have to actually declare your variable using variable block. For example:
variable "environment" {}
If you have such declarations, you have to double check the spelling and locations of them.
#AunZaidi , As stated in the error messages terraform can not find the defined variables.
The root module does not declare a variable named "environment" but a value was found in file "terraform.tfvars". If you meant to use this value, add a "variable" block to the configuration.
I would recommend you to take a look at the terraform-azure-tutorials to get acquainted with the basics.
you can solve your issue by just defining a new variable using syntax
variable "environment" {
type = string
description = "(optional) Environment for the deployment"
}
Refer to https://developer.hashicorp.com/terraform/language/values/variables#arguments for definitions of the arguments used in terraform variables.
Also one of the recommended practices is to use a dedicated file variables.tf for all the variables inputs required in your terraform code.

Terraform module unable to access variables

Terraform newbie would appreciate a push in the right direction.
Have created my first module, a child module to root called 'storage' in there I want to put all the templates for the storage accounts. I thought this would be simple.
I've added a folder named 'storage' and moved the existing 'storage.tf' file into that and added added modules block to my main.tf (in root) to tell it the module exists. I did a terraform init and it found and installed it - great!
But now when I try something like:
terraform plan -var-file='env/dev/dev.tfvars'
None of the variables which my storage.tf file references are found. I just get:
Error: Reference to undeclared input variable
│
│ on modules\storage\storage.tf line 11, in resource "google_storage_bucket" "new_bucket":
│ 11: name = "${var.env_name}-${var.name_of_bucket}-test"
│
│ An input variable with the name "env_name" has not been declared. This variable can be declared with a variable "env_name" {} block.
All my variables are defined in a variables.tf in root and when running terraform I pass in the path to the relevant variable values using the .tfvars method.
Why can't the module see those?
I tried creating a vars.tf in the module directory, next to the storage.tf file. I copied the variable definitions into that file and got errors that the value of the variable was missing (even though the values were in the .tfvars file used in the invocation).
I then tried adding the value to the variable definitions using the 'default' and this actually worked - but that means I can no longer use the .tfvars to control which environment I'm configuring.
I must be missing something really basic!
Question: How can I get my modules to accept the values contained in my .tfvars? I don't mind having the variables which each module needs in a seperate vars.tf for each module - thats fine and actually useful, but there has to be some way to pass in the values with a simple .tfvars? Please... what am I missing?
Update
Below is the folder and file structure, sorry if I am not using the right syntax for the graph!
condo (folder)
|--.git (folder)
|--.github (folder)
|--creds (folder)
| |--dev_creds.json (file)
| |--prod_creds.json (file)
|
|--infracomm (folder) (root)
|
|--.terraform (file)
|--.terraform.hcl.lock (file)
|--env (folder)
| |--dev (folder)
| | |--dev.tfvar (file)
| |
| |--prod (folder)
| |--prod.tfvars (file)
|
|--modules (folder) (child module)
| |--storage (folder)
| |--storage.tf (file)
|
|--backend.tf (file)
|--main.tf (file)
|--variables.tf (file)
All I am trying to do is get the variables in the root variables.tf AND the values for those variables, which I am providing in the cli arg
-var-file='./env/dev/dev.tfvars'
To be usable inside the storage module.
UPDATE #2
Here is the code from main.tf in root where I am referencing the child module. Let me know if more needed:
provider "google" {
project = var.project_name
region = "europe-west4"
zone = "europe-west4"
credentials = var.credentials_path
}
// Define modules
module "storage" {
source = "./modules/storage"
}
Do I need to duplicate those variables in a vars.tf in the module? If so, how do I get values into them without using 'default' as that would break scalability.
I'm sure I'm missing something basic.
Thanks!
Let's say you want to pass the variable variable_to_child_module in your module, from the root module.
The value of this variable was added through a .tfvars file in the root module, namely variable_from_root_module
When you call the storage module from the root module pass the variable
// Define modules
module "storage" {
source = "./modules/storage"
variable_to_child_module = var.variable_from_root_module
}
Also in ./modules/storage directory you should add a variables.tf where you will add the variable definition
E.g.
variable "variable_to_child_module" {
description = "my child module variable"
type = string
}
Consider each module (root and storage) an independent module. You need to (re)define the variables which you use in the sub-module (storage) in the sub-module itself.
Assuming you have variables region and size which you pass into the main module and want to use in storage module, you can do this as follows:
module "storage" {
source = "./modules/storage"
region = var.region
size = var.size
}

Using terragrunt generate provider block causes conflicts with require providers block in module

I'm using Terragrunt with Terraform version 0.14.8.
My project uses mono repo structure as it is a project requirement to package Terragrunt files and Terraform modules together in a single package.
Folder structure:
project root:
├── environments
│   └── prd
│   ├── rds-cluster
│   │   └── terragrunt.hcl
│   └── terragrunt.hcl
└── modules
├── rds-cluster
│   ├── README.md
│   ├── main.tf
│   ├── output.tf
│   └── variables.tf
└── secretsmanager-secret
├── README.md
├── main.tf
├── output.tf
└── variables.tf
In prd/terragrunt.hcl I define the remote state block and the generate provider block.
remote_state {
backend = "s3"
...
}
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = <<EOF
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
provider "aws" {
region = "ca-central-1"
}
EOF
}
In environments/prd/rds-cluster/terragrunt.hcl, I defined the following:
include {
path = find_in_parent_folders()
}
terraform {
source = "../../../modules//rds-cluster"
}
inputs = {
...
}
In modules/rds-cluster/main.tf, I defined the following:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.0"
}
}
}
// RDS related resources...
My problem is that when I try to run terragrunt plan under environments/prd/rds-cluster, I get the following error message:
Error: Duplicate required providers configuration
on provider.tf line 3, in terraform:
3: required_providers {
A module may have only one required providers configuration. The required
providers were previously configured at main.tf:2,3-21.
I can resolve this by declaring the version within the provider block as shown here. However, the version attribute in provider blocks has been deprecated in Terraform 0.13; Terraform recommends the use of the required_providers sub-block under terraform block instead.
Does anyone know what I need to do to use the new required_providers block for my aws provider?
As you've seen, Terraform expects each module to have only one definition of its required providers, which is intended to avoid a situation where it's unclear why Terraform is detecting a particular when the declarations are spread among multiple files.
However, to support this sort of piecemeal code generation use-case Terraform has an advanced feature called Override Files which allows you to explicitly mark certain files for a different mode of processing where they selectively override particular definitions from other files, rather than creating entirely new definitions.
The details of this mechanism depend on which block type you're overriding, but the section on Merging terraform blocks` discusses the behavior relevant to your particular situation:
If the required_providers argument is set, its value is merged on an element-by-element basis, which allows an override block to adjust the constraint for a single provider without affecting the constraints for other providers.
In both the required_version and required_providers settings, each override constraint entirely replaces the constraints for the same component in the original block. If both the base block and the override block both set required_version then the constraints in the base block are entirely ignored.
The practical implication of the above is that if you have an override file with a required_providers block that includes an entry for the AWS provider then Terraform will treat it as a full replacement for any similar entry already present in a non-override file, but it won't affect other provider requirements entries which do not appear in the override file at all.
Putting all of this together, you should be able to get the result you were looking for by asking Terragrunt to name this generated file provider_override.tf instead of just provider.tf, which will then activate the override file processing behavior and thus allow this generated file to override any existing definition of AWS provider requirements, while allowing the configurations to retain any other provider requirements they might also be defining.

Terraform modules: correct references of variables?

I'm writing a terraform script to create an EKS cluster with its worker nodes on AWS. First time doing it so I'm a bit confused.
Here is the folder organisation:
├─── Int AWS Account
│ ├─── variables.tf
│ ├─── eks-cluster.tf (refers the modules)
│ ├─── others
│
├─── Prod AWS Account
│ ├─── (will be the same than Int with different settings in variables)
│
├─── ReadMe.md
│
├─── data sources
│
├─── Modules
│ ├─── cluster.tf
│ ├─── worker-nodes.tf
│ ├─── worker-nodes-sg.tf
I am a bit confused regarding how to use and pass variables. Right now, what I'm doing is that I refer to ${var.name} in the module folder, in the eks-cluster.tf, I either put a direct value name = blabla (mostly avoiding it), or refer to the variable again and have a variable file in the account folder.
Is that correct?
I'm not sure if I get your question correctly but in general you would want to keep your module files with variables only, as modules are intended to be generic so you can easily include them in different environments.
When including the module in eks_cluster_int.tf or eks_cluster_prod.tf you would then pass the values for all variables defined in the module itself. This way you can use the environment specific values in the same module.
module "cluster" {
source = "..."
var1 = value1 # directly passing value
var2 = ${var.int_specific_var} # can be defined in variables.tf of environment
...
}
Does this answer your question?

Trouble setting terraform variable from CLI

My terraform project layout looks something like:
[project root dir]
main.tf
- my_module [directory]:
variables.tf
my_instance.tf
In variables.tf I have something like this:
variable "foo-ami" {
default = "ami-12345"
description = "Super awesome AMI"
}
In my_instance.tf I reference it like so:
resource "aws_instance" "my_instance" {
ami = "${var.foo-ami}"
...
}
If I want to override this variable from the command line, the docs here seem to suggest I should be able to run the following command from the top level (main.tf) location:
terraform plan -var 'foo-ami=ami-987654'
However, the variable switch doesn't seem to be getting picked up. The old value remains set. Further more if I remove the default setting I get an error from terraform saying it's not set, so clearly the -var switch isn't being picked up.
Thoughts?
TIA
The variables of root and my_module are different. If you want to pass a variable to my_module, you need to specify it.
In the main.tf, you should set the variable as follows:
variable "foo-ami" {}
module "my_module" {
source = "./my_module"
foo-ami = "${var.foo-ami}"
...
}
For details of the module feature refer to the following documents:
https://www.terraform.io/docs/modules/usage.html

Resources