2

Can terraform have 2 main files with one running after another ?

I need main1 to be ran fully before it calls main2 as my main2 has for_each, which requires the resources to be created prior. Same with main3.

So main1 has to be run fully (terraform plan/apply) then main2.

My tree structure

├── README.md
├── config
│   ├── prod
│   └── sandbox
│       └── us_east_2
│           ├── nonprod.tfvars
│           └── prod.tfvars
├── init
│   ├── prod
│   └── sandbox
│       └── us_east_2
│           ├── nonprod.tfvars
│           └── prod.tfvars
├── main.tf
├── modules
│   ├── create_transit_gateway
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── ec2
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── transit_gateway
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── transit_gateway_asso_prop
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   ├── transit_gateway_locals
│   │   ├── main.tf
│   │   ├── outputs.tf
│   │   └── variables.tf
│   └── vpc
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── output.tf
└── variables.tf
provider "aws" {
  region  = var.REGION
}

module "create_transit_gateway" {
    source = "./modules/create_transit_gateway"
    REGION_SHORTHAND = var.REGION_SHORTHAND
    TGW_LOCAL = var.TGW_LOCAL
    ENV_NAME = var.ENV_NAME
    AMAZON_SIDE_ASN = var.AMAZON_SIDE_ASN
}

module "vpc" {
    depends_on = [module.create_transit_gateway]
    source = "./modules/vpc"
    NETWORK_VPC = var.NETWORK_VPC
    SUBNETS = var.SUBNETS
    ROUTE_TABLES = var.ROUTE_TABLES
    ENV_NAME = var.ENV_NAME
    TGW_LOCAL = var.TGW_LOCAL
    REGION_SHORTHAND = var.REGION_SHORTHAND
}

module "ec2" {
    depends_on = [module.create_transit_gateway, module.vpc]
    source = "./modules/ec2"
    FW_ASNS = var.FW_ASNS
    FIREWALLS = var.FIREWALLS
    AMI_IMAGE_KEY = var.AMI_IMAGE_KEY
    REGION_SHORTHAND = var.REGION_SHORTHAND
    ENV_NAME = var.ENV_NAME
    VPC_ID = module.vpc.VPC_ID
    AWS_SUBNETS = module.vpc.AWS_SUBNETS
}

module "transit_gateway" {
    depends_on = [module.create_transit_gateway, module.ec2, module.vpc]
    source = "./modules/transit_gateway"
    REGION = var.REGION
    TGW_LOCAL = var.TGW_LOCAL
    ENV_NAME = var.ENV_NAME
    SITE_CUSTOMER_GATEWAYS = var.SITE_CUSTOMER_GATEWAYS
    EIP_FW1_NICS = module.ec2.EIP_FW1_NICS
    EIP_FW2_NICS = module.ec2.EIP_FW2_NICS
    AWS_SUBNETS = module.vpc.AWS_SUBNETS
    FW_ASNS = var.FW_ASNS
    TGW_PEERS = var.TGW_PEERS
    VPC_ID = module.vpc.VPC_ID
    TGW_ROUTE_TABLES = var.TGW_ROUTE_TABLES
    REGION_SHORTHAND = var.REGION_SHORTHAND
    AWS_ACCOUNT_ID = var.AWS_ACCOUNT_ID
}

module "transit_gateway_locals" {
    depends_on = [module.create_transit_gateway, module.transit_gateway]
    source = "./modules/transit_gateway_locals"
    REGION = var.REGION
    TGW_PEERS = var.TGW_PEERS
    TGA_VPC_ATTACHMENT = module.vpc.TG_VPC_ATTACHMENT
    ENV_NAME = var.ENV_NAME
    AWS_ACCOUNT_ID = var.AWS_ACCOUNT_ID
    NETWORK_S2S_VPN = module.transit_gateway.NETWORK_S2S_VPN
    NETWORK_TGW_RT = module.transit_gateway.NETWORK_TGW_RT
    TGW_PEERS_RESOURCE = module.transit_gateway.TGW_PEERS_RESOURCE
    PEER_ACCEPTOR_STATUS = module.transit_gateway.PEER_ACCEPTOR_STATUS
}

