Terragrunt ignores newly added outputs in a project - terraform

I've inherited a terraform project set up to deploy to the Elastic Container Service on AWS.
So far, I've greatly enjoyed working with it, and have managed to change a few things despite being very new to terraform.
Our project uses terragrunt to martial the environments and I've made changes tot he files to add environment specific settings before and it's gone great.
However, I've tried to add a whole new shiny module and... terragrunt hates it.
This is the code I've tried to add:
terraform {
source = "${path_relative_from_include()}/../modules//auto_scaling"
}
dependency "analytics_cluster" {
config_path = "../analytics_cluster"
}
dependency "analytics_app" {
config_path = "../analytics_app"
}
include {
path = find_in_parent_folders()
}
inputs = {
ecs_cluster_name = dependency.analytics_cluster.outputs.name
ecs_app_service_name = dependency.analytics_app.outputs.app_service_name
ecs_sidecar_service_name = dependency.analytics_app.outputs.sidecar_service_name
}
dependencies {
paths = [
"../analytics_cluster",
"../analytics_app",
]
}
and the error I'm getting is:
terragrunt.hcl:19,62-79: Unsupported attribute; This object does not have an attribute named "app_service_name"., and 1 other diagnostic(s)
This is a variable I added into the outputs of the module whose dependency I have set.
This is what the outputs look like:
# output "service_name" {
# value = module.analytics_app.service_name
# }
output "app_service_name" {
value = module.analytics_app.app_service
}
output "sidecar_service_name" {
value = module.analytics_app.sidecar_service
}
The weirdest thing is if I change the .hcl file to the commented out output, like so:
inputs = {
ecs_cluster_name = dependency.analytics_cluster.outputs.name
ecs_app_service_name = dependency.analytics_app.outputs.service_name
}
then this is a valid input, despite the fact that service_name is now commented out.
Why are the new output variables not being picked up? And why is the old variable which I've removed still present?

My guess is that you are only picking up the outputs of previously applied modules. This is because when a module is applied, its contents (along with an outputs.tf definition) are created in the .terragrunt-cache directory.
When you add a new variable but do not apply it, that file won't be regenerated as well, hence the missing attribute.
Try running tg apply on the dependency before running tg init on the target module.
I had this problem when I tried to run-all apply on a new repository with dependencies. Terragrunt said no outputs exist for the dependencies, but this also was because dependency modules were not applied yet. After I manually applied them - it worked.

Related

Terraform: share resource between modules

I'm creating a terraform module which is working fine. However, when I use it multiple times it creates multiples roles and policies which are literally the same.
I'm thinking if there is a way for the module to create a role when I call it for the first time and keep using the same role for the subsequent modules
No, Terraform does not support this. Your best bet is to create the shared resources outside the module (or in a separate module), and then pass them in as input arguments into the module you're creating multiple times.
I like the approach of having a module for "shared" resources, because then you can pass that entire module in as an input argument into any module that uses those shared resources.
EDIT: Sample code for shared modules:
main.tf
module "mod1" {
source = "./mymodule1"
}
module "mod2" {
source = "./mymodule2"
input_module = module.mod1
}
output "mod2" {
value = module.mod2
}
mymodule1/main.tf
output "some_field" {
value = "foo"
}
mymodule2/main.tf
variable "input_module" {}
output "module_that_was_input" {
value = var.input_module
}
Result:
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
mod2 = {
"module_that_was_input" = {
"some_field" = "foo"
}
}

Terraform: ignore changes to a certain environment variable

I have an AWS Lambda function I created using terraform. Code-changes are auto-deployed from our CI-server and the commit-sha is passed as an environment variable (GIT_COMMIT_HASH) - so this changes the Lambda function outside of the Terraform-scope (because people were asking...).
This works good so far. But now I wanted to update the function's node-version and terraform tries to reset the env var to the initial value of "unknown".
I tried to use the ignore_changes block but couldn't get terraform to ignore the changes made elsewhere ...
resource "aws_lambda_function" "test" {
filename = data.archive_file.helloworld.output_path
function_name = "TestName_${var.environment}"
role = aws_iam_role.test.arn
handler = "src/index.handler"
runtime = "nodejs14.x"
timeout = 1
memory_size = 128
environment {
variables = {
GIT_COMMIT_HASH = "unknown"
}
}
lifecycle {
ignore_changes = [
environment.0.variables["GIT_COMMIT_HASH"],
]
}
}
Is this possible? How do I have to reference the variable?
** edit **
Plan output looks like this:
# aws_lambda_function.test will be updated in-place
~ resource "aws_lambda_function" "test" {
# ... removed some lines
source_code_size = 48012865
tags = {}
timeout = 1
version = "12"
~ environment {
~ variables = {
~ "GIT_COMMIT_HASH" = "b7a77d0" -> "unknown"
}
}
tracing_config {
mode = "PassThrough"
}
}
I tried to replicate the issue and in my tests it works exactly as expected. I can only suspect that you are using an old version of TF, where this issue occurs. There has been numerous GitHub Issues reported regarding the limitations of ignore_changes. For example, here, here or here.
I performed tests using Terraform v0.15.3 with aws v3.31.0, and I can confirm that ignore_changes works as it should. Since this is a TF internal problem, the only way to rectify the problem, to the best of my knowledge, would be to upgrade your TF.

