Terraform provision to multiple accounts - terraform

I use terraform to deploy lambda to one aws account and s3 trigger for lambda in other. Because of that, I created two separate folders and each of them holds state of specific account.
However, I'd like to merge everything into one template. Is it possible to do it? Example:
provider "aws" {
profile = "${var.aws_profile}"
region = "eu-west-1"
}
provider "aws" {
alias = "bucket-trigger-account"
region = "eu-west-1"
profile = "${var.aws_bucket_trigger_profile}
}
I want thie following resource to be provisioned by aws bucket-trigger-account. How can I do it?
resource "aws_s3_bucket_notification" "bucket_notification" {
bucket = "${var.notifications_bucket}"
lambda_function {
lambda_function_arn = "arn:aws:lambda:eu-west-1-xxx"
events = ["s3:ObjectCreated:*"]
filter_suffix = ".test"
}
}

Found out that simply using provider argument on resource let's you use a different provider for that resource: provider = "aws.bucket-trigger-account"

Related

Create a S3 bucket on each AWS account created with terraform

I am using terraform to create multiple AWS accounts using aws_organizations_account. What I am now trying to do is to create a aws_S3_bucket on each new created accounts.
resource "aws_organizations_account" "this" {
for_each = local.all_user_ids
name = "Dev Sandbox ${each.value}"
email = "${var.manager}+sbx_${each.value}#example.com"
role_name = "Administrator"
parent_id = var.sandbox_organizational_unit_id
}
resource "aws_s3_bucket" "b" {
bucket = "my-tf-test-bucket"
acl = "private"
}
Everything is working as expected for aws_organizations_account during my terraform apply but my S3 bucket is created inside my current AWS project while I am trying to create a S3 bucket for every new AWS account.
Step 1: Create_terraform_s3_buckets.tf
# First configure the AWS Provider
provider "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = var.aws_region
}
// then use the resource block and create all the buckets in the variable array
// Here setup your accounts would in the variable for e.g. My_Accounts_s3_buckets
variable "My_Accounts_s3_buckets" {
type = list
default = ["Testbucket1.app", "Testbucket2.app", "Testbucket3.app"]
}
Look up the s3_bucket objectfor more help from Terraform ref. aws_s3_bucket
// resource "aws_s3_bucket" "rugged_buckets" "log_bucket" { <- different types of options on your buckets
resource "aws_s3_bucket" "b" {
count = length(var.My_Accounts_s3_buckets) // here are you 3 accounts
bucket = var.My_Accounts_s3_buckets[count.index]
acl = "private"
region = "us-east-1"
force_destroy = true
tags = {
Name = "My Test bucket"
Environment = "Dev"
}
}
Step 2: You can now automate this with the variables file.
# Make sure you keep this order
variable "My_Accounts_s3_buckets" {
type = list
default = ["mybucket1.app",
"mybucket2.app" // you can add more.. as needed
]
}

Is there a way to retrieve data from another Account in Terraform?

I want to use the resource "data" in Terraform for example for an sns topic but I don't want too look for a resource in the aws-account, for which I'm deploying my other resources. It should look up to my other aws-account (in the same organization) and find resources in there. Is there a way to make this happen?
data "aws_sns_topic" "topic_alarms_data" {
name = "topic_alarms"
}
Define an aws provider with credentials to the remote account:
# Default provider that you use:
provider "aws" {
region = var.context.aws_region
assume_role {
role_arn = format("arn:aws:iam::%s:role/TerraformRole", var.account_id)
}
}
provider "aws" {
alias = "remote"
region = var.context.aws_region
assume_role {
role_arn = format("arn:aws:iam::%s:role/TerraformRole", var.remote_account_id)
}
}
data "aws_sns_topic" "topic_alarms_data" {
provider = aws.remote
name = "topic_alarms"
}
Now the topics are loaded from the second provider.

AWS - Set a global budget for a project?

I am working on a Terraform project to create, update or delete AWS sandboxs for every new employee in my company. However, we would like to have a fix budget for every sandbox and if possible, not being able to exceed that budget. I read about AWS Budget Actions but I am not sure what will be the best way to integrate it with my current code.
main.tf
## this is creating a new project and I would like to set a budget of $50 for this account
resource "aws_organizations_account" "account" {
name = "sandbox"
email = "new-user+sandbox#company.com"
role_name = "myOrganizationRole"
}
You need to create aws_budgets_budget and aws_budgets_budget_action resources in the new account. To create resources in the new account, you need to create a provider for that account by assuming the administration role that was created:
provider "aws" {
alias = "sandbox"
region = "us-east-1"
...
// Specify your credentials source, same as you use for the provider you used for the aws_organizations_account resource
...
assume_role {
role_arn = "arn:aws:iam::${aws_organizations_account.account.id}:role/${aws_organizations_account.account.role_name}"
}
}
And now you can use that provider to create the budget resources in the new sandbox account (note the provider input parameters):
resource "aws_budgets_budget" "example" {
provider = aws.sandbox
name = "example"
budget_type = "USAGE"
limit_amount = "10.0"
limit_unit = "dollars"
time_period_start = "2006-01-02_15:04"
time_unit = "MONTHLY"
}
resource "aws_budgets_budget_action" "example" {
provider = aws.sandbox
budget_name = aws_budgets_budget.example.name
...
}

