Terraform not passing variable to module - terraform

I'm trying to pass a variable from an environment dir to a module, but having some issues. My directory structure looks like this
repository
-> prod
-> test
main.tf
terraform.tf
vars.tfvars
-> modules
infra
main.tf
terraform.tf
In test/main.tf I have
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
provider "aws" {
region = "eu-west-1"
}
module "launch" {
source = "../../modules/infra"
range = var.range
}
test/terraform.tf looks like this
variable "range" {}
test/vars.tfvars
range="10.0.0.0/16"
modules/infra/main.tf
resource "aws_vpc" "testvpc" {
cidr_block = var.range
}
When I run this I get the prompt
var.range
Enter a value:
I'm expecting it to pick up the value from the variable automatically, but even when I do enter the value I get the error
│ Error: Unsupported argument
on main.tf line 20, in module "launch":
range = var.range
An argument named "range" is not expected here
Is it possible to pass a variable from a file for a given environment to a module?

Make sure the file modules/infra/terraform.tf contains the variable:
variable "range" {}

Related

Terraform Cloud Run Service URL

I create a cloud run service like so
terraform {
required_version = ">= 1.1.2"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.1.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 4.2.0"
}
}
}
provider "google" {
project = "main_project"
region = "us-central-1"
credentials = "<my-key-path>"
}
resource "google_cloud_run_service" "default" {
name = "cloudrun-srv"
location = "us-central1"
template {
spec {
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
I want to save the value of the service url that is created - https://default-hml2qtrgfq-uw.a.run.app using an output variable. something like
output "cloud_run_instance_url" {
value = google_cloud_run_service.default.url
}
This gives me an error:
terraform plan
╷
│ Error: Unsupported attribute
│
│ on main.tf line 40, in output "cloud_run_instance_url":
│ 40: value = google_cloud_run_service.default.url
│
│ This object has no argument, nested block, or exported attribute named "url".
╵
How do I get this output value and assign it to a variable so that other services like cloud scheduler can point to it?
If you declare an output for the url resource attribute like:
output "cloud_run_instance_url" {
value = google_cloud_run_service.default.status.0.url
}
then it will be available for resolution (such as for inputs to other modules) at the scope where the module is declared in the namespace module.<declared module name>.cloud_run_instance_url. For example, if this module is declared in the root module config, then it can be resolved at that namespace elsewhere in the root module config.

Terraform interactive input doesn't work with the root module

Terraform Version
Terraform v1.1.2
on windows_amd64
Terraform Configuration Files
child_module1.tf(C1):
# Create Resource Group
resource "aws_resourcegroups_group" "resourcegroups_group" {
name = "test"
resource_query {
query = <<JSON
{
"ResourceTypeFilters": [
"AWS::EC2::Instance"
],
"TagFilters": [
{
"Key": "project",
"Values": ["${var.ProjectName}"]
}
]
}
JSON
}
}
child_module1_variables.tf:
########
variable "ProjectName" {
type = string
description = "This name would be prefixed with the cluster names."
}
Now call this child module in another child module**(C2)**:
child_module2.tf:
module "prepare_aws_cloud" {
source = "./modules/aws/prepare_cloud_copy"
ProjectName = "${var.test.ProjectName}"
}
child_module2_variables.tf:
variable "test" {
type = object({
ProjectName = string
})
}
Now I create a root module(R1)** which calls the child_module2.tf:**
terraform {
backend "local" {
}
}
module "test_deploy" {
source = "D:\\REPO\\installer_v2.2.22.1\\installer\\aws"
test = {
#ProjectName = ""
}
}
So the dependency is as follows:
R1 calls >> C2 calls >> C1
ERROR
PS D:\tkgTest> terraform apply -input=true
╷
│ Error: Invalid value for module argument
│
│ on testing.tf line 21, in module "test_deploy":
│ 21: test= {
│ 22: #ProjectName = ""
│ 23: }
│
│ The given value is not suitable for child module variable "test" defined at .terraform\modules\test_deploy\variables.tf:108,1-15: attribute "ProjectName" is required.
Expected Behavior
I would have expected that the user input would be taken interactively by the console as I am passing the -input=true flag but it doesn't seem to work.
The interactive prompts for input variables are intended only to help with getting started with Terraform (e.g. following a simple tutorial) and so they are limited in the scope of what they support. The typical way to set root module input variables for routine use is to either create a .tfvars file and pass it to Terraform with -var-file or to set a variable directly using -var.
Note also that only root module input variables can be set directly as part of the planning options. Any child module variables are defined exclusively by the expressions written in the module block, and so if you want to be able to set a child module's input variable on a per-run basis you'll need to also declare it as a root module variable and then pass it through to the child module.
For example, in the root module:
variable "test" {
type = object({
ProjectName = string
})
}
module "test_deploy" {
source = "./installer/aws"
test = var.test
}
You can then create an example.tfvars file with the following to set a value for the variable:
test = {
ProjectName = "example"
}
Specify that file when you run Terraform:
terraform apply -var-file=example.tfvars
If you will always set the same values then you can avoid the need for the extra option by naming your file example.auto.tfvars and placing it in the same directory where you will run Terraform. Terraform will load .auto.tfvars files automatically without any explicit -var-file option.

Terraform output defined are empty [duplicate]

I'm trying to setup some IaC for a new project using Hashicorp Terraform on AWS. I'm using modules because I want to be able to reuse stuff across multiple environments (staging, prod, dev, etc.)
I'm struggling to understand where I have to set an output variable within a module, and how I then use that in another module. Any pointers to this would be greatly appreciated!
I need to use some things created in my VPC module (subnet IDs) when creating EC2 machines. My understanding is that you can't reference something from one module in another, so I am trying to use an output variable from the VPC module.
I have the following in my site main.tf
module "myapp-vpc" {
source = "dev/vpc"
aws_region = "${var.aws_region}"
}
module "myapp-ec2" {
source = "dev/ec2"
aws_region = "${var.aws_region}"
subnet_id = "${module.vpc.subnetid"}
}
dev/vpc simply sets some values and uses my vpc module:
module "vpc" {
source = "../../modules/vpc"
aws_region = "${var.aws_region}"
vpc-cidr = "10.1.0.0/16"
public-subnet-cidr = "10.1.1.0/24"
private-subnet-cidr = "10.1.2.0/24"
}
In my vpc main.tf, I have the following at the very end, after the aws_vpc and aws_subnet resources (showing subnet resource):
resource "aws_subnet" "public" {
vpc_id = "${aws_vpc.main.id}"
map_public_ip_on_launch = true
availability_zone = "${var.aws_region}a"
cidr_block = "${var.public-subnet-cidr}"
}
output "subnetid" {
value = "${aws_subnet.public.id}"
}
When I run terraform plan I get the following error message:
Error: module 'vpc': "subnetid" is not a valid output for module "vpc"
Outputs need to be passed up through each module explicitly each time.
For example if you wanted to output a variable to the screen from a module nested below another module you would need something like this:
child-module.tf
output "child_foo" {
value = "foobar"
}
parent-module.tf
module "child" {
source = "path/to/child"
}
output "parent_foo" {
value = "${module.child.child_foo}"
}
main.tf
module "parent" {
source = "path/to/parent"
}
output "main_foo" {
value = "${module.parent.parent_foo}"
}

Invoking value from remote state

I have a module cluster that spins up a GKE cluster and associated GCS buckets.
The backend for this is a GCS bucket called cluster_tf_state_bucket
I have defined an output in this module
output "vault_storage_bucket_name" {
value = "${google_storage_bucket.vault_storage.name}"
}
Once I run the code, I get the output
✗ terraform output
vault_storage_bucket_name = vault-storage
Now I have a another module vault and in that module I need to invoke the value of vault_storage_bucket_name from the other module that is using a different GCS backend bucket.
So in the main.tf of my vault module, Im doing something like
terraform {
required_version = ">= 0.12.2"
}
terraform {
backend "gcs" {
bucket = "app_tf_state_bucket"
prefix = "vault"
}
}
data "terraform_remote_state" "cluster_vault" {
backend = "gcs"
config = {
bucket = "cluster_tf_state_bucket"
}
}
module "vault" {
source = "../../../modules/vault"
env = "test"
region = "europe-west5"
storage_bucket_name = "${data.terraform_remote_state.cluster_vault.vault_storage_bucket_name}"
But when I run this code, I get
Error: Unsupported argument
on main.tf line 24, in module "vault":
24: storage_bucket_name = "${data.terraform_remote_state.cluster_vault.vault_storage_bucket_name}"
An argument named "storage_bucket_name" is not expected here.
I resolved the above issue by adding the below to the `variables.tf file
variable "storage_bucket_name"{
type = "string"
}
And now I run into the error
Error: Unsupported attribute
on main.tf line 24, in module "vault":
24: storage_bucket_name = "${data.terraform_remote_state.cluster_vault.vault_storage_bucket_name}"
This object has no argument, nested block, or exported attribute named
"vault_storage_bucket_name".
What am I missing here ?
The output values from the upstream state snapshot are exposed on terraform_remote_state via an attribute called outputs, so you need to include that in your expression:
storage_bucket_name = data.terraform_remote_state.cluster_vault.outputs.vault_storage_bucket_name
Note the extra .outputs before .vault_storage_bucket_name. That outputs attribute is a map value, so you can potentially also use the map as a whole in expressions. For example, you might want to create a Local Value if you expect to be referring to those remote attributes a lot and want to simplify the references:
locals {
vault_cluster = data.terraform_remote_state.cluster_vault.outputs
}
module "vault" {
source = "../../../modules/vault"
env = "test"
region = "europe-west5"
storage_bucket_name = local.vault_cluster.vault_storage_bucket_name
}

Values to variables mentioned in variables.tf in Terraform through environment variables file .tfvars

How do I pass the values of variables mentioned in variable.tf of type "list" or "map" . Are there any mistakes in the syntax in the input.tfvars file mentioned below?
The goal is not to hard code any values in the variables.tf or the main.tf file. In the Terraform.io docs I verified that the values can be provided in this format.
This is from the site:
Lists are defined either explicitly or implicitly:
# implicitly by using brackets [...]
variable "cidrs" { default = [] }
# explicitly
variable "cidrs" { type = "list" }
You can specify lists in a the terraform.tfvars file:
cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ]
Now, when I try to do
terraform plan -var-file=input.tfvars
it is unable to read the variable values from the tfvars file and presents the following error:
Error: module root:
module vpc: required variable "vpccidr" not set
module vpc: required variable "vpcname" not set
The variables.tf file looks like:
variable "vpccidr" { type = "list"}
variable "vpcname" { type = "list" }
The input.tfvars file looks like:
vpccidr=[ "10.1.0.0/16", "10.2.0.0/16", "10.3.0.0/16" ]
vpcname=[ "vpc1", "vpc2", "vpc3" ]
The main.tf file looks like:
module "vpc"{
source = "modules/network/vpc"
}
The VPC module under modules/network has the following configuration file main.tf and the variable file mentioned above in variables.tf:
resource "aws_vpc" "customVpc" {
count = "${length(var.vpccidr)}"
cidr_block = "${element(var.vpccidr, count.index)}"
tags {
count = "${length(var.vpcname)}"
Name = "${element(var.vpcname, count.index)}"
}
}
You need to pass the variables to your module, i.e
module "vpc"{
source = "modules/network/vpc"
vpccidr = "${var.vpccidr}"
vpcname = "${var.vpcname}"
}

Resources