terraform 12 count date_template not working - amazon-web-services

I'm upgrading to terraform 12 and have an asg module that references a root repository. As part of this, it uses a data.template_file resource to attach user data to the asg which is then put in to log files on the instances. The module looks as follows;
module "cef_fleet" {
source = "git::ssh://git#github.com/asg-repo.git?ref=terraform12"
user_data_rendered = data.template_file.init.rendered
instance_type = var.instance_type
ami = var.ami
etc ...
which as you can see calls the data resource;
data "template_file" "init" {
count = signum(var.cluster_size_max)
template = file("${path.module}/template-files/init.sh")
vars = {
account_name = var.account_name
aws_account_number = var.aws_account_number
this works fine in terraform 11 but when I changed to terraoform 12 and try to apply I get this error:
*Because data.template_file.init has "count" set, its attributes must be
accessed on specific instances.
For example, to correlate with indices of a referring resource, use:
data.template_file.init[count.index]*
if I change this in my module to user_data_rendered = data.template_file.init[count.index]
I then get this error;
The "count" object can be used only in "resource" and "data" blocks, and only
when the "count" argument is set.
I don't know what to do here. If I leave the .rendered in then it doesn't seem to recognise the [count.index] as I get the first error again. Has anyone any advice on what I need to do?

Related

GCP Terraform resource policy in compute module issues

I am trying to add a start-stop schedule to our vm instances in our cloud repository (it is a terraform/terragrunt setup)
The example presented on the official site is this:
So since we use Terragrunt as a wrapper my module looks like this:
And for reference my variable block is this:
When i push the code it errors on step 0 in CloudBuild with the following error:
Error: Reference to undeclared input variable on main.tf line 116, in resource "google_compute_resource_policy" "hourly": 116: time_zone = var.time_zone
An input variable with the name "time_zone" has not been declared. This variable can be declared with a variable "time_zone" {}block.
I have tried placing this variable in different positions of the block but i keep getting the same error. Has anyone got any ideas?
This is now resolved. I want to thank #kornshell93 for pointing me in the right direction.
I ended up using the block as suggested but creating a new module and hitting that from a separate section within my vm instance block. I linked to the project as a dependency this way. The previous method via the main compute instance module kept failing on all other vm instances, almost like it was expecting this block on all of them.
resource "google_compute_resource_policy" "hourly" {
name = var.instance_schedule_policy.name
region = var.region
project = var.project
description = "Start and stop instances"
instance_schedule_policy {
vm_start_schedule {
schedule = var.instance_schedule_policy.vm_start_schedule
}
vm_stop_schedule {
schedule = var.instance_schedule_policy.vm_stop_schedule
}
time_zone = var.instance_schedule_policy.time_zone
}
}
And the vm instance block
inputs = {
#instance start/stop schedules
project = dependency.project.outputs.project_id
region = "europe-west2"
instance_schedule_policy = {
name = "start-stop"
vm_start_schedule = "30 07 * * *"
vm_stop_schedule = "00 18 * * *"
time_zone = "GMT"
}
}

Terraform - How to output input variable?

I have a custom terraform module which create an AWS EC2 instance, so it's relying on the aws provider.
This terraform custom module is used as a base to describes the instance i want to create, but I also need some other information that will be reused later.
For example, i want to define a description to the VM as an input variable, but i don't need to use it at all to create my vm with the aws provider.
I just want this input variable to be sent directly as an output so it can be re-used later once terraform has done its job.
ex
What I have as input variable
variable "description" {
type = string
description = "Description of the instance"
}
what I wanna put as output variable
output "description" {
value = module.ec2_instance.description
}
What my main module is doing
module "ec2_instance" {
source = "./modules/aws_ec2"
ami_id = var.ami_id
instance_name = var.hostname
disk_size = var.disk_size
create_disk = var.create_disk
availability_zone = var.availability_zone
disk_type = var.disk_type
// I don't need the description variable for the module to work, and I don't wanna do anything with it here, i need it later as output
}
I feel stupid because i searched the web for an answer and can't find anything to do that.
Can you help ?
Thanks
EDIT: Added example of code
If you have an input variable declared like this:
variable "description" {
type = string
}
...then you can return its value as an output value like this, in the same module where you declared it:
output "description" {
value = var.description
}

Terraform Module dependency not working ( version 0.12)

i am trying to pass one output value from one terraform module to another terraform module, but facing below issue
my use case is this, in first module i am creating one IAM role and in the second module i need to use above created IAM role (also, in second module if role is not getting created in first module it will create role itself in second module, please consider it as requirement)
module "createiamrole"{
source = "./modules/createiamrole"
}
// this module creates new role, if role is not supplied from above module (default value of iam_role is "" set in variables.tf).
module "checkiamrole"{
source = "./modules/checkiamrole"
iam_role_depends_on = module.createiamrole.iam_role_name
iam_role = "${module.createiamrole.iam_role_name}"
}
outputs.tf for stroing iam_role_name from first module
output "iam_role_name" {
description = "name for IAM role"
value = aws_iam_role.createiamrole[0].name
}
resource code of module checkiamrole for which i am getting error
resource "aws_iam_role" "newrole" {
count = var.iam_role == "" ? 1 : 0
name = "my-new-iamrole"
assume_role_policy = data.aws_iam_policy_document.iampolicy[0].json
tags = var.tags
depends_on = [var.iam_role_depends_on]
}
Error
Invalid count argument
count = var.iam_role == "" ? 1 : 0
The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.
My Query is how to implement module dependency as well how to pass one output value from dependent module to required module
Your count, as the error message says, can't depend on any other resources. The condition for the count must be know before you run your code. So you have to create some new variable, e.g. var.create_role which you specify during your apply. Based on this value, the modules will create or not create the role in question.
Other alternative, again as the error message says, is to first deploy module1, and then module2.

Preventing destroy of resources when refactoring Terraform to use indices

When I was just starting to use Terraform, I more or less naively declared resources individually, like this:
resource "aws_cloudwatch_log_group" "image1_log" {
name = "${var.image1}-log-group"
tags = module.tagging.tags
}
resource "aws_cloudwatch_log_group" "image2_log" {
name = "${var.image2}-log-group"
tags = module.tagging.tags
}
resource "aws_cloudwatch_log_stream" "image1_stream" {
name = "${var.image1}-log-stream"
log_group_name = aws_cloudwatch_log_group.image1_log.name
}
resource "aws_cloudwatch_log_stream" "image2_stream" {
name = "${var.image2}-log-stream"
log_group_name = aws_cloudwatch_log_group.image2_log.name
}
Then, 10-20 different log groups later, I realized this wasn't going to work well as infrastructure grew. I decided to define a variable list:
variable "image_names" {
type = list(string)
default = [
"image1",
"image2"
]
}
Then I replaced the resources using indices:
resource "aws_cloudwatch_log_group" "service-log-groups" {
name = "${element(var.image_names, count.index)}-log-group"
count = length(var.image_names)
tags = module.tagging.tags
}
resource "aws_cloudwatch_log_stream" "service-log-streams" {
name = "${element(var.image_names, count.index)}-log-stream"
log_group_name = aws_cloudwatch_log_group.service-log-groups[count.index].name
count = length(var.image_names)
}
The problem here is that when I run terraform apply, I get 4 resources to add, 4 resources to destroy. I tested this with an old log group, and saw that all my logs were wiped (obviously, since the log was destroyed).
The names and other attributes of the log groups/streams are identical- I'm simply refactoring the infrastructure code to be more maintainable. How can I maintain my existing log groups without deleting them yet still refactor my code to use lists?
You'll need to move the existing resources within the Terraform state.
Try running terraform show to get the strings under which the resources are stored, this will be something like [module.xyz.]aws_cloudwatch_log_group.image1_log ...
You can move it with terraform state mv [module.xyz.]aws_cloudwatch_log_group.image1_log '[module.xyz.]aws_cloudwatch_log_group.service-log-groups[0]'.
You can choose which index to assign to each resource by changing [0] accordingly.
Delete the old resource definition for each moved resource, as Terraform would otherwise try to create a new group/stream.
Try it with the first import and check with terraform plan if the resource was moved correctly...
Also check if you need to choose some index for the image_names list jsut to be sure, but I think that won't be necessary.

Terraform: order of creating array of resources

I have two ec2 instances defined in terraform using the count method.
resource "aws_instance" "example" {
count = "2"
ami = "ami-2d39803a"
instance_type = "t2.micro"
tags {
Name = "example-${count.index}"
}
}
How can I enforce that they are launched one after the other? e.g. the second instance should be created when the first one finishes.
Attempt 1:
depends_on = [aws_instance.example[0]]
result:
Error: aws_instance.example: resource depends on non-existent resource 'aws_instance.example[0]'
Attempt 2:
tags {
Name = "example-${count.index}"
Active = "${count.index == "1" ? "${aws_instance.example.1.arn}" : "this"}"
}
result:
Error: aws_instance.example[0]: aws_instance.example[0]: self reference not allowed: "aws_instance.example.0.arn"
Which leads me to believe the interpolation is calculated after the instance configurations are complete thus it doesn't see that there isn't in fact a circular dependency.
Any ideas?
Thanks
Use terraform apply -parallelism=1 to limit the number of concurrent operations to 1 at a time.