How to Output From Nested Source Module?

Hi there,

I’m new to Terragrunt and want to create a VPC using the Terraform Registry Module (terraform-aws-modules/terraform-aws-vpc). I have been able to successfully do this in Terraform and I have also been successful at doing this with Terragrunt too by putting everything into one terragrunt.hcl file like this in my project’s root folder:

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region = "us-east-1"
}
EOF
}

remote_state {
  backend = "s3"
  config = {
    encrypt        = true
    bucket         = "tf-12314325254362"
    key            = "vpc/terraform.tfstate"
    region         = "us-east-1"
  }
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
}


terraform {
  source = "git::git@github.com:terraform-aws-modules/terraform-aws-vpc.git?ref=v2.21.0"
}

inputs = {
  name = "tg"
  cidr = "10.0.0.0/16"
  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  public_subnets  = ["10.0.128.0/20", "10.0.144.0/20", "10.0.160.0/20"]
  private_subnets = ["10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/19"]
}

Now I have moved the source VPC code from this root terragrunt.hcl file to another location under dev/us-east-1/vpc/terragrunt.hcl as follows:

terraform {
  source = "git::git@github.com:terraform-aws-modules/terraform-aws-vpc.git?ref=v2.21.0"
}

inputs = {
  name = "tg"
  cidr = "10.0.0.0/16"
  azs             = ["us-east-1a", "us-east-1b", "us-east-1c"]
  public_subnets  = ["10.0.128.0/20", "10.0.144.0/20", "10.0.160.0/20"]
  private_subnets = ["10.0.0.0/19", "10.0.32.0/19", "10.0.64.0/19"]
}

When I cd to this dev/us-east-1/vpc folder and execute ‘terragrunt apply -auto-approve’, the VPC gets created, however no VPC outputs are displayed in the terminal and no VPC output information gets stored in the remote state file on S3, which I need to have. I understand that I need to create a dev/us-east-1/vpc/outputs.tf file to output all the VPC outputs which will then also get stored in the remote state file but I can’t figure out how to do this. Basically I don’t how to reference this source defined in the terragrunt.hcl file:

terraform {
  source = "git::git@github.com:terraform-aws-modules/terraform-aws-vpc.git?ref=v2.21.0"
}

As it doesn’t have a name handle or something. I think I need to define something like this in the outputs.tf file:

output "vpc_id" {
  value       = module.vpc.vpc_id
}

But there is no module name to reference. Can someone show me how to output all the VPC outputs please?
Thanks

OK so I found a solution though I’m not its not the best. I basically just copied the outputs.tf from the Terraform VPC Registry Module https://github.com/terraform-aws-modules/terraform-aws-vpc/blob/master/outputs.tf into my nested module folder and removed any outputs that I didn’t need. Now all the VPC output appears on screen when I do a terragrunt apply and the VPC state information gets saved in my remote S3 state file.

Hmm, I haven’t been able to reproduce this issue. When I set up a structure similar to yours and apply the code with terragrunt, it clones the open source VPC module, applies the VPC, and prints the outputs that are in the repo.

Note that if you want to use the generated provider.tf and remote_state blocks from the terragrunt.hcl in the parent, you’ll need a section like this in your dev/us-east-1/vpc/terragrunt.hcl:

# Include all settings from the root terragrunt.hcl file
include {
  path = find_in_parent_folders()
}

Hi, thank you for your time and response and sorry for not replying sooner, I was working pulled off onto another project and only coming back to this now.

So I already have the find_in_parent_folders() code you suggested in my dev/us-east-1/vpc/terragrunt.hcl file:

# Include all settings from the root terragrunt.hcl file
    include {
      path = find_in_parent_folders()
    }

Yet still no output appears on-screen or in my state file when I don’t have the downloaded outputs.tf file there. I’m clearly making some basic mistake but I can’t see it. Here is some more information about my project directory hierarchy:

/myproject/123456789/
/myproject/123456789/us-east-1
/myproject/123456789/us-east-1/vpc
/myproject/123456789/us-east-1/vpc/outputs.tf     # downloaded from Terraform Registry
/myproject/123456789/us-east-1/vpc/terragrunt.hcl
/myproject/123456789/us-east-1/region.hcl
/myproject/123456789/account.hcl
/myproject/terragrunt.hcl

The ‘123456789’ represents my AWS account ID. I execute Terragrunt in the /myproject/123456789/us-east-1/vpc folder.

The file contents are as follows:
/myproject/terragrunt.hcl:

locals {
  account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
  region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
  account_name = local.account_vars.locals.account_name
  account_id   = local.account_vars.locals.aws_account_id
  aws_region   = local.region_vars.locals.aws_region
}

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region = "${local.aws_region}"
  version             = "= 3.11.0"
  # Only these AWS Account IDs may be operated on by this template
  allowed_account_ids = ["${local.account_id}"]
}
EOF
}

