Take Output From First Terraform and Use in Second Terraform - amazon-web-services

I need two different Terraform file for different purposes. In the Second Terraform file, I have to take input from the output of First Terraform file.
In my Scenario, My first Terraform creates an AWS Security Group. Now I have to use the the ID of this Security group in my Second Terraform file.
I also want to sure that Second Terraform creation cannot initilaize before First Terraform complete. How can i achieve this?

It doesn't matter how many .tf files you are creating. Terraform first loads all the .tf files and then try to create a graph to create the resources. So you can do it like this.
resource "aws_security_group" "default" {
name = "allow_all"
description = "Allow all inbound traffic"
.
.
}
Now you can use id of this security group in another file/other module. For ex. let's use it for ec2 creation. Like.
resource "aws_instance" "web" {
ami = "${var.ami_id}"
instance_type = "t2.micro"
security_groups = ["${aws_security_group.default.id}"]
}
For more details about security group params, you can check following document.
https://www.terraform.io/docs/providers/aws/r/security_group.html

for this requirement you might want to use terraform modules with which you can have the luxury of code reuse as well as you can supply that ID of security group to as many terraform files as you want. And whenever you do terraform apply it will check for resource dependencies and execution will take place accordingly.

Related

Terraform: "known only after apply" ISSUE

I'm creating an aws_subnet and referencing it in another resource.
Example:
resource "aws_subnet" "mango" {
vpc_id = aws_vpc.mango.id
cidr_block = "${var.subnet_cidr}"
}
The reference
network_configuration {
subnets = "${aws_subnet.mango.id}"
}
When planning it I get
aws_subnet.mango.id is a string, known only after apply
error. I'm new to Terraform. Is there something similar to Cloudformation's DependsOn or Export/Import?
This is a case of explicit dependency.
The argument depends_on similar to CloudFormation's DependsOn solves such dependencies.
Note: "Since Terraform will wait to create the dependent resource until after the specified resource is created, adding explicit dependencies can increase the length of time it takes for Terraform to create your infrastructure."
Example:
depends_on = [aws_subnet.mango]
This line:
cidr_block = "${var.subnet_cidr}"
should look like
cidr_block = var.subnet_cidr
And this line:
subnets = "${aws_subnet.mango.id}"
should look like
subnets = aws_subnet.mango.id
Terraform gives a warning when a string value only has a template in it. The reason is that for cases like yours, it's able to make a graph with the bare value and resolve it on apply, but it's unable to make the string without creating the resource first.
The information like ID or other such information which will be generated by AWS, cannot be predicted by terraform plan as this step only does a dry run and doesn't apply any changes.
The fields which have known only after apply is not an error, but just informs the user that these fields only get populated in terraform state after its applied. The dependency order is handled by Terraform and hence referring values (even those which have known only after apply) will be resolved at run time.
The error in this case is not the string "known only after apply" but the message "Incorrect attribute value type".
subnets (plural) requires a list of strings but you gave only one string.
network_configuration {
subnets = ["${aws_subnet.mango.id}"]
}
The depends_on is not necessary in this case, tf resolves this dependency by itself. depends_on is only important if tf can't get this by itself.
writing "${foo.bar}" instead of foo.bar is also no problem but doesn't follow tf's code-style-rules.

How to get the default vpc id with terraform

I am trying to get the vpc_id of default vpc in my aws account using terraform
This is what I tried but it gives an error
Error: Invalid data source
this is what I tried:
data "aws_default_vpc" "default" {
}
# vpc
resource "aws_vpc" "kubernetes-vpc" {
cidr_block = "${var.vpc_cidr_block}"
enable_dns_hostnames = true
tags = {
Name = "kubernetes-vpc"
}
}
The aws_default_vpc is indeed not a valid data source. But the aws_vpc data source does have a boolean default you can use to choose the default vpc:
data "aws_vpc" "default" {
default = true
}
For completeness, I'll add that an aws_default_vpc resource exists that also manages the default VPC and implements the resource life-cycle without really creating the VPC* but would make changes in the resource like changing tags (and that includes its name).
* Unless you forcefully destroy the default VPC
From the docs:
This is an advanced resource and has special caveats to be aware of when using it. Please read this document in its entirety before using this resource.
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/default_vpc
This
resource "aws_default_vpc" "default" {
}
will do.
I think this is convenient for terraform projects managing a whole AWS account, but I would advise against using it whenever multiple terraform projects are deployed in a single organization account. You should better stay with #blokje5's answer in that case.

How to block Terraform from deleting an imported resource?

