Hi,
I'm leveraging AWS ECS with Terraform. I've divided the stacks into a common set of ECS resources (ECS Cluster, ASG, Launch Config, IAM Role + Policy, SG) and application specific resources (ECS Service, ELB, ECS Task, IAM Role + Policy). The reason behind this is so that I can share the ECS Cluster across multiple applications for multi-tenancy.
The different stacks have been created in different Git repos. However the ECS Service needs a reference to the ECS Cluster. What is the recommended way to reference a resource in a different stack?
Hi @andrewoh531
Thanks for the question here. Hopefully, I can help :)
Right now, I tend to use modules for everything. Think of modules as re-usable terraform scripts. You have a module package that has all the code in it (for example ecs service) and you pass all the variables in
This means for something like a dev environment you would have the following:
module "vpc" {
source = "../modules/vpc"
name = "dev"
cidr = "10.10.0.0/16"
private_subnets = ["10.10.160.0/19"]
public_subnets = ["10.10.0.0/21"]
availability_zones = ["${data.aws_availability_zones.zones.names}"]
}
I need to use the outputs of the VPC in ECS Cluster so i add the ECS Cluster module to my environment as follows:
module "ecs_cluster" {
source = "../modules/ecs_cluster"
env_prefix = "dev"
ami_id = "${var.ecs_ami}"
cluster_name = "dev_cluster"
desired_capacity = "3"
max_size = "5"
instance_type = "t2.large"
vpc_id = "${module.vpc.vpc_id}"
subnet_ids = ["${module.vpc.private_subnets}"]
}
As you can see, I am passing in vpc_id and subnet_ids from the VPC module. Terraform is clever enough to understand this in the graph and will create the resources in the VPC module before it creates the modules in the ECS cluster module
Hope this sort of helps - I can suggest reading https://medium.com/@petey5000/petes-terraform-tips-694a3c4c5169 as some good tips for real world terraform usage :)
Paul
Yeha, but that way for that module it will create a new resource. He's talking about two different resources created with two different "terraform apply" commands
The general idea here is to have one of the configurations ("stack" is not usual Terraform terminology) write out information about what it has created into some data store that the other configuration is able to access, using paired resource types and data sources.
For example:
tags
filter argument on the aws_security_group
data source by relying on that tagging scheme.consul_key_prefix
to publish information to a prefix in Consul's key/value store and the the "consumer" configuration can use the consul_keys
data source to retrieve that information.aws_s3_bucket_object
resource type to write and the corresponding data source to read. (This'll get easier in the forthcoming 0.12 release because it'll have both jsonencode
and jsondecode
functions so you can easily bundle several items into a single object, since S3 isn't really optimized for storing and retrieving lots of tiny objects.)dns
provider can look up information from DNS in a hosting-provider-agnostic way.An easy option to get started, if you have your Terraform states published in a common shared location that can be accessed by the credentials running the consumer configuration, is to use terraform_remote_state
to read the outputs directly out of the producing configuration's state. This does generally require making states globally readable (or, at least, managing some more complex permissions between them) but avoids the need to introduce an additional data store into the mix.
If you have many more consumers than producers (the common case, I think) then I'd prefer to hide the details of how this data is _retrieved_ in a re-usable module that each consumer can call, and then if you change your mind about how this information is distributed in future you can just update that module and have the various consumers immediately read from the new locations:
module "shared_infra" {
# This module doesn't _create_ the shared infrastructure, but rather just uses
# data sources to _find_ that infrastructure, returning the ids it discovered.
source = "..." # whatever makes sense in your environment
# Pass any arguments you need to distinguish between distinct deployment
# environments here. This is just a placeholder, assuming that there's some
# way to map from this name to a suitable data store for the settings.
environment = "production"
}
resource "aws_ecs_service" "example" {
cluster_id = "${module.shared_infra.ecs_cluster_id}"
# ...
}
I wrote some more words about how I employed this pattern at a previous employer (using Consul as the data store, for example) in an article on my blog, with some other parts of what I discussed here on the "bonus patterns" article in that series.
I'm going to lock this issue because it has been closed for _30 days_ โณ. This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
Most helpful comment
The general idea here is to have one of the configurations ("stack" is not usual Terraform terminology) write out information about what it has created into some data store that the other configuration is able to access, using paired resource types and data sources.
For example:
tags
filter argument on theaws_security_group
data source by relying on that tagging scheme.consul_key_prefix
to publish information to a prefix in Consul's key/value store and the the "consumer" configuration can use theconsul_keys
data source to retrieve that information.aws_s3_bucket_object
resource type to write and the corresponding data source to read. (This'll get easier in the forthcoming 0.12 release because it'll have bothjsonencode
andjsondecode
functions so you can easily bundle several items into a single object, since S3 isn't really optimized for storing and retrieving lots of tiny objects.)dns
provider can look up information from DNS in a hosting-provider-agnostic way.An easy option to get started, if you have your Terraform states published in a common shared location that can be accessed by the credentials running the consumer configuration, is to use
terraform_remote_state
to read the outputs directly out of the producing configuration's state. This does generally require making states globally readable (or, at least, managing some more complex permissions between them) but avoids the need to introduce an additional data store into the mix.If you have many more consumers than producers (the common case, I think) then I'd prefer to hide the details of how this data is _retrieved_ in a re-usable module that each consumer can call, and then if you change your mind about how this information is distributed in future you can just update that module and have the various consumers immediately read from the new locations:
I wrote some more words about how I employed this pattern at a previous employer (using Consul as the data store, for example) in an article on my blog, with some other parts of what I discussed here on the "bonus patterns" article in that series.