How to enable terraform module only with a specific workspace - amazon-web-services

I define a provider containing modules.
I can call terraform apply at the provider level with two different workspace:
workspace list
default
wk-p1
* wk-p2
I would like the following module only to be launch when I use the workspace wk-1:
module "sync" {
source = "./../test-modules/sync"
workspace="${local.workspace}"
entity = "${local.entity}"
}
I would like something like this:
module "sync" {
if ("${local.workspace}" == "wk-p2") {
source = "./../test-modules/sync"
workspace="${local.workspace}"
entity = "${local.entity}"
}
}
Do you have any idea?

For this you could use the terraform.workspace variable in conjuction with the count directive
count = (terraform.workspace == "wk-p2") ? 1 : 0
This way when the workspace is wk-p2, terraform will create an instance of the module.
On the other hand if it evaluates to false it will create 0 instances of the modules, meaning it will not create anything

Maybe try with "count". Something like:
count = terraform.workspace == "default" ? 1 : 0

Related

Terraform - ensure value is set depending on if another value is also set

I'd like to enforce that a value is set rather than using the default "" if one of the other values is a certain string.
For example I have:
module "test_beanstalk" {
tier = "Worker"
queue = "myQueue"
///
}
in this, when tier is set to worker I'd like to enforce that queue is also set. In the above example there's a scenario where the queue can be omitted resulting in aws spawning a generic one rather than using the queue that is required for that particular application.
Such feature is not directly supported in TF. But you can force TF to error out using locals and some condition that will simply lead to error if your verification fails. For example, in your test_beanstalk you can have:
variable "tier" {
default = "Worker"
}
variable "queue" {
default = ""
}
locals {
if_queue_given = var.tier == "Worker" && var.queue == "" ? tonumber("queue can't be empty") : 1
}
The tonumber("queue can't be empty") will be executed and will lead to TF error, if the condition var.tier == "Worker" && var.queue == "" is true.

Count = true. Works in terraform 11 but not terrafom 12

