This project is for demoing how to provision an AWS EC2 instance. The setup has the minimum requirements for a server that is both usable and accessible. You can connect to the instance using SSH and web services using port 80 and 443.
In order to use this sample setup, you will need to install the following tools.
-
Install tfenv for easier terraform version management.
Go into the official repository and follow the installation instructions, you should be able to use the command afterwards.
❯ tfenv tfenv 2.0.0-37-g0494129 Usage: tfenv <command> [<options>] Commands: install Install a specific version of Terraform use Switch a version to use uninstall Uninstall a specific version of Terraform list List all installed versions list-remote List all installable versions
-
Install latest terraform version, version 13 and up.
Version 13 and up has new features that you might not be familiar if you had used v12 and below, check the official migration document for more information.
❯ tfenv install 0.14.9 Installing Terraform v0.14.9 Downloading release tarball from https://releases.hashicorp.com/terraform/0.14.9/terraform_0.14.9_linux_amd64.zip Downloading SHA hash file from https://releases.hashicorp.com/terraform/0.14.9/terraform_0.14.9_SHA256SUMS No keybase install found, skipping OpenPGP signature verification Archive: tfenv_download.pizBai/terraform_0.14.9_linux_amd64.zip inflating: /home/megamoth/.tfenv/versions/0.14.9/terraform Installation of terraform v0.14.9 successful. To make this your default version, run 'tfenv use 0.14.9' ❯ tfenv use 0.14.9 Switching default version to v0.14.9 Switching completed
-
Install AWS CLI v2 using this guide, AWS CLI Installation
If the installation went smoothly, you should see this output with using the command for the first time.
❯ aws usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters] To see help text, you can run: aws help aws <command> help aws <command> <subcommand> help aws: error: too few arguments
-
Setup your assigned AWS credentials.
Since our infrastructure is in AWS this needed for us to be able to apply terraform definitions into the infrastructure.
There will be designated developers that has access and not everyone can apply updates.
❯ aws configure AWS Access Key ID []: **************** AWS Secret Access Key []: **************** Default region name []: api-east-1 Default output format []: json
-
Test out terraform setup.
Run initialize command and see if it has no issues.
❯ terraform init Initializing modules... Initializing the backend... Initializing provider plugins... - Using previously-installed hashicorp/template v2.1.2 - Using previously-installed hashicorp/aws v2.70.0 ... Terraform has been successfully initialized!
-
Create the terraform S3 bucket.
Our Terraform project uses S3 as the tfstate management provider. Everytime we provision, update and delete resources, Terraform saves the latest infrastructure state into a JSON file, and this file will be stored in S3.
❯ aws s3api create-bucket --bucket iac-demo-terraform --region ap-southeast-1 --create-bucket-configuration LocationConstraint=ap-southeast-1
The project is straightforward, if you are new to terraform and AWS, you should check first how to create an AWS account and create your own AWS access keys.
To get your first EC2 up and running, feel free to run the commands, this is assuming that you had finish the installation instructions.
❯ terraform init
Initializing the backend...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Installing hashicorp/aws v3.31.0...
- Installed hashicorp/aws v3.31.0 (signed by HashiCorp)
Terraform has been successfully initialized!
...
❯ terraform apply -var-file global.tfvars
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_instance.personal_server will be created
+ resource "aws_instance" "personal_server" {
+ ami = "ami-048b4b1ddefe6759f"
+ arn = (known after apply)
+ associate_public_ip_address = true
+ availability_zone = (known after apply)
+ cpu_core_count = (known after apply)
+ cpu_threads_per_core = (known after apply)
+ get_password_data = false
+ host_id = (known after apply)
+ id = (known after apply)
+ instance_state = (known after apply)
+ instance_type = "t2.micro"
+ ipv6_address_count = (known after apply)
+ ipv6_addresses = (known after apply)
+ key_name = "aws-eb-hov"
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
+ primary_network_interface_id = (known after apply)
+ private_dns = (known after apply)
+ private_ip = (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
+ secondary_private_ips = (known after apply)
+ security_groups = (known after apply)
+ source_dest_check = true
+ subnet_id = (known after apply)
+ tags = {
+ "Name" = "personal-server"
}
+ tenancy = (known after apply)
+ vpc_security_group_ids = (known after apply)
+ ebs_block_device {
+ delete_on_termination = true
+ device_name = "/dev/sda1"
+ encrypted = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ snapshot_id = (known after apply)
+ throughput = (known after apply)
+ volume_id = (known after apply)
+ volume_size = 20
+ volume_type = "gp3"
}
+ enclave_options {
+ enabled = (known after apply)
}
+ ephemeral_block_device {
+ device_name = (known after apply)
+ no_device = (known after apply)
+ virtual_name = (known after apply)
}
+ metadata_options {
+ http_endpoint = (known after apply)
+ http_put_response_hop_limit = (known after apply)
+ http_tokens = (known after apply)
}
+ network_interface {
+ delete_on_termination = (known after apply)
+ device_index = (known after apply)
+ network_interface_id = (known after apply)
}
+ root_block_device {
+ delete_on_termination = (known after apply)
+ device_name = (known after apply)
+ encrypted = (known after apply)
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ tags = (known after apply)
+ throughput = (known after apply)
+ volume_id = (known after apply)
+ volume_size = (known after apply)
+ volume_type = (known after apply)
}
}
# aws_security_group.firewall will be created
+ resource "aws_security_group" "firewall" {
+ arn = (known after apply)
+ description = "Managed by Terraform"
+ egress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = ""
+ from_port = 0
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "-1"
+ security_groups = []
+ self = false
+ to_port = 0
},
]
+ id = (known after apply)
+ ingress = [
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = "ssh traffic"
+ from_port = 22
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 22
},
+ {
+ cidr_blocks = [
+ "0.0.0.0/0",
]
+ description = "web traffic"
+ from_port = 80
+ ipv6_cidr_blocks = []
+ prefix_list_ids = []
+ protocol = "tcp"
+ security_groups = []
+ self = false
+ to_port = 80
},
]
+ name = "firewall"
+ name_prefix = (known after apply)
+ owner_id = (known after apply)
+ revoke_rules_on_delete = false
+ vpc_id = (known after apply)
}
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Hit yes
and terraform will create your first EC2 server in AWS.
CloudFormation is an AWS in-house tool for creating infrastructure stacks, it occupies the same problem space with Terraform, the difference is it only supports AWS but has the latest features in AWS. This Terraform project will create a CloudFormation stack, you can check the stack details in the CloudFormation console.
❯ terraform apply -var-file global.tfvars
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_cloudformation_stack.stack will be created
+ resource "aws_cloudformation_stack" "stack" {
+ capabilities = [
+ "CAPABILITY_IAM",
+ "CAPABILITY_NAMED_IAM",
]
+ id = (known after apply)
+ name = "PersonalEC2Server"
+ outputs = (known after apply)
+ parameters = (known after apply)
+ policy_body = (known after apply)
+ template_body = <<-EOT
AWSTemplateFormatVersion : "2010-09-09"
Description: "Your virtual personal server"
Resources:
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupName: "ec2_firewall"
GroupDescription: "your personal server firewall"
VpcId: "vpc-c6f987a2"
SecurityGroupIngress:
- CidrIp: "0.0.0.0/0"
FromPort: 22
ToPort: 22
IpProtocol: "tcp"
- CidrIp: "0.0.0.0/0"
FromPort: 80
ToPort: 80
IpProtocol: "tcp"
- CidrIp: "0.0.0.0/0"
FromPort: 443
ToPort: 443
IpProtocol: "tcp"
SecurityGroupEgress:
- CidrIp: "0.0.0.0/0"
FromPort: "-1"
ToPort: "-1"
IpProtocol: "-1"
EC2:
Type: "AWS::EC2::Instance"
Properties:
ImageId: "ami-01581ffba3821cdf3"
InstanceType: "t3.small" # 2 vCPU, 2GB memory
BlockDeviceMappings:
- DeviceName: "/dev/sda1"
Ebs:
DeleteOnTermination: true
Encrypted: false
Iops: 3000
VolumeSize: 50
VolumeType: "gp3"
SecurityGroupIds:
- !Ref SecurityGroup
EOT
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
CloudFormation also support reusable infrastructure functions. We can reuse definitions to minimize the stack size and to abstract complex infrastructure patterns.
❯ cfn init
Initializing new project
Do you want to develop a new resource(r) or a module(m)?.
>> m
What's the name of your module type?
(<Organization>::<Service>::<Name>::MODULE)
>> HOV::S3::Bucket::MODULE
Directory /home/megamoth/highoutput/cloudformation/fragments Created
Initialized a new project in /home/megamoth/highoutput/cloudformation
❯ cfn submit
Module fragment is valid.
Successfully submitted type. Waiting for registration with token '7f3b6fa6-1ed9-46d3-8e68-393b7c8c1cb1' to complete.
Registration complete.
{'ProgressStatus': 'COMPLETE', 'Description': 'Deployment is currently in DEPLOY_STAGE of status COMPLETED; ', 'TypeArn': 'arn:aws:cloudformation:ap-southeast-1:498457837717:type/module/HOV-S3-Bucket-MODULE', 'TypeVersionArn': 'arn:aws:cloudformation:ap-southeast-1:498457837717:type/module/HOV-S3-Bucket-MODULE/00000001', 'ResponseMetadata': {'RequestId': 'c26548b2-cd84-49aa-8515-3d4008d4a44f', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': 'c26548b2-cd84-49aa-8515-3d4008d4a44f', 'content-type': 'text/xml', 'content-length': '685', 'date': 'Fri, 26 Mar 2021 01:00:32 GMT'}, 'RetryAttempts': 0}}
❯ aws cloudformation describe-type --type MODULE --type-name HOV::S3::Bucket::MODULE
{
"Arn": "arn:aws:cloudformation:ap-southeast-1:498457837717:type/module/HOV-S3-Bucket-MODULE/00000001",
"Type": "MODULE",
"TypeName": "HOV::S3::Bucket::MODULE",
"DefaultVersionId": "00000001",
"IsDefaultVersion": true,
"Description": "Schema for Module Fragment of type HOV::S3::Bucket::MODULE",
"Schema": "{\n \"typeName\": \"HOV::S3::Bucket::MODULE\",\n \"description\": \"Schema for Module Fragment of type HOV::S3::Bucket::MODULE\",\n \"properties\": {\n \"Parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"BucketName\": {\n \"type\": \"object\",\n \"properties\": {\n \"Type\": {\n \"type\": \"string\"\n },\n \"Description\": {\n \"type\": \"string\"\n
}\n },\n \"required\": [\n \"Type\",\n \"Description\"\n ],\n \"description\": \"Name for the bucket\"\n }\n }\n },\n \"Resources\": {\n \"properties\": {\n \"S3Bucket\": {\n \"type\": \"object\",\n \"properties\": {\n
\"Type\": {\n \"type\": \"string\",\n \"const\": \"AWS::S3::Bucket\"\n
},\n \"Properties\": {\n \"type\": \"object\"\n }\n
}\n }\n },\n \"type\": \"object\",\n \"additionalProperties\": false\n }\n },\n \"additionalProperties\": true\n}\n",
"DeprecatedStatus": "LIVE",
"Visibility": "PRIVATE",
"LastUpdated": "2021-03-26T01:00:04.586000+00:00",
"TimeCreated": "2021-03-26T01:00:04.586000+00:00"
}