0
votes

Use-case

  • Using Terraform I want to create different preprod env's (like: dev, qa, uat env.. so on). Resource definition and module usage will be same, only difference will be the name prefix, so that it creates separate resources for each of the mentioned env's, but keeping the VPC common for all.

Terraform Version: v0.13.5

Directory Structure

├── dev
│   ├── dev.tfvars
│   ├── main.tf
│   ├── outputs.tf
│   ├── provider.tf
│   └── variables.tf
├── qa
│   ├── qa.tfvars
│   ├── main.tf
│   ├── outputs.tf
│   ├── provider.tf
│   └── variables.tf
└── preprod-common
    ├── main.tf
    ├── outputs.tf
    ├── provider.tf
    └── variables.tf

preprod-common

main.tf

  module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = var.vpc_name
  cidr = var.vpc_cidr

  azs             = var.vpc_azs
  private_subnets = var.vpc_private_subnets
  public_subnets  = var.vpc_public_subnets

}

Output.tf

output "vpc_id" {
  description = "The ID of the VPC"
  value       = module.vpc.vpc_id
}

dev

main.tf

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  
  name        = ${var.prefix}-${var.sg-name}
  vpc_id      = <vpc_id created from preprod-common>
}

prefix - is the environment name. This way it creates separate resource for each environment, as per prefix value which will be like: (dev, qa or uat..so on).

qa

main.tf

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  
  name        = ${var.prefix}-${var.sg-name}
  vpc_id      = <vpc_id created from preprod-common>
}

and so on.. for other environments.

FYI - I already ran preprod-common configuration and it has created a new AWS VPC. The question is, how can I refer the vpc_id created from preprod-common into dev, qa and other lower environments?

Note: - I am aware about workspaces as well but this is how I want to implement.

2
Where and how do you use child in your parent template?Marcin
@Marcin, That's a part of my question, how can I use it or what is the best way to use it? Sorry, if I was not that clear earlier.Arun Dhiman
You use it analogically to using vpc module: module "child-module" source path to it.Marcin
yeah i did that. So again i need to define vpc_id when I import the local child module? and what will be the value argument to the vpc_id in the imported module?Arun Dhiman
@Marcin, Please have a look again, I have updated my question for better understanding.Arun Dhiman

2 Answers

2
votes

Before answering the question I just want to note some terminology: I think what you are calling a "template" here is what is actually called a Terraform module. I'm noting that not to be pedantic but just because knowing the correct terminology will be helpful when referring to the Terraform documentation or asking other questions in the future.

With that said, the pattern you are following here, of calling two modules and passing the output of one into another, is called Module Composition and the documentation about that pattern has a number of different examples of it.

For your specific case, you can pass the vpc_id output from your vpc module into the security-group module like this:

module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = var.vpc_name
  cidr = var.vpc_cidr

  azs             = var.vpc_azs
  private_subnets = var.vpc_private_subnets
  public_subnets  = var.vpc_public_subnets

}

module "security_group" {
  source = "terraform-aws-modules/security-group/aws"
  
  name   = var.sg_name
  vpc_id = module.vpc.vpc_id
}
0
votes

Below is the answer to my question. Considering the same example in my question this is what you need to do.

Note: If you are saving your terraform state locally. That means if you are using local backend.

In your dev directory

main.tf

data "terraform_remote_state" "vpc" {
  backend = "local"

  config = {
    path = "../preprod-common/terraform.tfstate"
  }
}

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  
  name        = ${var.prefix}-${var.sg-name}
  vpc_id      = data.terraform_remote_state.vpc.outputs.vpc_id
}

Note: If you are saving your terraform state remotely. That means if you are using remote backend with Terraform Cloud.

In your dev directory

main.tf

data "terraform_remote_state" "vpc" {
  backend = "remote"

  config = {
    organization = "hashicorp"
    workspaces = {
      name = "vpc-preprod"
    }
  }
}

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  
  name        = ${var.prefix}-${var.sg-name}
  vpc_id      = data.terraform_remote_state.vpc.outputs.vpc_id
}

Note: If you are saving your terraform state remotely. That means if you are using remote backend other than terraform cloud, example S3. In this case as mentioned in the terraform documentation (https://www.terraform.io/docs/backends/operations.html), I am pasting the exact words below.

Currently, the remote backend is the only backend to support remote operations, and Terraform Cloud is the only remote execution environment that supports it.

In this case one solution would be to pull the terraform state of preprod-common locally, let's say in your /tmp directory and use it with the local backend.

cd preprod-common

terraform state pull > /tmp/terraform.state

In your dev directory

main.tf

data "terraform_remote_state" "common" {
  backend = "local"

  config = {
    path = "/tmp/terraform.tfstate"
  }
}

module "security-group" {
  source  = "terraform-aws-modules/security-group/aws"
  
  name        = ${var.prefix}-${var.sg-name}
  vpc_id      = data.terraform_remote_state.vpc.outputs.vpc_id
}

Similarly you have to do it for other lower environments like: qa, uat, int.. so on