├── credentials.tf.example ├── .gitignore ├── outputs.tf ├── vars.tf ├── README.md └── main.tf /credentials.tf.example: -------------------------------------------------------------------------------- 1 | variable "acc_key" { 2 | description = "Your access key" 3 | default = "" 4 | } 5 | 6 | variable "priv_key" { 7 | description = "Your private key. Keep it secret. Keep it safe." 8 | default = "" 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore state files 2 | terraform.tfstate* 3 | 4 | # Ignore static credentials file (In the future, these shouldn't be necessary.) 5 | credentials.tf 6 | 7 | # Ignore temporary lockstate file, in case it gets left behind 8 | .terraform.tfstate.lock.info 9 | 10 | # Ignore plugins directory, these should be created locally 11 | .terraform/ -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "ips" { 2 | value = ["${ 3 | formatlist( 4 | "Jump instance %s has public elastic IP %s 5 | ", 6 | aws_instance.jump.*.tags.Name, 7 | aws_instance.jump.*.public_ip 8 | ) 9 | }", 10 | "${ 11 | formatlist( 12 | "Instance %s has the following addreses 13 | Public: %s 14 | Private: %s 15 | ", 16 | aws_instance.centos7.*.tags.Name, 17 | aws_instance.centos7.*.public_ip, 18 | aws_instance.centos7.*.private_ip 19 | ) 20 | }", 21 | "${ 22 | formatlist( 23 | "Instance %s has the following addreses 24 | Public: %s 25 | Private: %s 26 | ", 27 | aws_instance.centos6.*.tags.Name, 28 | aws_instance.centos6.*.public_ip, 29 | aws_instance.centos6.*.private_ip 30 | ) 31 | }"] 32 | } 33 | -------------------------------------------------------------------------------- /vars.tf: -------------------------------------------------------------------------------- 1 | ################# 2 | ### Variables ### 3 | ################# 4 | 5 | 6 | ############### 7 | ### Globals ### 8 | ############### 9 | 10 | locals { 11 | my_tags = { 12 | # Set this so we know who owns resources, plz! 13 | who = "${var.whoami}" 14 | created = "terraformed" 15 | } 16 | } 17 | 18 | variable "whoami" { 19 | description = "But really, who *AM* I?" 20 | default = "" 21 | } 22 | 23 | variable "region" { 24 | description = "The region for your instance." 25 | default = "" 26 | } 27 | 28 | variable "az" { 29 | description = "The availability zone for your instance." 30 | default = "" 31 | } 32 | 33 | variable "az_b" { 34 | description = "The availability zone for your instance." 35 | default = "" 36 | } 37 | 38 | variable "az_c" { 39 | description = "The availability zone for your instance." 40 | default = "" 41 | } 42 | 43 | variable "azs" { 44 | description = "Availability Zones to use for load balancing" 45 | type = "list" 46 | default = [] 47 | } 48 | 49 | variable "my_addresses" { 50 | description = "Your public IP addresses to provide access into the VPC (i.e. google 'what is my ip' and add it here)" 51 | default = ["127.0.0.1/32"] 52 | } 53 | 54 | variable "vpc_cidr" { 55 | description = "Internal CIDR subnet for your VPC" 56 | default = "" 57 | } 58 | 59 | variable "pref" { 60 | description = "The preface for all of your object names" 61 | default = "" 62 | } 63 | 64 | variable "keypair" { 65 | description = "The name of your keypair for access" 66 | default = "" 67 | } 68 | 69 | variable "count" { 70 | description = "How many things to make" 71 | default = "0" 72 | } 73 | 74 | ######################### 75 | ### Instance Settings ### 76 | ######################### 77 | 78 | variable "instance_cidr" { 79 | description = "Internal CIDR subnet for your instances" 80 | default = "/26" 81 | } 82 | 83 | variable "def_ami" { 84 | description = "The default AMI to use for instances if not otherwise defined." 85 | # Ubuntu 16.04 86 | default = "ami-db710fa3" 87 | } 88 | 89 | variable "jump_ami" { 90 | description = "Configured Ubuntu jump API" 91 | # Ubuntu 16.04 92 | default = "ami-0c7dbf6c10e9e16a6" 93 | } 94 | 95 | variable "ins_type" { 96 | description = "The default instance type" 97 | default = "t2.micro" 98 | } 99 | 100 | variable "cent7_ami" { 101 | description = "Default AMI for CentOS (7) Linux" 102 | default = "ami-3ecc8f46" 103 | } 104 | 105 | variable "cent6_ami" { 106 | description = "Default AMI for CentOS (6) Linux" 107 | default = "ami-6fcc8f17" 108 | } 109 | 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zero-to-VPC 2 | 3 | This should build a working VPC with an Ubuntu 16.04 jump box, 1 CentOS 6 server, and 1 CentOS 7 server. You can change these with the "count" variable in vars.tf, and the AMIs used for each with the "*_ami" variables in the same location. (Be sure to check the ami values and ensure you have access to these AMIs in your account/region) 4 | 5 | You will need to decide how to provide access credentials until we have Vault implemented. You can pull them from your AWS CLI credentials file, export them to environment variables, or store them in a static credentials.tf file (an example of this is provided, but there are better ways). 6 | 7 | Securing your credentials and IAM users is an exercise left for the reader. 8 | 9 | You can find details about the options for credential configuration here: 10 | https://www.terraform.io/docs/providers/aws/ 11 | 12 | ## Get started 13 | You **MUST** modify the following things to start building your own environments. 14 | In vars.tf: 15 | - Change the "whoami" value value to yourself 16 | - Change the "vpc_cidr" to a value that's all yours 17 | - Change the "pref" value to something you'd like to use to uniquely identify all objects created by this terraform plan 18 | - Change the "keypair" to a keypair that you own and wish to use for access to these instances 19 | - You will also likely need to adjust the ami variables as these are account, region, and subscription dependent. 20 | 21 | You **MAY** wish to modify some of the following, as well 22 | + In vars.tf: 23 | + Add any tags that you wish to be added to every object in the "my_tags" mapping. 24 | + The "region" and "az" if you wish to work in a region/AZ other than Oregon/us-west-2(a) 25 | + The default AMI values to match the distributions of your choice. These subscruptions appear to be region-specific, so you may need to subscribe to these in your preferred region, or choose an AMI that already has an active subscription. 26 | subscription. 27 | 28 | + In main.tf: 29 | + If you need more/fewer instances, you can remove/add aws_instance blocks to match the number and types you wish to build. 30 | + The instances as configured will delete the EBS volumes (virtual disk) when you run "terraform destroy" to keep the volume storage from filling up with defunct instances. If you wish to preserve your EBS volumes, be sure to remove the "root_block_device" block from the relevant aws_instance blocks. 31 | 32 | ## To-Do 33 | This was my very first terraform. It was largely an exercise to build/manage a multi-purpose test environment. I know a lot could be better, but this served its purpose at the time. My would-like-to-fix-this-if-I-get-hit-by-a-car-and-spend-six-months-in-a-body-cast-with-nothing-better-to-do list is: 34 | * Dynamically enumerate/assign availability zones 35 | * Better handle instance counts/assignments with subnets in different AZs 36 | * Actually use a tfvars file instead of leaving stuff empty in vars.tf (this was definitely a gap in my TF knowledge when I began) 37 | * Maybe divide the files out more logically from main.tf. Maybe. 38 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | access_key = "${var.acc_key}" 3 | secret_key = "${var.priv_key}" 4 | region = "${var.region}" 5 | } 6 | 7 | resource "aws_vpc" "vpc" { 8 | cidr_block = "${var.vpc_cidr}" 9 | enable_dns_hostnames = true 10 | tags = "${merge( 11 | local.my_tags, 12 | map( 13 | "Name", "${var.pref}-vpc" 14 | ) 15 | )}" 16 | } 17 | 18 | resource "aws_security_group" "secgrp" { 19 | name = "${var.pref}-sg" 20 | description = "${var.whoami} terraformed SG" 21 | 22 | ingress { 23 | from_port = "22" 24 | to_port = "22" 25 | protocol = "tcp" 26 | cidr_blocks = "${var.my_addresses}" 27 | } 28 | ingress { 29 | from_port = -1 30 | to_port = -1 31 | protocol = "icmp" 32 | cidr_blocks = "${var.my_addresses}" 33 | } 34 | 35 | ## Allow intra-VPC traffic 36 | 37 | ingress { 38 | from_port = 0 39 | to_port = 0 40 | protocol = "-1" 41 | self = true 42 | } 43 | 44 | ## Allow outbound http/https traffic 45 | egress { 46 | from_port = 80 47 | to_port = 80 48 | protocol = "tcp" 49 | cidr_blocks = ["0.0.0.0/0"] 50 | } 51 | egress { 52 | from_port = 443 53 | to_port = 443 54 | protocol = "tcp" 55 | cidr_blocks = ["0.0.0.0/0"] 56 | } 57 | egress { 58 | from_port = 0 59 | to_port = 0 60 | protocol = "-1" 61 | self = true 62 | } 63 | egress { 64 | from_port = 0 65 | to_port = 0 66 | protocol = "icmp" 67 | cidr_blocks = ["0.0.0.0/0"] 68 | } 69 | 70 | vpc_id = "${aws_vpc.vpc.id}" 71 | tags = "${merge( 72 | local.my_tags, 73 | map( 74 | "Name", "${var.pref}-sg" 75 | ) 76 | )}" 77 | } 78 | 79 | resource "aws_internet_gateway" "igw" { 80 | vpc_id = "${aws_vpc.vpc.id}" 81 | tags = "${merge( 82 | local.my_tags, 83 | map( 84 | "Name", "${var.pref}-igw" 85 | ) 86 | )}" 87 | } 88 | 89 | resource "aws_subnet" "subnet" { 90 | vpc_id = "${aws_vpc.vpc.id}" 91 | cidr_block = "${var.vpc_cidr}" 92 | availability_zone = "${var.az}" 93 | tags = "${merge( 94 | local.my_tags, 95 | map( 96 | "Name", "${var.pref}-subnet" 97 | ) 98 | )}" 99 | } 100 | 101 | resource "aws_route_table" "rtbl" { 102 | vpc_id = "${aws_vpc.vpc.id}" 103 | route { 104 | cidr_block = "0.0.0.0/0" 105 | gateway_id = "${aws_internet_gateway.igw.id}" 106 | } 107 | tags = "${merge( 108 | local.my_tags, 109 | map( 110 | "Name", "${var.pref}-rtbl" 111 | ) 112 | )}" 113 | } 114 | 115 | resource "aws_route_table_association" "rtbl-assoc" { 116 | subnet_id = "${aws_subnet.subnet.id}" 117 | route_table_id = "${aws_route_table.rtbl.id}" 118 | } 119 | 120 | resource "aws_instance" "jump" { 121 | count = "1" 122 | ami = "${var.def_ami}" 123 | instance_type = "${var.ins_type}" 124 | availability_zone = "${var.az}" 125 | #availability_zone = "${element(var.azs, count.index)}" 126 | key_name = "${var.keypair}" 127 | vpc_security_group_ids = ["${aws_security_group.secgrp.id}"] 128 | subnet_id = "${aws_subnet.subnet.id}" 129 | associate_public_ip_address = true 130 | volume_tags = "${merge( 131 | local.my_tags, 132 | map( 133 | "Name", "${var.pref}-jump-ebs-${count.index}" 134 | ) 135 | )}" 136 | 137 | root_block_device { 138 | delete_on_termination = true 139 | } 140 | 141 | tags = "${merge( 142 | local.my_tags, 143 | map( 144 | "Name", "${var.pref}-jump-${count.index}" 145 | ) 146 | )}" 147 | } 148 | 149 | resource "aws_instance" "centos7" { 150 | count = "${var.count}" 151 | ami = "${var.cent7_ami}" 152 | instance_type = "${var.ins_type}" 153 | availability_zone = "${var.az}" 154 | #availability_zone = "${element(var.azs, count.index)}" 155 | key_name = "${var.keypair}" 156 | vpc_security_group_ids = ["${aws_security_group.secgrp.id}"] 157 | subnet_id = "${aws_subnet.subnet.id}" 158 | associate_public_ip_address = true 159 | volume_tags = "${merge( 160 | local.my_tags, 161 | map( 162 | "Name", "${var.pref}-cent7-ebs-${count.index}" 163 | ) 164 | )}" 165 | 166 | root_block_device { 167 | delete_on_termination = true 168 | } 169 | 170 | tags = "${merge( 171 | local.my_tags, 172 | map( 173 | "Name", "${var.pref}-centos7-${count.index}" 174 | ) 175 | )}" 176 | } 177 | 178 | resource "aws_instance" "centos6" { 179 | count = "${var.count}" 180 | ami = "${var.cent6_ami}" 181 | instance_type = "${var.ins_type}" 182 | availability_zone = "${var.az}" 183 | #availability_zone = "${element(var.azs, count.index)}" 184 | key_name = "${var.keypair}" 185 | vpc_security_group_ids = ["${aws_security_group.secgrp.id}"] 186 | subnet_id = "${aws_subnet.subnet.id}" 187 | associate_public_ip_address = true 188 | volume_tags = "${merge( 189 | local.my_tags, 190 | map( 191 | "Name", "${var.pref}-cent6-ebs-${count.index}" 192 | ) 193 | )}" 194 | 195 | root_block_device { 196 | delete_on_termination = true 197 | } 198 | 199 | tags = "${merge( 200 | local.my_tags, 201 | map( 202 | "Name", "${var.pref}-centos6-${count.index}" 203 | ) 204 | )}" 205 | } 206 | --------------------------------------------------------------------------------