Terraform Apps and Infra 6 - Hands-on with Providers
Providers
AWS: Set up apache server
Code
main.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27# Create and bootstrap webserver
# create EC2 server, parameters come from setup.tf file
resource "aws_instance" "webserver" {
ami = data.aws_ssm_parameter.webserver-ami.value
instance_type = "t3.micro"
key_name = aws_key_pair.webserver-key.key_name
associate_public_ip_address = true
vpc_security_group_ids = [aws_security_group.sg.id]
subnet_id = aws_subnet.subnet.id
# if its remote, execute this code using the parameters embedded on connection
provisioner "remote-exec" {
inline = [
"sudo yum -y install httpd && sudo systemctl start httpd",
"echo '<h1><center>My Test Website With Help From Terraform Provisioner</center></h1>' > index.html",
"sudo mv index.html /var/www/html/"
]
connection {
type = "ssh"
user = "ec2-user"
private_key = file("~/.ssh/id_rsa")
host = self.public_ip
}
}
tags = {
Name = "webserver"
}
}setup.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98provider "aws" {
region = "us-east-1"
}
# Create key-pair for logging into EC2 in us-east-1
resource "aws_key_pair" "webserver-key" {
key_name = "webserver-key"
public_key = file("~/.ssh/id_rsa.pub")
}
# Get Linux AMI ID using SSM Parameter endpoint in us-east-1
data "aws_ssm_parameter" "webserver-ami" {
name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}
# Create VPC in us-east-1
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "terraform-vpc"
}
}
# Create IGW in us-east-1
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.vpc.id
}
# Get main route table to modify
data "aws_route_table" "main_route_table" {
filter {
name = "association.main"
values = ["true"]
}
filter {
name = "vpc-id"
values = [aws_vpc.vpc.id]
}
}
# Create route table in us-east-1
resource "aws_default_route_table" "internet_route" {
default_route_table_id = data.aws_route_table.main_route_table.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "Terraform-RouteTable"
}
}
#Get all available AZ's in VPC for master region
data "aws_availability_zones" "azs" {
state = "available"
}
#Create subnet # 1 in us-east-1
resource "aws_subnet" "subnet" {
availability_zone = element(data.aws_availability_zones.azs.names, 0)
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.1.0/24"
}
#Create SG for allowing TCP/80 & TCP/22
resource "aws_security_group" "sg" {
name = "sg"
description = "Allow TCP/80 & TCP/22"
vpc_id = aws_vpc.vpc.id
ingress {
description = "Allow SSH traffic"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "allow traffic from TCP/80"
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"]
}
}
output "Webserver-Public-IP" {
value = aws_instance.webserver.public_ip
}
On CLI
1
2
3
4
5
6terraform init
terraform validate
terraform plan
terraform apply
# terraform provisioner tries to connect to the EC2 instance
# then runs bootstraped code
Azure: Deploy WebApp
Code
Basic
main.tf
for Azure1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24# Configure the Azure provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 2.26"
}
}
required_version = ">= 0.14.9"
}
provider "azurerm" {
features {}
skip_provider_registration = true
}
# Create a virtual network
resource "azurerm_virtual_network" "vnet" {
name = "BatmanInc"
address_space = ["10.0.0.0/16"]
location = "Central US"
resource_group_name = "<ADD YOUR RESOURCE GROUP NAME>"
}maint.tf
fpor webapp on Azure1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27provider "azurerm" {
version = 1.38
}
resource "azurerm_app_service_plan" "svcplan" {
name = "Enter App Service Plan name"
location = "eastus"
resource_group_name = "Enter Resource Group Name"
sku {
tier = "Standard"
size = "S1"
}
}
resource "azurerm_app_service" "appsvc" {
name = "Enter Web App Service Name"
location = "eastus"
resource_group_name = "Enter Resource Group Name"
app_service_plan_id = azurerm_app_service_plan.svcplan.id
site_config {
dotnet_framework_version = "v4.0"
scm_type = "LocalGit"
}
}
CLI
1
2
3terraform init
terraform validate
terraform plan
Kubernetes deployment
Code
kubernetes.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
}
}
}
variable "host" {
type = string
}
variable "client_certificate" {
type = string
}
variable "client_key" {
type = string
}
variable "cluster_ca_certificate" {
type = string
}
provider "kubernetes" {
host = var.host
client_certificate = base64decode(var.client_certificate)
client_key = base64decode(var.client_key)
cluster_ca_certificate = base64decode(var.cluster_ca_certificate)
}terraform.tfvars
1
2
3
4host = "DUMMY VALUE"
client_certificate = "DUMMY VALUE"
client_key = "DUMMY VALUE"
cluster_ca_certificate = "DUMMY VALUE".kind-config
1
2
3
4
5
6
7
8kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30201
hostPort: 30201
listenAddress: "0.0.0.0"
CLI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# PREPARE KUBERNETES
# create Kubernetes cluster, using kind-cli
kind create cluster --name lab-terraform-kubernetes --config kind-config.yaml
kubectl cluster-info --context kind-lab-terraform-kubernetes
# verify
kind get clusters
# get the server data
kubectl config view --minify --flatten --context=kind-lab-terraform-kubernetes
# put the server address, client-key-data into terraform.tfvars
# DEPLOY IT
terraform init
terraform validate
terraform plan
# validate "long-live-the-bat" exists
kubectl get deployments
AWS EKS deployment
Code
kubernetes.tf
1
2
3
4
5provider "kubernetes" {
host = data.aws_eks_cluster.cluster.endpoint
token = data.aws_eks_cluster_auth.cluster.token
cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data)
}eks-cluster.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "17.24.0"
cluster_name = local.cluster_name
cluster_version = "1.20"
subnets = module.vpc.private_subnets
tags = {
Environment = "training"
GithubRepo = "terraform-aws-eks"
GithubOrg = "terraform-aws-modules"
}
vpc_id = module.vpc.vpc_id
workers_group_defaults = {
root_volume_type = "gp2"
}
worker_groups = [
{
name = "worker-group-1"
instance_type = "t2.small"
additional_userdata = "echo foo bar"
asg_desired_capacity = 2
additional_security_group_ids = [aws_security_group.worker_group_mgmt_one.id]
},
{
name = "worker-group-2"
instance_type = "t2.medium"
additional_userdata = "echo foo bar"
additional_security_group_ids = [aws_security_group.worker_group_mgmt_two.id]
asg_desired_capacity = 1
},
]
}
data "aws_eks_cluster" "cluster" {
name = module.eks.cluster_id
}
data "aws_eks_cluster_auth" "cluster" {
name = module.eks.cluster_id
}security-groups.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46resource "aws_security_group" "worker_group_mgmt_one" {
name_prefix = "worker_group_mgmt_one"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"10.0.0.0/8",
]
}
}
resource "aws_security_group" "worker_group_mgmt_two" {
name_prefix = "worker_group_mgmt_two"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"192.168.0.0/16",
]
}
}
resource "aws_security_group" "all_worker_mgmt" {
name_prefix = "all_worker_management"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
]
}
}vpc.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47variable "region" {
default = "us-east-1"
description = "AWS region"
}
provider "aws" {
region = "us-east-1"
}
data "aws_availability_zones" "available" {}
locals {
cluster_name = "education-eks-${random_string.suffix.result}"
}
resource "random_string" "suffix" {
length = 8
special = false
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.66.0"
name = "education-vpc"
cidr = "10.0.0.0/16"
azs = data.aws_availability_zones.available.names
private_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
public_subnets = ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
enable_dns_hostnames = true
tags = {
"kubernetes.io/cluster/${local.cluster_name}" = "shared"
}
public_subnet_tags = {
"kubernetes.io/cluster/${local.cluster_name}" = "shared"
"kubernetes.io/role/elb" = "1"
}
private_subnet_tags = {
"kubernetes.io/cluster/${local.cluster_name}" = "shared"
"kubernetes.io/role/internal-elb" = "1"
}
}versions.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 3.20.0"
}
random = {
source = "hashicorp/random"
version = "3.0.0"
}
local = {
source = "hashicorp/local"
version = "2.0.0"
}
null = {
source = "hashicorp/null"
version = "3.0.0"
}
template = {
source = "hashicorp/template"
version = "2.2.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.0.1"
}
}
required_version = "> 0.14"
}outouts.tf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34output "cluster_id" {
description = "EKS cluster ID."
value = module.eks.cluster_id
}
output "cluster_endpoint" {
description = "Endpoint for EKS control plane."
value = module.eks.cluster_endpoint
}
output "cluster_security_group_id" {
description = "Security group ids attached to the cluster control plane."
value = module.eks.cluster_security_group_id
}
output "kubectl_config" {
description = "kubectl config as generated by the module."
value = module.eks.kubeconfig
}
output "config_map_aws_auth" {
description = "A kubernetes configuration to authenticate to this EKS cluster."
value = module.eks.config_map_aws_auth
}
output "region" {
description = "AWS region"
value = var.region
}
output "cluster_name" {
description = "Kubernetes Cluster Name"
value = local.cluster_name
}lab_kubernetes_resources.tf
(for nginx, added to dir when required by CLI steps)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45resource "kubernetes_deployment" "nginx" {
metadata {
name = "long-live-the-bat"
labels = {
App = "longlivethebat"
}
}
spec {
replicas = 2
selector {
match_labels = {
App = "longlivethebat"
}
}
template {
metadata {
labels = {
App = "longlivethebat"
}
}
spec {
container {
image = "nginx:1.7.8"
name = "batman"
port {
container_port = 80
}
resources {
limits = {
cpu = "0.5"
memory = "512Mi"
}
requests = {
cpu = "250m"
memory = "50Mi"
}
}
}
}
}
}
}
CLI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20# DEPLOY THE EKS CLUSTER
terraform init
terraform plan
terraform apply
# Configure kubectl to interact with the cluster
aws eks --region $(terraform output -raw region) update-kubeconfig --name $(terraform output -raw cluster_name)
# verify deployment
kubectl get cs
# DEPLOY NGINX PODS
# add lab_kubernetes_resources.tf file
terraform plan
terraform apply
# verify 2 "long-live-the-bat" pods are up and running
kubectl get deployments
# CLEAN UP
terraform destroy
# verify
terraform show
Troubleshooting
graph LR subgraph Primary UI A[configuration language] end subgraph Metadata B[state] end subgraph Resource graph comms C[TF core application] end subgraph Auth mapping D[Cloud Provider] end A --> B B --> C C --> D
Detect syntax errors on files
1
terraform fmt
Detect errors on dependencies (e.g. cycles, invalid references, unsupported group vars)
1
terraform validate
Detect version missmatching
1
terraform version
Get TF trace, setting environment variables
1
2
3
4export TF_LOG_CORE=TRACE
export TF_LOG_PROVIDER=TRACE
export TF_LOG_PATH=logs.txt
terraform refreshDetect state discrepancies
1
terraform apply