Terraform variables - variables

I'm trying to define some variables for aws_key_path and name but I get an error when terraform init
# variables.tf
variable "aws_key_path" {
aws_key_path = "Users/user/Documents/folder/filename.pem"
}
variable "aws_key_name" {
aws_key_name = "filename"
}
# main.tf
module "us-west-1" {
source = "./some_sorce"
aws_key_path = "${var.aws_key_path}"
aws_key_name = "${var.aws_key_name}"
aws_region = "us-west-1"
amis = "${var.amis}"
providers = {
aws.source = "aws.us-west-1"
}
}
get an error when init:
variable[aws_key_path]: invalid key: aws_key_path
what is wrong?
thanks

These are not environment variables.
Terraform variables are defined as follows:
variable "aws_key_name" {
default = "filename"
}

Related

Write a dynamic Terraform block for a load balancer listener rule

I'm new to dynamic blocks and am having some trouble writing rules to listeners on a load balancer that was created using for_each.
Below are the resources I created:
resource "aws_lb_listener" "app_listener_forward" {
for_each = toset(var.app_listener_ports)
load_balancer_arn = aws_lb.app_alb.arn
port = each.value
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS-1-2-Ext-2018-06"
certificate_arn = var.ssl_cert
default_action {
type = "forward"
forward {
dynamic "target_group" {
for_each = aws_lb_target_group.app_tg
content {
arn = target_group.value["arn"]
}
}
stickiness {
enabled = true
duration = 86400
}
}
}
}
resource "aws_lb_listener_rule" "app_https_listener_rule" {
for_each = toset(var.app_listener_ports)
listener_arn = aws_lb_listener.app_listener_forward[each.value].arn
action {
type = "forward"
forward {
dynamic "target_group" {
for_each = aws_lb_target_group.app_tg
content {
arn = target_group.value["arn"]
}
}
}
}
dynamic "condition" {
for_each = var.images
path_pattern {
content {
values = condition.value["paths"]
}
}
}
}
resource "aws_lb_target_group" "app_tg" {
for_each = var.images
name = each.key
port = each.value.port
protocol = "HTTP"
target_type = "ip"
vpc_id = aws_vpc.app_vpc.id
health_check {
interval = 130
timeout = 120
healthy_threshold = 10
unhealthy_threshold = 10
}
stickiness {
type = "lb_cookie"
cookie_duration = 86400
}
}
Below are how the variables are defined:
variable "images" {
type = map(object({
app_port = number
paths = set(string)
}))
{
"app-one" = {
app_port = 3000
paths = [
"/appOne",
"/appOne/*"
]
}
"app-two" = {
app_port = 4000
paths = [
"/appTwo",
"/appTwo/*"
]
}
}
variable "app_listener_ports" {
type = list(string)
default = [
80, 443, 22, 7999, 8999
]
}
Upon executing, I am getting an error dealing with the path_pattern being unexpected:
Error: Unsupported block type
│
│ on alb.tf line 78, in resource "aws_lb_listener_rule" "app_https_listener_rule":
│ 78: path_pattern {
│
│ Blocks of type "path_pattern" are not expected here.
I've tried a few ways to get this dynamic block but am having some difficulty. Any advice would be appreciated.
Thank you!
Try it like this:
dynamic "condition" {
for_each = var.images
content {
path_pattern {
values = condition.value.paths
}
}
}
And change the type of paths from set(string) to list(string).
This is also completely acceptable:
dynamic "condition" {
for_each = var.images
content {
path_pattern {
values = condition.value["paths"]
}
}
}
However, in my opinion here it's better to not use a dynamic block for the condition to maintain readability and maintenance.
condition {
path_pattern {
values = [
"/appOne",
"/appOne/*" ## can also use variables if you prefer !!
]
}
}
I have already answered your original post related to the problem which you had after fixing the dynamic syntax.
Post URL: Error when creating dynamic terraform rule for alb listener rule

terraform variable using block with no argument

I have a sample code below from terraform but I'm having some issues trying to declare a variable that the argument is a block
basic {}
and moving to production will be something like
dedicated {
cku = 2
}
DEV
resource "confluent_kafka_cluster" "basic" {
display_name = "basic_kafka_cluster"
availability = "SINGLE_ZONE"
cloud = "GCP"
region = "us-central1"
basic {} <<<< # I want this block to be declared as variable
# Calling the variable
local.cluster_type["dev"] <<<< # this approach is not supported. how can I call the variable directly if there is no argument?
}
PROD
resource "confluent_kafka_cluster" "dedicated" {
display_name = "dedicated_kafka_cluster"
availability = "MULTI_ZONE"
cloud = "GCP"
region = "us-central1"
# For Production it is using a different block
dedicated {
cku = 2
}
# Calling the variable
local.cluster_type["prod"] <<<<< # this approach is not supported. how can I call the variable directly if there is no argument?
}
Local variables
locals {
cluster_type = {
prod = "dedicated {
cku = 2
}"
dev = "basic {}"
}
}
You have some issues with your script:
confluent_kafka_cluster is deprecated, it should be replaced by confluentcloud_kafka_cluster
To use the environment, you can create confluentcloud_environment:
resource "confluentcloud_environment" "env" {
display_name = var.environment
}
To solve the issue of the block, you can use dynamic with conditions, like this:
dynamic "basic" {
for_each = var.environment == "dev" ? [1] : []
content {}
}
dynamic "dedicated" {
for_each = var.environment == "prod" ? [1] : []
content {
cku = 2
}
}
Your code can be like this:
resource "confluentcloud_environment" "env" {
display_name = var.environment
}
resource "confluentcloud_kafka_cluster" "basic" {
display_name = "basic_kafka_cluster"
availability = "SINGLE_ZONE"
cloud = "GCP"
region = "us-central1"
dynamic "basic" {
for_each = var.environment == "dev" ? [1] : []
content {}
}
dynamic "dedicated" {
for_each = var.environment == "prod" ? [1] : []
content {
cku = 2
}
}
environment {
id = confluentcloud_environment.env.id
}
}
variable "environment" {
default = "dev"
}

Unable to use terraform.tfvars inside terraform module

Facing some issue to use terraform.tfvars inside module.
My folder structure is
module/
main.tf
variable.tf
terraform.tfvars
demo.tf
provider.tf
The code of demo.tf is
module "module" {
source = "./module"
}
Inside the module folder I have decleared variables inside variable.tf and put their values inside terraform.tfvars.
when I run terraform plan then it is showing
Error: Missing required argument
on main.tf line 1, in module "module":
1: module "module" {
The argument "<variable_name>" is required, but no definition was found.
Please let me know the solution, Thanks in advance.
( When I put values as default inside variables.tf then it is working file.)
For more details, I am adding all files below -
main.tf
resource "aws_glue_catalog_database" "glue_database_demo" {
name = var.database_name # var
location_uri = "s3://${var.bucket_location}" # var
}
resource "aws_glue_catalog_table" "aws_glue_catalog_table" {
name = var.table_name # var
database_name = aws_glue_catalog_database.glue_database_demo.name
table_type = "EXTERNAL_TABLE"
parameters = {
EXTERNAL = "TRUE"
"parquet.compression" = "SNAPPY"
}
storage_descriptor {
location = "s3://${var.bucket_location}" # var
input_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat"
ser_de_info {
name = "my-stream"
serialization_library = "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe"
}
columns {
name = "filekey"
type = "string"
}
columns {
name = "reead_dt"
type = "string"
}
}
partition_keys {
name = "load_dt"
type = "string"
}
}
variables.tf
variable "database_name" {
}
variable "bucket_location" {
}
variable "table_name" {
}
terraform.tfvars
database_name = "mydatabase"
bucket_location = "kgvjgfkjhglbg"
table_name = "mytable"
This is not how modules work. If you define a variable, it is expecting you to provide a value when you are calling it unless it has a default value defined as you have already noted. In order for this to work, you would have to provide values when calling the module:
module "modules" {
source = "./module"
database_name = "mydatabase"
bucket_location = "kgvjgfkjhglbg"
table_name = "mytable"
}
The other option would be to define a variables.tf file in the same directory where you are calling the module from, e.g.,:
# provide input for the module
variable "database_name" {
type = string
description = "Glue DB name."
}
variable "bucket_location" {
type = string
description = "Bucket region."
}
variable "table_name" {
type = string
description = "Glue catalog table name."
}
Then, copy the terraform.tfvars to the same directory where you are calling the module from and in the demo.tf do the following:
module "glue" {
source = "./module"
database_name = var.database_name
bucket_location = var.bucket_location
table_name = var.table_name
}
Note that I have changed the logical name of the module from modules to glue as it is more descriptive, but it's not necessary.
The final outlook of the directories should be:
module/
main.tf
variables.tf
demo.tf
provider.tf
terraform.tfvars
variables.tf
Within your demo.tf file, in the "modules" module you need to provide the input variables value.
For example:
module "modules" {
source = "./module"
database_name = var.database_name
bucket_location = var.bucket_location
table_name = var.table_name
}

How to pass/concat variable into the `data.aws_ami` section in the `aws_instance` resource

I am having difficulties with defining a variable inside the ami=data.aws_ami.$var.ami_name.id line.
I have tried ami= "${data.aws_ami.(var.ami_name).id}"but in both cases I am getting the:
79: ami = data.aws_ami.(var.ami_name)).id
│
│ An attribute name is required after a dot.
It only works with the string value data.aws_ami.ubuntu-1804.id.
My question is how to concat the variable to the data.aws_ami?
The end goal is to provision based on different OS ec2 instances (Suse,Ubuntu,RHEL) All depending on the variable provided when deploying it.
variable "ami_name" {
default = "ubuntu"
}
data "aws_ami" "amazon" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm*"]
}
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "linux" {
key_name = var.ami_key_pair_name
//ami = var.ami_id
//I want this to be dynamic so I can deploy either Amazon or Ubuntu in all regions.
ami = data.aws_ami.$var.ami_name.id
//ami = data.aws_ami.ubuntu.id # this works
instance_type = "t2.micro"
tags = {
Name = var.instance_name
}
vpc_security_group_ids = [
aws_security_group.allow-ssh-http.id
]
}
I did the search but could not find anything related. I am using Terraform v0.15.4
The code you show: data.aws_ami.$var.ami_name.id that is not valid terraform syntax.
Here is a possibility for what you are asking:
provider "aws" { region = "us-east-2" }
locals {
allowed_os = {
"amazon": {owner: "amazon", filter: "amzn2-ami-hvm*"},
"suse": {owner: "amazon", filter: "*suse*"},
"RHEL": {owner: "amazon", filter: "*RHEL*"},
"ubuntu": {owner: "099720109477", filter: "*ubuntu-bionic-18.04-amd64-*"},
}
}
variable "ami_name" {
default = "ubuntu"
validation {
condition = can(regex("amazon|suse|RHEL|ubuntu", var.ami_name))
error_message = "Invalid ami name, allowed_values = [amazon suse RHEL ubuntu]."
}
}
data "aws_ami" "os" {
for_each = local.allowed_os
most_recent = true
owners = [each.value.owner]
filter {
name = "name"
values = [each.value.filter]
}
}
resource "aws_instance" "linux" {
ami = data.aws_ami.os[var.ami_name].id
instance_type = "t2.micro"
# ... todo add arguments here
}
My approach here is to use a for_each in the aws_ami, that will give us an array, we can consume that later in the aws_instance resource:
data.aws_ami.os["ubuntu"].id
Here we use a hardcoded value to access a specific AMI in your code.
data.aws_ami.os[var.ami_name].id
Or this way with the variable that will be provided by user or a config file.
You can add more items to the array to add other operating systems, and same with the filters, you can just change the allowed_os local variable to suit your needs.
As an extra, I added validation to your ami_name variable to match the allowed different OS we use in the for_each, that way we prevent any issues right before they can cause errors.