How to define global variables in terraform?

I have a terraform project I am working on. In it, I want a file to contain many variables. I want these variables to be accessible from any module of the project. I have looked in the docs and on a udemy course but still don't see how to do this. How does one do this in terraform? Thanks!
I don't think this is possible. There are several discussions about this at Github, but this is not something the Hashicorp team wants.
In general we're against the particular solution of Global Variables, since it makes the input -> resources -> output flow of Modules less explicit, and explicitness is a core design goal.
I know, we have to repeat a lot of variables between different modules
I try to do this
Define a the set of common variables e.g. common_tags at the bottom/top of the variables.tf file for all modules. Usually, your tech ops admin/cloud admin will have a template project created for this.
For each module, I add the following as the last item
global_var = var.global_var_val
An example is common tags. In any root/child module I combine them using merge() function. I hope that makes sense.
You can use -var-file to pass the overrides. Example:
terraform -chdir=staging/000base apply -var-file ../../global.tfvars
Other way can be dedicated shared module.
For example you have module with:
output "my_var" {
value = "apollo440"
}
And in other modules you can use it like:
module "gvars" {
# source = "git#github.com:user/terraform-global-vars.git?ref=v0.0.1-alpha"
source="../../../modules/global_vars"
}
provider "something" {
name="${module.gvars.my_var}"
}
Third way could be to use https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http to query some http endpoint.
Ouh... and forth way could be to utilize Vault Provider https://registry.terraform.io/providers/hashicorp/vault/latest/docs
For pathing constants between lots of modules we can use the following simple way.
/modules/global_constants/outputs.tf:
Describe module with global constants as outputs:
output "parameter_1" {
value = "value_1"
sensitive = true
}
output "parameter_2" {
value = "value_2"
sensitive = true
}
/example_1.tf
After we can use in any *.tf
module "global_settings" {
source = "./modules/global_constants"
}
data "azurerm_key_vault" "keyvault" {
name = module.global_settings.parameter_1
resource_group_name = module.global_settings.parameter_2
}
/modules/module2/main.tf
Or in other any modules:
module "global_settings" {
source = "../global_constants"
}
data "azurerm_key_vault" "keyvault" {
name = module.global_settings.parameter_1
resource_group_name = module.global_settings.parameter_2
}

Terraform: can module's name be resolved dynmically?

Given a series of modules whose names follow a pattern:
module "mod-a" {
// ...
}
module "mod-b" {
// ...
}
module "mod-b" {
// ...
}
and assuming each module defines an output called my_output, can I refer to a particular module on the basis of a dynamically resolved name?
e.g.
...
// some_module = "mod-a"
some_value = module[some_module].my_output
...
The syntax above gives the error:
The "module" object cannot be accessed directly. Instead, access one of its
attributes.
Is there another way to access a module whose name is only known at runtime?
To achieve this in Terraform today (Terraform 0.12.13), you'll need to construct a suitable map explicitly as a local value, and then index that map:
locals {
modules = {
mod_a = module.mod_a
mod_b = module.mod_b
mod_c = module.mod_c
}
}
Elsewhere in the configuration you can then use an expression like local.modules[local.dynamic_module_key], selecting the desired object from the map.
Terraform requires static references to objects like this so that it can properly construct a dependency graph. In this case, Terraform infers that local.modules depends on all of the outputs from all three of these modules, and thus anything that refers to local.modules must wait until all of the outputs from all of these modules are ready to ensure that the final index operation has a complete value to work with.

Terraform - variable definition

I have inherited a project with two files:
a.tf
dir_name/b.tf
and each contains:
variable "region" {
default = "us-east-1"
}
Is there any reason why I can't delete the variable definition from dir_name/b.tf as it seems to be already defined?
UPDATE
a.tf contains a module definition that goes like this:
module "dir_name" {
source = "./dir_name"
}
No, you can't remove one or the other. Terraform works on the module level, where each module has an explicit set of input variables and output attributes; variables can't be passed implicitly from one script to a module.
If you're passing variables from one place to another, it does currently result in a lot of repetition, eg:
module "dir_name" {
source = "./dir_name"
region = "${var.region}"
}

Resources