Is there a way to merge terraform variables to use same module across multiple AWS regions?

I'm brand new to terraform, and I'm utilizing terragrunt to help me get things rolling. I have a decent amount of infrastructure to migrate and get set up w/ terraform, but I'm getting my feet underneath me first. We have multiple VPC's in different regions with a lot of the same security group rules used i.e.(web, db, etc..) that I want to replicate across each region.
I have a simple example of how I currently have an EC2 module setup to recreate the security group rules and was wondering if there's a better way to organize this code so I don't have to create a new module for the same SG rule for each region? i.e. some smart way to utilize lists for my vpc's, providers, etc...
since this is just one SG rule across two regions, I'm trying to avoid this growing ugly as we scale up to even more regions and I input multiple SG rules
My state is currently being stored in S3 and in this setup I pull the state so I can access the VPC outputs from another module I used to create the VPC's
terraform {
backend "s3" {}
}
provider "aws" {
version = "~> 1.31.0"
region = "${var.region}"
profile = "${var.profile}"
}
provider "aws" {
version = "~> 1.31.0"
alias = "us-west-1"
region = "us-west-1"
profile = "${var.profile}"
}
#################################
# Data sources to get VPC details
#################################
data "terraform_remote_state" "vpc" {
backend = "s3"
config {
bucket = "${var.vpc_remote_state_bucket}"
key = "${var.vpc_remote_state_key}"
region = "${var.region}"
profile = "${var.profile}"
}
}
#####################
# Security group rule
#####################
module "east1_vpc_web_server_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "2.5.0"
name = "web-server"
description = "Security group for web-servers with HTTP ports open within the VPC"
vpc_id = "${data.terraform_remote_state.vpc.us_east_vpc1_id}"
# Allow VPC public subnets to talk to each other for API's
ingress_cidr_blocks = ["${data.terraform_remote_state.vpc.us_east_vpc1_public_subnets_cidr_blocks}"]
ingress_rules = ["https-443-tcp", "http-80-tcp"]
# List of maps
ingress_with_cidr_blocks = "${var.web_server_ingress_with_cidr_blocks}"
# Allow engress all protocols to outside
egress_rules = ["all-all"]
tags = {
Terraform = "true"
Environment = "${var.environment}"
}
}
module "west1_vpc_web_server_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "2.5.0"
providers {
aws = "aws.us-west-1"
}
name = "web-server"
description = "Security group for web-servers with HTTP ports open within the VPC"
vpc_id = "${data.terraform_remote_state.vpc.us_west_vpc1_id}"
# Allow VPC public subnets to talk to each other for API's
ingress_cidr_blocks = ["${data.terraform_remote_state.vpc.us_west_vpc1_public_subnets_cidr_blocks}"]
ingress_rules = ["https-443-tcp", "http-80-tcp"]
ingress_with_cidr_blocks = "${var.web_server_ingress_with_cidr_blocks}"
# Allow engress all protocols to outside
egress_rules = ["all-all"]
tags = {
Terraform = "true"
Environment = "${var.environment}"
}
}
Your current setup uses two times the same module differing in the provider. You can pass down multiple providers to the module (see the documentation). Then, within the module, you can use the same variables you specified once in your main document to create all the instances you need.
However, since you are using one separate provider for each resource type, you have to have at least some code duplication down the line.
Your code could then look something like this
module "vpc_web_server_sg" {
source = "terraform-aws-modules/security-group/aws"
version = "2.5.0"
providers {
aws.main = "aws"
aws.secondary = "aws.us-west-1"
}
name = "web-server"
description = "Security group for web-servers with HTTP ports open within the VPC"
vpc_id = "${data.terraform_remote_state.vpc.us_west_vpc1_id}"
# Allow VPC public subnets to talk to each other for API's
ingress_cidr_blocks = ["${data.terraform_remote_state.vpc.us_west_vpc1_public_subnets_cidr_blocks}"]
ingress_rules = ["https-443-tcp", "http-80-tcp"]
ingress_with_cidr_blocks = "${var.web_server_ingress_with_cidr_blocks}"
# Allow engress all protocols to outside
egress_rules = ["all-all"]
tags = {
Terraform = "true"
Environment = "${var.environment}"
}
}
Inside your module you can then use the main and secondary provider to deploy all your required resources.

The Terraform resource "random_pet" is not working

This code will create an EC2 instance with name EC2 Instance:
provider "aws" {
region = "eu-west-1"
}
module ec2 {
source = "./ec2_instance"
name = "EC2 Instance"
}
However, if I try and use the random_pet resource the Instance name becomes an empty string.
provider "aws" {
region = "eu-west-1"
}
resource "random_pet" "server" {
length = 4
}
module ec2 {
source = "./ec2_instance"
name = "${random_pet.server.id}"
}
How come?
I'm using the random_pet.server.id code from https://www.terraform.io/docs/providers/random/r/pet.html
UPDATE: by using an output I was able to debug this.
Terraform does not seem to show the value of this variable during a plan. However, when doing an apply it did successfully populate this variable (and therefore name the instance). The question then becomes why does it not work in plan but does in apply?

Resources