Terraform AWS optional logging for S3 bucket

I am trying to create S3 bucket using terraform from examples in the link
https://www.terraform.io/docs/providers/aws/r/s3_bucket.html
I have created a S3 module.
The issue i am facing is, for certain bucket i do not want logging enabled.
How can this be accomplished in terraform.
logging {
target_bucket = "${aws_s3_bucket.log_bucket.id}"
target_prefix = "log/"
}
Using empty string for target_bucket and target_prefix causes terraform to make an attempt to create target_bucket.
Also, i am trying to use a module.
Using the newer dynamic block support in terraform 0.12+ we pass a single-item array containing the logging settings if we want logging like so:
variable "logging" {
type = list
default = []
description = "to enable logging set this to [{target_bucket = 'xxx' target_prefix = 'logs/'}]"
}
resource "aws_s3_bucket" "s3bucket" {
dynamic "logging" {
for_each = [for l in var.logging : {
target_bucket = l.target_bucket
target_prefix = l.target_prefix
}]
content {
target_bucket = logging.value.target_bucket
target_prefix = logging.value.target_prefix
}
}
}
Can Fly.
If you want to make the values of logging optional, first make your module aws_s3_bucket.tf:
resource "aws_s3_bucket" "b" {
bucket = "my-tf-test-bucket"
acl = "private"
logging = "${var.logging}"
}
variable "logging" {
type = "list"
default = []
}
then in a sub-folder example add your template module.tf:
module "s3" {
source = "../"
logging = [
{
target_bucket = "loggingbucketname"
target_prefix = "log/"
},
]
}
provider "aws" {
region = "eu-west-1"
version = "2.4.0"
}
This is your version that has logging.
Next modify your module.tf to look like
module "s3" {
source = "../"
}
provider "aws" {
region = "eu-west-1"
version = "2.4.0"
}
That's your version without. This worked with:
Terraform v0.11.11
+ provider.aws v2.4.0
Updated
This is answer for v0.12.5.
module is now:
resource "aws_s3_bucket" "b" {
bucket = "my-tf-test-bucket"
acl = "private"
logging {
target_bucket = var.logging["target_bucket"]
target_prefix = var.logging["target_prefix"]
}
}
variable "logging" {
type=map
default={
target_bucket = ""
target_prefix = ""
}
}
Use module with logging becomes (your path to modules might differ):
module "s3" {
source = "../"
logging={
target_bucket = aws_s3_bucket.log_bucket.id
target_prefix = "log/"
}
}
provider "aws" {
region = "eu-west-1"
version = "2.34.0"
}
resource "aws_s3_bucket" "log_bucket" {
bucket = "my-tf-log-bucket"
acl = "private"
}
and without:
module "s3" {
source = "../"
}
provider "aws" {
region = "eu-west-1"
version = "2.34.0"
}