├── .gitignore ├── LEARNING-LOG.md ├── LICENSE ├── README.md └── src ├── free-tier ├── backend │ └── example.config.tf ├── main.tf ├── provision │ └── access │ │ └── .gitkeep └── variables.tf └── modules ├── ec2 ├── main.tf ├── outputs.tf └── variables.tf ├── internet-gateway ├── main.tf ├── outputs.tf └── variables.tf ├── public-subnet ├── main.tf ├── outputs.tf └── variables.tf ├── route-table ├── main.tf ├── outputs.tf └── variables.tf └── vpc ├── main.tf ├── outputs.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .terraform/ 3 | 4 | **/backend/config.tf 5 | **/provision/access/free-tier-ec2-key* -------------------------------------------------------------------------------- /LEARNING-LOG.md: -------------------------------------------------------------------------------- 1 | #### 2020, Sat, Jan 11 2 | 3 | 1. `Public Subnet`, `IGW`, `Route Table, EC2` modules working: 4 | * Added `Public Subnet`, `IGW`, `Route Table, EC2` modules 5 | * Tried to apply and destroy `VPC`, `Public Subnet`, `IGW`, `Route Table, EC2` infrastructure 6 | 7 | #### 2020, Fri, Jan 10 8 | 9 | 1. Learning of the Terraform: 10 | * Configuring S3 backend to store Terraform state 11 | * Using ` -backend-config=path/to/config` key in `init` command. This key gives the ability to not show backend config in the repository 12 | 13 | 2. `VPC module` working: 14 | * Added `VPC module` 15 | * Tried to apply and destroy `VPC` infrastructure 16 | 17 | #### 2020, Thu, Jan 9 18 | 19 | 1. Terraform binary has been installed: 20 | * [Download Terraform](https://www.terraform.io/downloads.html) 21 | * How to make the `terrafrom` binary available on the `PATH`: 22 | 1. Add `export PATH="$PATH:$HOME/path/to/terrafrom/binary/directory"` to `~/.bashrc` 23 | 2. Update `PATH` for the remainder of the session - `source ~/.bashrc` 24 | 25 | 2. The Terraform IAM user and group have been created: 26 | * Terraform User is in a Terraform Group 27 | * Group has `AdministratorAccess` policy 28 | * User has Sign-In credentials and Access Keys -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pavel Varentsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Terraform AWS Free Tier 2 | 3 | > Getting started with the Terraform for managing a base free-tier AWS resources. 4 | 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](./LICENSE) 6 | 7 | ### Project description 8 | 9 | This is a [Terraform](https://www.terraform.io/) project for managing AWS resources. 10 | 11 | It can build the next infrastructure: 12 | 13 | * [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html) 14 | * Public [Subnet](https://docs.aws.amazon.com/vpc/latest/userguide/working-with-vpcs.html#AddaSubnet) in the `VPC` 15 | * [IGW](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html) to enable access to or from the Internet for `VPC` 16 | * [Route Table](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html) to associate `IGW`, `VPC` and `Subnet` 17 | * [EC2 Instance](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html) in the public `Subnet` with the HTTP(s) & SSH access 18 | 19 | ### Pre steps 20 | 21 | 1. [Install Terraform](https://learn.hashicorp.com/terraform/getting-started/install.html) 22 | 2. Create AWS account 23 | 3. If the file `~/.aws/credentials` does't exist, create it and add you Terraform profile to the file. For example: 24 | ```text 25 | [terraform] 26 | aws_access_key_id = Your access key 27 | aws_secret_access_key = Your secret access key 28 | ``` 29 | 4. Create S3 bucket to store Terraform state 30 | 5. Create config file `./src/free-tier/backend/config.tf` that will contain information how to store state in a given bucket. See [example](./src/free-tier/backend/example.config.tf). 31 | 6. Create SSH key pair to connect to EC2 instance: 32 | ```bash 33 | cd ./src/free-tier/provision/access 34 | 35 | # it creates "free-tier-ec2-key" private key and "free-tier-ec2-key.pub" public key 36 | ssh-keygen -f free-tier-ec2-key 37 | ``` 38 | 39 | ### Build infrastructure 40 | 41 | 1. `cd ./src/free-tier` 42 | 2. `terraform init -backend-config="./backend/config.tf"` 43 | 3. `terraform plan` 44 | 4. `terraform apply` 45 | 46 | ### Post steps 47 | 48 | After building the infrastructure you can try to connect to you `EC2 instance` via SSH: 49 | 50 | 1. `cd ./src/free-tier` 51 | 2. `ssh -i ./provision/access/free-tier-ec2-key ubuntu@[EC2 public IP]` 52 | 53 | To check HTTP access you can install `apache2` on your EC2 instance: 54 | 55 | 1. `sudo apt update && sudo apt install apache2` (on EC2 machine) 56 | 2. `sudo service apache2 start` (on EC2 machine) 57 | 3. Check in browser: `http://[EC2 public IP]/`. You can see `Apache2 Default Page` (something like [this](https://annex.exploratorium.edu/)) 58 | 59 | To destroy infrastructure: 60 | 61 | 1. `cd ./src/free-tier` 62 | 2. `terraform destroy` 63 | 64 | ### Similar projects 65 | 66 | * https://github.com/rogeriomm/aws-lab 67 | -------------------------------------------------------------------------------- /src/free-tier/backend/example.config.tf: -------------------------------------------------------------------------------- 1 | region = "us-east-1" 2 | bucket = "backet_name" 3 | key = "terraform.tfstate" 4 | profile = "terraform" 5 | -------------------------------------------------------------------------------- /src/free-tier/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = var.profile 3 | region = var.region 4 | } 5 | 6 | terraform { 7 | backend "s3" {} 8 | } 9 | 10 | module "vpc" { 11 | source = "../modules/vpc" 12 | } 13 | 14 | module "public_subnet" { 15 | source = "../modules/public-subnet" 16 | 17 | vpc_id = module.vpc.vpc_id 18 | } 19 | 20 | module "internet_gateway" { 21 | source = "../modules/internet-gateway" 22 | 23 | vpc_id = module.vpc.vpc_id 24 | } 25 | 26 | module "route_table" { 27 | source = "../modules/route-table" 28 | 29 | vpc_id = module.vpc.vpc_id 30 | internet_gateway_id = module.internet_gateway.internet_gateway_id 31 | public_subnet_id = module.public_subnet.public_subnet_id 32 | } 33 | 34 | module "ec2" { 35 | source = "../modules/ec2" 36 | 37 | vpc_id = module.vpc.vpc_id 38 | public_subnet_id = module.public_subnet.public_subnet_id 39 | 40 | ec2_ssh_key_name = var.ec2_ssh_key_name 41 | ec2_ssh_public_key_path = var.ec2_ssh_public_key_path 42 | } -------------------------------------------------------------------------------- /src/free-tier/provision/access/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pvarentsov/terraform-aws-free-tier/23b65326a55f39764ae72a44527bfe90a6efe4e0/src/free-tier/provision/access/.gitkeep -------------------------------------------------------------------------------- /src/free-tier/variables.tf: -------------------------------------------------------------------------------- 1 | variable "profile" { 2 | description = "AWS Profile" 3 | type = string 4 | default = "terraform" 5 | } 6 | 7 | variable "region" { 8 | description = "Region for AWS resources" 9 | type = string 10 | default = "us-east-1" 11 | } 12 | 13 | variable "ec2_ssh_key_name" { 14 | description = "The SSH Key Name" 15 | type = string 16 | default = "free-tier-ec2-key" 17 | } 18 | 19 | variable "ec2_ssh_public_key_path" { 20 | description = "The local path to the SSH Public Key" 21 | type = string 22 | default = "./provision/access/free-tier-ec2-key.pub" 23 | } 24 | -------------------------------------------------------------------------------- /src/modules/ec2/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_instance" "ec2" { 2 | count = var.ec2_should_be_created ? 1 : 0 3 | 4 | ami = var.ec2_ami 5 | instance_type = var.ec2_instance_type 6 | 7 | subnet_id = var.public_subnet_id 8 | vpc_security_group_ids = [aws_security_group.ec2_security_group.id] 9 | associate_public_ip_address = true 10 | 11 | key_name = aws_key_pair.ec2_key_pair.key_name 12 | 13 | tags = { 14 | Name = var.ec2_name 15 | } 16 | } 17 | 18 | resource "aws_security_group" "ec2_security_group" { 19 | name = var.ec2_security_group_name 20 | description = var.ec2_security_group_description 21 | 22 | vpc_id = var.vpc_id 23 | 24 | ingress { 25 | from_port = 22 26 | to_port = 22 27 | protocol = "tcp" 28 | cidr_blocks = ["0.0.0.0/0"] 29 | } 30 | 31 | ingress { 32 | from_port = 80 33 | to_port = 80 34 | protocol = "tcp" 35 | cidr_blocks = ["0.0.0.0/0"] 36 | } 37 | 38 | ingress { 39 | from_port = 443 40 | to_port = 443 41 | protocol = "tcp" 42 | cidr_blocks = ["0.0.0.0/0"] 43 | } 44 | 45 | egress { 46 | from_port = 0 47 | to_port = 0 48 | protocol = "-1" 49 | cidr_blocks = ["0.0.0.0/0"] 50 | } 51 | 52 | tags = { 53 | Name = var.ec2_security_group_name 54 | } 55 | } 56 | 57 | resource "aws_key_pair" "ec2_key_pair" { 58 | key_name = var.ec2_ssh_key_name 59 | public_key = file(var.ec2_ssh_public_key_path) 60 | } 61 | -------------------------------------------------------------------------------- /src/modules/ec2/outputs.tf: -------------------------------------------------------------------------------- 1 | output "ec2_id" { 2 | description = "The ID of the EC2" 3 | value = concat(aws_instance.ec2.*.id, [""])[0] 4 | } 5 | 6 | output "ec2_arn" { 7 | description = "The ARN of the EC2" 8 | value = concat(aws_instance.ec2.*.arn, [""])[0] 9 | } -------------------------------------------------------------------------------- /src/modules/ec2/variables.tf: -------------------------------------------------------------------------------- 1 | variable "ec2_should_be_created" { 2 | description = "Should the EC2 be created?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "ec2_name" { 8 | description = "The Name of the EC2" 9 | type = string 10 | default = "Free Tier EC2" 11 | } 12 | 13 | variable "ec2_security_group_name" { 14 | description = "The Name of the EC2 Security Group" 15 | type = string 16 | default = "Free Tier EC2 Security Group" 17 | } 18 | 19 | variable "ec2_security_group_description" { 20 | description = "The Description of the EC2 Security Group" 21 | type = string 22 | default = "Free Tier EC2 Security Group" 23 | } 24 | 25 | variable "ec2_ami" { 26 | description = "The Amazon Machine Image" 27 | type = string 28 | default = "ami-04b9e92b5572fa0d1" # Ubuntu 18.04 LTS (64-bit x86) Free Tier eligible 29 | } 30 | 31 | variable "ec2_instance_type" { 32 | description = "The EC2 Instance type" 33 | type = string 34 | default = "t2.micro" # Free Tier eligible 35 | } 36 | 37 | variable "vpc_id" { 38 | description = "The ID of the VPC" 39 | type = string 40 | } 41 | 42 | variable "public_subnet_id" { 43 | description = "The ID of the Public Subnet" 44 | type = string 45 | } 46 | 47 | variable "ec2_ssh_key_name" { 48 | description = "The SSH Key Name" 49 | type = string 50 | default = "free-tier-ec2-key" 51 | } 52 | 53 | variable "ec2_ssh_public_key_path" { 54 | description = "The local path to the SSH Public Key" 55 | type = string 56 | } 57 | -------------------------------------------------------------------------------- /src/modules/internet-gateway/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_internet_gateway" "internet_gateway" { 2 | count = var.internet_gateway_should_be_created ? 1 : 0 3 | vpc_id = var.vpc_id 4 | 5 | tags = { 6 | Name = var.internet_gateway_name 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/internet-gateway/outputs.tf: -------------------------------------------------------------------------------- 1 | output "internet_gateway_id" { 2 | description = "The ID of the Internet Gateway" 3 | value = concat(aws_internet_gateway.internet_gateway.*.id, [""])[0] 4 | } 5 | -------------------------------------------------------------------------------- /src/modules/internet-gateway/variables.tf: -------------------------------------------------------------------------------- 1 | variable "internet_gateway_should_be_created" { 2 | description = "Should the Internet Gateway be created?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "internet_gateway_name" { 8 | description = "The Name of the Internet Gateway" 9 | type = string 10 | default = "Free Tier Internet Gateway" 11 | } 12 | 13 | variable "vpc_id" { 14 | description = "The ID of the VPC" 15 | type = string 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/public-subnet/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_subnet" "public_subnet" { 2 | count = var.subnet_should_be_created ? 1 : 0 3 | vpc_id = var.vpc_id 4 | cidr_block = var.subnet_cidr_block 5 | availability_zone = var.subnet_availability_zone 6 | map_public_ip_on_launch = true 7 | 8 | tags = { 9 | Name = var.subnet_name 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/modules/public-subnet/outputs.tf: -------------------------------------------------------------------------------- 1 | output "public_subnet_id" { 2 | description = "The ID of the Public Subnet" 3 | value = concat(aws_subnet.public_subnet.*.id, [""])[0] 4 | } 5 | 6 | output "public_subnet_arn" { 7 | description = "The ARN of the Public Subnet" 8 | value = concat(aws_subnet.public_subnet.*.arn, [""])[0] 9 | } -------------------------------------------------------------------------------- /src/modules/public-subnet/variables.tf: -------------------------------------------------------------------------------- 1 | variable "subnet_should_be_created" { 2 | description = "Should the Public Subnet be created?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "subnet_name" { 8 | description = "The Name of the Public Subnet" 9 | type = string 10 | default = "Free Tier Public Subnet" 11 | } 12 | 13 | variable "subnet_cidr_block" { 14 | description = "The IPv4 CIDR block of the Public Subnet" 15 | type = string 16 | default = "10.0.1.0/24" 17 | } 18 | 19 | variable "subnet_availability_zone" { 20 | description = "The Availability Zone of the Public Subnet" 21 | type = string 22 | default = "us-east-1a" 23 | } 24 | 25 | variable "vpc_id" { 26 | description = "The ID of the VPC" 27 | type = string 28 | } 29 | -------------------------------------------------------------------------------- /src/modules/route-table/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_route_table" "route_table" { 2 | count = var.route_table_should_be_created ? 1 : 0 3 | vpc_id = var.vpc_id 4 | 5 | route { 6 | cidr_block = "0.0.0.0/0" 7 | gateway_id = var.internet_gateway_id 8 | } 9 | 10 | tags = { 11 | Name = var.route_table_name 12 | } 13 | } 14 | 15 | resource "aws_route_table_association" route_table_association { 16 | subnet_id = var.public_subnet_id 17 | route_table_id = concat(aws_route_table.route_table.*.id, [""])[0] 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/modules/route-table/outputs.tf: -------------------------------------------------------------------------------- 1 | output "route_table_id" { 2 | description = "The ID of the Route Table" 3 | value = concat(aws_route_table.route_table.*.id, [""])[0] 4 | } 5 | -------------------------------------------------------------------------------- /src/modules/route-table/variables.tf: -------------------------------------------------------------------------------- 1 | variable "route_table_should_be_created" { 2 | description = "Should the Route Table be created?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "route_table_name" { 8 | description = "The Name of the Route Table" 9 | type = string 10 | default = "Free Tier Route Table" 11 | } 12 | 13 | variable "vpc_id" { 14 | description = "The ID of the VPC" 15 | type = string 16 | } 17 | 18 | variable "internet_gateway_id" { 19 | description = "The ID of the Internet Gateway" 20 | type = string 21 | } 22 | 23 | variable "public_subnet_id" { 24 | description = "The ID of the Public Subnet" 25 | type = string 26 | } 27 | -------------------------------------------------------------------------------- /src/modules/vpc/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_vpc" "vpc" { 2 | count = var.vpc_should_be_created ? 1 : 0 3 | cidr_block = var.vpc_cidr_block 4 | enable_dns_hostnames = var.vpc_enable_dns_hostnames 5 | enable_dns_support = var.vpc_enable_dns_support 6 | 7 | tags = { 8 | Name = var.vpc_name 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/modules/vpc/outputs.tf: -------------------------------------------------------------------------------- 1 | output "vpc_id" { 2 | description = "The ID of the VPC" 3 | value = concat(aws_vpc.vpc.*.id, [""])[0] 4 | } 5 | 6 | output "vpc_arn" { 7 | description = "The ARN of the VPC" 8 | value = concat(aws_vpc.vpc.*.arn, [""])[0] 9 | } 10 | 11 | output "vpc_cidr_block" { 12 | description = "The CIDR block of the VPC" 13 | value = concat(aws_vpc.vpc.*.cidr_block, [""])[0] 14 | } -------------------------------------------------------------------------------- /src/modules/vpc/variables.tf: -------------------------------------------------------------------------------- 1 | variable "vpc_should_be_created" { 2 | description = "Should the VPC be created?" 3 | type = bool 4 | default = true 5 | } 6 | 7 | variable "vpc_name" { 8 | description = "The Name of the VPC" 9 | type = string 10 | default = "Free Tier VPC" 11 | } 12 | 13 | variable "vpc_cidr_block" { 14 | description = "The IPv4 CIDR block of the VPC" 15 | type = string 16 | default = "10.0.0.0/16" 17 | } 18 | 19 | variable "vpc_enable_dns_hostnames" { 20 | description = "Should instances in the VPC get public DNS hostnames?" 21 | type = bool 22 | default = true 23 | } 24 | 25 | variable "vpc_enable_dns_support" { 26 | description = "Should the DNS resolution be supported?" 27 | type = bool 28 | default = true 29 | } 30 | --------------------------------------------------------------------------------