I'm upgrading to terraform 12 and facing a few issues. We have an autoscaling module (non-root) that calls another module in a central repository (root).
So this module;
module "cef_fleet" {
source = "git::ssh://git#github.com/asg-repo.git?ref=terraform12"
instance_type = var.instance_type
ami = var.ami
etc ...
calls the repository "asg-repo" and in here some resources have a count function such as;
resource "aws_autoscaling_schedule" "schedule_stop" {
count = var.create_resource * var.auto_stop
These two variables in the central repository are both set to 'true'. This works with terraform 11 but when I upgraded to 12 I now get the error;
var.create_resource is true
Unsuitable value for left operand: number required.
Is the way to fix this, to simply put replace the true values with 1? Or should it be something like;
count = signum(count = var.create_resource * var.auto_start) - where both are also 1?
Use a ternary operator:
count = var.create_resource && var.auto_start ? 1 : 0

How can I apply a module to two inputs within the same main.tf?

Currently I am learning terraform, particularly about modules. I have built a module which takes an S3 bucket's arn, applies and creates some policies, and then outputs an arn that will be used by other buckets.
When I apply the module like:
module "meow" {
source = "github.com/terraform-modules//woof?ref=00000000000000"
s3_buckets_arn = aws_s3_bucket.woof_bar.arn
}
The module seems to work.
However, I would like to apply the module to two s3_buckets_arn's. If I reuse the same module in main.tf I will get an error.
I have also tried looping like:
locals {
stuff = {
name1 = aws_s3_bucket.foo.arn
name2 = aws_s3_bucket.bar.arn
}
}
module "meow" {
for_each = local.stuff
source = "github.com/terraform-modules//woof?ref=00000000000000"
s3_buckets_arn = each.value
}
But when I plan I get strange naming for resources like:
module.meow["name2"].bar.foo will be created
And the arn of the bucket does not seem to be outputted.
How can I apply module to more than one input within the same main.tf? And is it possible to do that with a loop?
For example, I want to be able to do something like:
module "meow" {
source = "github.com/terraform-modules//woof?ref=00000000000000"
s3_buckets_arn = aws_s3_bucket.foo_one.arn
}
module "meow" {
source = "github.com/terraform-modules//woof?ref=00000000000000"
s3_buckets_arn = aws_s3_bucket.foo_two.arn
}
(Note: I am using terraform 0.13)

Terraform: put variables in tfvar file not work

I defined a variable map my_role in terraform and set its value in abc.tfvar file as follows. if I assign account id as actual value, it works, if I set account id as a variable, it does not work. Does it mean tfvar file only allow actual value, not variable? By the way, I use terraform workspace. Therefore my_role is different based on workspace I select.
The following works:
my_role = {
dev = "arn:aws:iam::123456789012:role/myRole"
test = ...
prod = ...
}
The following does not work:
my_role = {
dev = "arn:aws:iam::${lookup(var.aws_account_id, terraform.workspace)}:role/myRole"
test = ...
prod = ...
}
The following does not work either:
lambdarole = {
dev = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/myRole"
test = ...
prod = ...
}
does
Have you tried following the example on Input Variables?
You can define your abc.tfvars file with:
variable "my_role" {
type = "map"
default = {
"dev" = "arn:aws:iam::123456789012:role/myRole"
"test" = "..."
"prod" = "..."
}
}
And access it with "${lookup(var.my_role, terraform.workspace)}".
Also, according to the from a file, the variables defined in .tfvars files are automatically loaded if you name the file terraform.tfvars, if not, you have to pass as an argument with -var-file=...
Cannot test it right now, but probably is something in this way.
I am replying when terraform 0.12 version is latest one. Solution is simple, you can create one file say vars.tf and declare variables in it.
Example - variable "xyz" {}
Now create terraform.tfvars and initialize it.
Example - xyz="abcd"
No need to pass any runtime args, it will be picked directly.
Terraform has aws_caller_identity data source. You do not need to mention or hand code account id anywhere. It can be fetched using this source.
In any of your .tf file, just include this source and then you can fetch relevant argument value.
This is how you can do it. Define this in any *.tf file
data "aws_caller_identity" "current" {}
Now where ever you want the value of arn or account id, it can be fetched using :
For account id(For terraform0.12):
data.aws_caller_identity.current.account_id
In your case, it would be like this :
dev = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/myRole"
But in order to this work, you need to define data source like shown above in any *.tf file.
For more help, refer following:
URL : https://www.terraform.io/docs/providers/aws/d/caller_identity.html

How to create an RDS instance from the most recent snapshot or from scratch

In terraform, is there a way to conditionally create an RDS instance from the most recent snapshot of a given database or to create an empty database depending on the value of a parameter?
I tried something like that:
variable "db_snapshot_source" {
default = ""
}
data "aws_db_snapshot" "last_snap" {
count = "${var.db_snapshot_source == "" ? 0 : 1}"
most_recent = true
db_instance_identifier = "${var.db_snapshot_source}"
}
resource "aws_db_instance" "db" {
[...]
snapshot_identifier = "${var.db_snapshot_source == "" ? "" : data.aws_db_snapshot.last_snap.db_snapshot_identifier}"
}
Unfortunately, it does not work because TF seems to dereference data.aws_db_snapshot.last_snap even if the ternary is false. I get the following error message: * aws_db_instance.db: Resource 'data.aws_db_snapshot.last_snap' not found for variable 'data.aws_db_snapshot.last_snap.db_snapshot_identifier'.
How can I achieve a such behaviour? The only option I see is to declare two aws_db_instance resources each with opposed count which is horrifying.
By defining a count you are saying the result of the data resource will be a list even if it is a zero value.
resource "aws_db_instance" "db" {
[...]
snapshot_identifier = "${
var.db_snapshot_source == "" ? "" :
element(
concat(data.aws_db_snapshot.last_snap.*.db_snapshot_identifier, list("")), 0)
}"
}
The concat is required if you expect the list to be empty. Otherwise you get an error
element: element() may not be used with an empty list...
Github issue describing the concat behaviour
The documentation reads as though specifying snapshot_identifier is what triggers using a snapshot or not, so passing in an empty string is not enough to avoid starting from a snapshot. In that case, you would need two aws_rds_instance resources, and then have ternary expressions for count on each resource to decide which one to create. As you mentioned, this is horrifying, but it might work ok.
Another way to think about it is if you had a blank snapshot in your inventory to start from. Then it's just a ternary operator away from deciding to use the custom snapshot or this blank snapshot. I don't know that you can create a blank snapshot in Terraform though, it's creation might be out of band.