remote_state {
  backend = "s3"
  config = {
    encrypt        = true
    bucket         = "tf-${local.account_id}-${local.aws_region}"
    key            = "${path_relative_to_include()}/terraform.tfstate"
    region         = local.aws_region
  }
  generate = {
    path      = "backend.tf"
    if_exists = "overwrite_terragrunt"
  }
}

/myproject/123456789/account.hcl:

locals {
  account_name   = "DevOps"
  aws_account_id = "123456789"
}

/myproject/123456789/us-east-1/region.hcl:

locals {
  aws_region = "us-east-1"
  vpc_name                                   = "my-vpc"
  vpc_cidr                                   = "10.10.0.0/16"
  vpc_azs                                    = ["us-east-1a", "us-east-1b", "us-east-1c"]
  vpc_public_subnets                         = ["10.10.128.0/20", "10.10.144.0/20", "10.10.160.0/20"] 
  vpc_private_subnets                        = ["10.10.0.0/19", "10.10.32.0/19", "10.10.64.0/19"]
  vpc_enable_nat_gateway                     = false
  vpc_enable_single_nat_gateway              = true
  vpc_enable_dns_hostnames                   = true
  vpc_enable_s3_endpoint                     = false
  vpc_tags = {
    Terraform   = "true"
  }
}

/myproject/123456789/us-east-1/vpc/terragrunt.hcl:

locals {
  account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
  region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
  account_name = local.account_vars.locals.account_name
  account_id   = local.account_vars.locals.aws_account_id
  aws_region   = local.region_vars.locals.aws_region

  vpc_name = local.region_vars.locals.vpc_name
  vpc_cidr = local.region_vars.locals.vpc_cidr
  vpc_azs         = local.region_vars.locals.vpc_azs
  vpc_public_subnets  = local.region_vars.locals.vpc_public_subnets
  vpc_private_subnets = local.region_vars.locals.vpc_private_subnets
  vpc_db_subnets = local.region_vars.locals.vpc_db_subnets
  vpc_create_database_subnet_group = local.region_vars.locals.vpc_create_database_subnet_group
  vpc_create_database_subnet_route_table = local.region_vars.locals.vpc_create_database_subnet_route_table
  vpc_create_database_internet_gateway_route = local.region_vars.locals.vpc_create_database_internet_gateway_route
  vpc_enable_nat_gateway = local.region_vars.locals.vpc_enable_nat_gateway
  vpc_enable_single_nat_gateway = local.region_vars.locals.vpc_enable_single_nat_gateway
  vpc_enable_dns_hostnames = local.region_vars.locals.vpc_enable_dns_hostnames
  vpc_enable_s3_endpoint = local.region_vars.locals.vpc_enable_s3_endpoint
  vpc_tags = local.region_vars.locals.vpc_tags
}

terraform {
  source = "git::git@github.com:terraform-aws-modules/terraform-aws-vpc.git?ref=v2.60.0"
}

# Include all settings from the root terragrunt.hcl file
include {
  path = find_in_parent_folders()
}

inputs = {
  name = local.vpc_name
  cidr = local.vpc_cidr
  azs             = local.vpc_azs
  public_subnets  = local.vpc_public_subnets
  private_subnets = local.vpc_private_subnets
  enable_nat_gateway = local.vpc_enable_nat_gateway
  single_nat_gateway = local.vpc_enable_single_nat_gateway
  enable_dns_hostnames = local.vpc_enable_dns_hostnames
  enable_s3_endpoint = local.vpc_enable_s3_endpoint
  tags = local.vpc_tags    
}

The code is probably a bit messy but I think the issue I’m having is due to the directory hierarchy and that the outputs are getting swallowed up. Maybe I am executing Terragrunt from the wrong folder. I cd to the /myproject/123456789/us-east-1/vpc folder to execute terragrunt. After I execute terragrunt apply -auto-approve and run terragrunt output I get the following response:

[terragrunt] 2020/10/28 09:40:35 Running command: terraform output

Warning: No outputs found

The state file either has no outputs defined, or all the defined outputs are
empty. Please define an output in your configuration with the `output` keyword
and run `terraform refresh` for it to become available. If you are using
interpolation, please verify the interpolated value is not empty. You can use
the `terraform console` command to assist.

However as I said in my previous post, if I put the downloaded outputs.tf into /myproject/123456789/us-east-1/vpc and run terragrunt again then everything works fine. I think I’m lacking some basic understanding about how the Terragrunt fold hierarchy structure should work.

OK I’ve improved my solution by simply moving the downloaded outputs.tf file to the root project folder, ie from:

/myproject/123456789/us-east-1/vpc/outputs.tf

to:

/myproject/outputs.tf