Terraform
Define infrastructure with Terraform resources
Terraform uses resource
blocks to manage infrastructure, such as virtual
networks, compute instances, or higher-level components such as DNS records.
Resource blocks represent one or more infrastructure objects in your Terraform
configuration.
Most Terraform providers have a number of different resources that map to the appropriate APIs to manage that particular infrastructure type.
Generic | AWS Provider Resource | AWS Infrastructure |
---|---|---|
Resource A | aws_instance | EC2 Instance |
Resource B | aws_security_group | Security Group |
In this tutorial, you will create an EC2 instance that runs a PHP web application. You will then refer to documentation in the Terraform Registry to create a security group to make the application publicly accessible.
Prerequisites
You can complete this tutorial using the same workflow with either Terraform Community Edition or HCP Terraform. HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more.
Select the HCP Terraform tab to complete this tutorial using HCP Terraform.
This tutorial assumes that you are familiar with the Terraform workflow. If you are new to Terraform, complete the Get Started collection first.
In order to complete this tutorial, you will need the following:
- Terraform v1.2+ installed locally.
- An AWS account with local credentials configured for use with Terraform.
Clone the example repository
Clone the Learn Terraform Resources repository, which contains example configuration to provision an AWS EC2 instance.
$ git clone https://github.com/hashicorp-education/learn-terraform-resources
Navigate to the repository directory in your terminal.
$ cd learn-terraform-resources
There are five files in this directory:
init-script.sh
contains the provisioning script to install dependencies and start a sample PHP applicationterraform.tf
contains theterraform
block that defines the providers required by your configurationmain.tf
contains the configuration for an EC2 instanceoutputs.tf
contains the definitions for the output values of your resourcesREADME.md
describes the repository and its contents
Open main.tf
to review the two resource blocks: random_pet.name
and aws_instance.web
.
Review the random_pet
resource
The first resource block defines a random_pet
resource named name
, which
generates a random pet name. You can use the name generated by this resource to
ensure that your other resources have unique names.
resource "random_pet" "name" {}
Resource blocks declare a resource type and name. Together, the type and name
form a resource identifier (ID) in the format resource_type.resource_name
, in
this case random_pet.name
. The resource's ID must be unique within a
workspace. When Terraform displays information about this resource in its
output it will use the resource ID.
Resource types always start with the provider name followed by an underscore.
The random_pet
resource type belongs to the random
provider.
The Terraform Registry houses the documentation for Terraform providers and their associated resources. Open the random_pet
documentation page and notice that it is nested under the documentation for the random
provider. The page contains a description of the random_pet
resource, an example usage, argument reference, and attribute reference.
Resources have arguments, attributes, and meta-arguments.
- Arguments configure a particular resource; because of this, many arguments are resource-specific. Arguments can be
required
oroptional
, as specified by the provider. If you do not supply a required argument, Terraform will give an error and not apply the configuration. - Attributes are values exposed by an existing resource. References to resource attributes take the format
resource_type.resource_name.attribute_name
. Unlike arguments which specify an infrastructure object's configuration, a resource's attributes are often assigned to it by the underlying cloud provider or API. - Meta-arguments change a resource's behavior, such as using a
count
meta-argument to create multiple resources. Meta-arguments are a function of Terraform itself and are not resource or provider-specific.
The random_pet
resource has four optional arguments and exposes one attribute. Because there are no required arguments, you can define the random_pet.name
resource without arguments.
Review the EC2 instance resource
The aws_instance.web
resource block defines an aws_instance
resource named web
to create an AWS EC2 instance.
resource "aws_instance" "web" {
ami = "ami-a0cfeed8"
instance_type = "t2.micro"
user_data = file("init-script.sh")
tags = {
Name = random_pet.name.id
}
}
The arguments inside the aws_instance.web
resource block specify what type of resource to create.
The
instance_type
andami
arguments tell the AWS provider to create at2.micro
EC2 instance using theami-a0cfeed8
machine image.Note
If you use a different AMI, you may need to update the
init-script.sh
. Connect to your EC2 instance and review/var/log/cloud-init-output.log
to debug any errors.The
user_data
argument uses thefile()
function to return the contents ofinit-script.sh
.The
tags
argument specifies this EC2 instance's name. Notice that the argument references therandom_pet.name
's ID attribute (random_pet.name.id
) to give the EC2 instance a unique name. This defines an implicit dependency between the EC2 instance and therandom_pet
resource; Terraform cannot create the instance until it has a name for it.
These are only a subset of the available aws_instances
arguments. Refer to the aws_instance
documentation page for a complete list.
Create infrastructure
Initialize this configuration.
$ terraform init
Initializing the backend...
##...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Apply your configuration to create the two resources. Confirm the operation with a yes
.
$ terraform apply
## ...
Plan: 2 to add, 0 to change, 0 to destroy.
## ...
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
application-url = "ec2-18-236-123-132.us-west-2.compute.amazonaws.com/index.php"
domain-name = "ec2-18-236-123-132.us-west-2.compute.amazonaws.com"
The domain-name
output will display your EC2 instance's endpoint upon completion. However, if you try to visit the URL, it will not resolve. This is because you have not yet configured access to port 80
of the instance.
Tip
This tutorial shows the output for Terraform commands run with Terraform Community Edition. If you are following the HCP Terraform workflow, the output may differ slightly but the results will be the same.
If you use HCP Terraform to provision your resources, your workspace now displays the list of all of the resources it manages and the outputs for your configuration.
Associate security group with instance
To enable access to the EC2 instance's web server, you must define a security group that allows ingress traffic on port 80
and all egress traffic, and associate the security group with your instance.
Open the AWS Provider documentation page. Search for security_group
and select the aws_security_group
resource.
Review the configuration options available on the aws_security_group
documentation
page.
Then, define a new aws_security_group
resource named web-sg
in main.tf
that allows ingress traffic on port 80
and all egress traffic for all CIDR blocks.
Your security group resource should be similar to the following:
resource "aws_security_group" "web-sg" {
name = "${random_pet.name.id}-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
Then, update your aws_instance.web
resource to use this security group. As
specified in the aws_instance
Argument Reference
section,
thevpc_security_group_ids
argument requires a list of security group IDs.
Add the vpc_security_group_ids
argument to the aws_instance.web
resource as a list by placing the aws_security_group.web-sg.id
attribute inside square brackets.
resource "aws_instance" "web" {
ami = "ami-a0cfeed8"
instance_type = "t2.micro"
user_data = file("init-script.sh")
+ vpc_security_group_ids = [aws_security_group.web-sg.id]
tags = {
Name = random_pet.name.id
}
}
Save your changes.
Next, apply your configuration. Remember to confirm your apply with a yes
.
$ terraform apply
## ...
Apply complete! Resources: 1 added, 1 changed, 0 destroyed.
Outputs:
application-url = "ec2-18-236-123-132.us-west-2.compute.amazonaws.com/index.php"
domain-name = "ec2-18-236-123-132.us-west-2.compute.amazonaws.com"
Verify that your EC2 instance is now publicly accessible.
In your browser, visit the application-url
address from Terraform's output to view the PHP application. The address should start with http
, not https
.
To view your application-url
output again, run the following command.
$ terraform output application-url
"ec2-18-236-123-132.us-west-2.compute.amazonaws.com/index.php"
It may take about 10 minutes for the EC2 instance to completely deploy the PHP application. If you receive a ... took too long to respond.
message, please wait a few minutes before trying again.
Clean up your infrastructure
Now that you have verified that the EC2 instance is publicly available, run
terraform destroy
to destroy the resources. Remember to respond to the
confirmation prompt with yes
.
$ terraform destroy
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
##...
Plan: 0 to add, 0 to change, 3 to destroy.
Changes to Outputs:
- application-url = "ec2-18-236-123-132.us-west-2.compute.amazonaws.com/index.php"
- domain-name = "ec2-18-236-123-132.us-west-2.compute.amazonaws.com"
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
aws_security_group.web-sg: Destroying... [id=sg-0cd7b5ebfafbe3d7f]
aws_instance.web: Destroying... [id=i-0798dc10162db25f3]
aws_security_group.web-sg: Destruction complete after 1s
aws_instance.web: Still destroying... [id=i-0798dc10162db25f3, 10s elapsed]
aws_instance.web: Still destroying... [id=i-0798dc10162db25f3, 20s elapsed]
aws_instance.web: Still destroying... [id=i-0798dc10162db25f3, 30s elapsed]
aws_instance.web: Still destroying... [id=i-0798dc10162db25f3, 40s elapsed]
aws_instance.web: Destruction complete after 42s
random_pet.name: Destroying... [id=pleasing-swine]
random_pet.name: Destruction complete after 0s
Destroy complete! Resources: 3 destroyed.
If you used HCP Terraform for this tutorial, after destroying your resources, delete the learn-terraform-resource
workspace from your HCP Terraform organization.
Next steps
In this tutorial, you made an EC2 instance publicly available by referencing the Terraform Registry to define a security group. You also reviewed resource arguments and attributes and defined a dependency between resources.
To learn more about the Terraform configuration language, check out the following resources:
- Learn more about resource dependencies in the Create Resource Dependencies tutorial.
- Review the Perform Dynamic Operations with Functions and Create Dynamic Expressions tutorials.
- Review the difference between resource arguments, attributes, and meta-arguments in the Terraform documentation.