I'm brand new to Terraform so I'm sure i'm missing something, but the answers i'm finding don't seem to be asking the same question I have.
I have an AWS VPC/Security Group that we need our EC2 instances to be created under and this VPC/SG is already created. To create an EC2 instance, Terraform requires that if I don't have a default VPC, I must import my own. But once I import and apply my plan, when I wish to destroy it, its trying to destroy my VPC as well. How do I encapsulate my resources so when I run "terraform apply", I can create an EC2 instance with my imported VPC, but when I run "terraform destroy" I only destroy my EC2 instance?
In case anyone wants to mention, I understand that:
lifecycle = {
prevent_destroy = true
}
is not what I'm looking for.
Here is my current practice code.
resource "aws_vpc" "my_vpc" {
cidr_block = "xx.xx.xx.xx/24"
}
provider "aws" {
region = "us-west-2"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "web" {
ami = "${data.aws_ami.ubuntu.id}"
instance_type = "t3.nano"
vpc_security_group_ids = ["sg-0e27d851dxxxxxxxxxx"]
subnet_id = "subnet-0755c2exxxxxxxx"
tags = {
Name = "HelloWorld"
}
}
Terraform should not require you to deploy or import a VPC in order to deploy an EC2 instance into it. You should be able to reference the VPC, subnets and security groups by id so TF is aware of your existing network infrastructure just like you've already done for SGs and subnets. All you should need to deploy the EC2 instance "aws_instance" is give it an existing subnet id in the existing VPC like you already did. Why do you say deploying or importing a VPC is required by Terraform? What error or issue do you have deploying without the VPC and just using the existing one?
You can protect the VPC through AWS if you really wanted to, but I don't think you really want to import the VPC into your Terraform state and let Terraform manage it here. Sounds like you want the VPC to service other resources, maybe applications manually deployed or through other TF stacks, and the VPC to live independent of anyone application deployment.
To answer the original question, you can use a data source and match your VPC by id or tag name :
data "aws_vpc" "main" {
tags = {
Name = "main_vpc"
}
}
Or
data "aws_vpc" "main" {
id = "vpc-nnnnnnnn"
}
Then refer to it with : data.aws_vpc.main
Also, if you already included your VPC but would like not to destroy it while remove it from your state, you can manage to do it with the terraform state command : https://www.terraform.io/docs/commands/state/index.html

How to share a terraform script without module dependencies

I want to share a terraform script that will be used across different projects. I know how to create and share modules, but this setup has a big annoyance: when I reference a module in a script and perform a terraform apply, if the module resource does not exist it will be created, but also if I perform a terraform destroy this resource will be destroyed.
If I have two projects dependent on the same module, and in one of them I call a terraform destroy it may lead to a inconsistent state, since the module is being used by another project. The script can either fail because it cannot destroy the resource or it will destroy the resource and affect the other project.
In my scenario, I want to share network scripts between two projects and I want the network resources to never be destroyed. I cannot create a project only for this resource because I need to reference it somehow in my projects, and the only way to do it is via its ID, which I have no idea what is going to be.
prevent_destroy is also not an option, since I do need to destroy other resources but the shared script resource. This configuration makes terraform destroy fail.
Is there any way to reference the resource, like by its name, or is there any other better approach to accomplish what I want?
If I understand you correctly, you have some resource R that is a "singleton". That is, only one instance of R can ever exist in your AWS account. For example, you can only ever have one aws_route53_zone with the name "foo.com". If you include R as a module in two different places, then either one may create it when you run terraform apply and either one may delete it when you run terraform destroy. You'd like to avoid that, but you still need some way to get an output attribute from R (e.g. the zone_id for an aws_route53_zone resource is generated by AWS, so you can't guess it).
If that's the case, then instead of using a R as a module, you should:
Create R by itself in its own set of Terraform templates. Let's say those are under /terraform/R.
Configure /terraform/R to use Remote State. For example, here is how you can configure those templates to store their remote state in an S3 bucket (you'll need to fill in the bucket name/region as indicated):
terraform remote config \
-backend=s3 \
-backend-config="bucket=(YOUR BUCKET NAME)" \
-backend-config="key=terraform.tfstate" \
-backend-config="region=(YOUR BUCKET REGION)" \
-backend-config="encrypt=true"
Define any output attributes you need from R as output variables. For example:
output "zone_id" {
value = "${aws_route_53.example.zone_id}"
}
When you run terraform apply in /terraform/R, it will store its Terraform state, including that output, in an S3 bucket.
Now, in all other Terraform templates that need that output attribute from R, you can pull it in from the S3 bucket using the terraform_remote_state data source. For example, let's say you had some template /terraform/foo that needed that zone_id parameter to create an aws_route53_record (you'll need to fill in the bucket name/region as indicated):
data "terraform_remote_state" "r" {
backend = "s3"
config {
bucket = "(YOUR BUCKET NAME)"
key = "terraform.tfstate"
region = "(YOUR BUCKET REGION)"
}
}
resource "aws_route53_record" "www" {
zone_id = "${data.terraform_remote_state.r.zone_id}"
name = "www.foo.com"
type = "A"
ttl = "300"
records = ["${aws_eip.lb.public_ip}"]
}
Note that terraform_remote_state is a read-only data source. That means when you run terraform apply or terraform destroy on any templates that use that resource, they will not have any effect in R.
For more info, check out How to manage terraform state and Terraform: Up & Running.

How to use default AWS VPC resources when created via Terraform

Trying to learn to use Terraform (v 0.3.7) with Amazon Web Services.
When I create a VPC using Terraform via the following:
resource "aws_vpc" "test-vpc" {
cidr_block = "${var.vpc_cidr}"
enable_dns_hostnames = true
tags {
Name = "test-vpc"
}
}
The VPC will have a main routing table and a "default" security group automatically created (I assume by AWS, rather than Terraform); these can be identified by the attributes on the created VPC: main_route_table_id and default_security_group_id.
While following this tutorial it talks about creating your own default security group and routing table - it makes no mention of the default ones that will get created (even if you create your own routing table, the "main" one created by default will just remain sitting there, associated with no subnets or anything).
Shouldn't we be using the default resource that created with a VPC? Especially the routing table, will there be any effects because of not using the "main" routing table?
And if I should be using the default resources, how do I do that with Terraform?
I couldn't see anything in the Terraform documentation about these default resources, and if I try to override them (for example by telling Terraform to create a security group with name default, I get errors).
AWS creates these default routing tables and sec groups. If you don't use them ( I know we don't) they are fine to get deleted.
Terraform throws errors if you require it to create default sec group as probably the group is already there or maybe this sec group name is reserved.
You can create one new resource "aws_security_group" ( https://terraform.io/docs/providers/aws/r/security_group.html )and have a dependency listed on the resource with
depends_on = ["aws_instance.instance-name-from-resource"]
for the instance thus sec group will be created first and then assign sec groups to the instance with "security_groups"