module "transit_gateway_asso_prop" {
    depends_on = [module.transit_gateway_locals]
    source = "./modules/transit_gateway_asso_prop"
    REGION = var.REGION
    VPC_ASSO_LIST = module.transit_gateway_locals.VPC_ASSO_LIST
    VPC_PROP_LIST = module.transit_gateway_locals.VPC_PROP_LIST
    VPN_ASSO_LIST = module.transit_gateway_locals.VPN_ASSO_LIST
    VPN_PROP_LIST = module.transit_gateway_locals.VPN_PROP_LIST
    PEER_ASSO_LIST = module.transit_gateway_locals.PEER_ASSO_LIST
    SEC_STATIC_LIST = module.transit_gateway_locals.SEC_STATIC_LIST
    TGW_PEERS = var.TGW_PEERS
    ENV_NAME = var.ENV_NAME
}

I need module transit_gateway_asso_prop to run after apply of the above modules. As vars like VPN_PROP_LIST, VPN_ASSO_LIST etc. will be created after the transit_gateway_locals and other above modules are applied

Error: Invalid for_each argument

  on modules/transit_gateway_asso_prop/main.tf line 325, in resource "aws_ec2_transit_gateway_route_table_propagation" "NETWORK-TGW-VPNPROPAGATION":
 325:   for_each = var.VPN_PROP_LIST 

The "for_each" 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 for_each depends on.

Vinita Shah
  • 99
  • 12

4 Answers4

2

Sadly you can't do this. The proper way to structure your code is into modules. So you would have to transform main1 and main2 into submodules and then create top-level main.tf. The creation of main1 and main2 modules in main will ensure correct order.

.
├── main.tf
└── modules
    ├── main1
    │   └── main.tf
    └── main2
        └── main.tf

Then in main.tf you would create the two modules:

module "main1" {    
    source = "./modules/main1/main"
    VAR2 = var.VAR2
}

module "main2" {    
    source = "./modules/main1/main"
    VAR2 = module.main2.id
}
Marcin
  • 215,873
  • 14
  • 235
  • 294
  • Thank you for the comments @Marcin . I have modules already within my code. I reframed my question. Let me know if that helps. – Vinita Shah Apr 19 '21 at 22:16
1

With an example so contrived it's hard to give specific advice so instead some general observations:

  • The main unit of decomposition for "set of infrastructure to be created/modified together" is a separate configuration, rather than a separate module within the same configuration. If you split your configuration into two parts and use Data Sources so that the second part can read the result of the first part then you can apply the overall system in two steps, thus ensuring the correct order.

  • With that said, for the problem of a for_each value that isn't known during planning there are often (but not always) different ways to structure things so that Terraform can at least see the keys of the map at plan time. As long as the keys are known, the values can be unknown.

    If you'd like to hear more about that then I'd suggest starting a new question on Stack Overflow where you share a more realistic version of your configuration and describe the underlying problem, since then an answerer can suggest specific refactors or restructures that might allow for a working solution.

  • High use of depends_on, particularly within module blocks, is often a code smell in Terraform, because it suggests that the modules are not properly encapsulated and so they are relying on external annotations in order to behave correctly.

    That isn't always true, and again it's hard to be specific without a real example, but there are better techniques for telling Terraform the relationships between objects in different modules which can often make modules behave correctly by default, without additional annotations like that.

Martin Atkins
  • 62,420
  • 8
  • 120
  • 138
  • Thank you for the comments @Martin. makes sense. I reframed my question. Let me know if that helps. – Vinita Shah – Vinita Shah Apr 19 '21 at 22:16
0

You could do this with -target. Its not ideal what your doing.

terraform plan -target=module.mymodule.aws_instance.myinstance ```

terraform apply

This might help: https://stackoverflow.com/questions/46762047/i-would-like-to-run-terraform-only-for-a-specific-resource
OpenBSDNinja
  • 1,037
  • 10
  • 18
0

I had if rt_att_id.rt_id != null condition in my if loop.

For each cannot have reference to a resource that is not known, which is why it erred.

I modified my loop by getting the route table id via data and using specific names instead of looping through the whole route table.

Thank you all for helping.

Vinita Shah
  • 99
  • 12