├── provider.tf ├── .gitignore ├── versions.tf ├── .vscode └── settings.json ├── README.md ├── k8s └── app.yaml ├── main.tf ├── output.tf └── vpc.tf /provider.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | profile = "default" 3 | region = "eu-west-1" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | terraform.tfstate 3 | terraform.tfstate.backup 4 | .terraform 5 | .terrafor* 6 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13.1" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.71" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "workbench.colorCustomizations": { 3 | "editor.lineHighlightBackground": "#1073cf2d", 4 | "editor.lineHighlightBorder": "#9fced11f", 5 | "activityBar.background": "#4E2001", 6 | "titleBar.activeBackground": "#6E2D01", 7 | "titleBar.activeForeground": "#FFFBF8" 8 | } 9 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Requirements 2 | 3 | | Name | Version | 4 | |------|---------| 5 | | [terraform](#requirement\_terraform) | >= 0.13.1 | 6 | | [aws](#requirement\_aws) | >= 3.71 | 7 | 8 | ## Providers 9 | 10 | | Name | Version | 11 | |------|---------| 12 | | [aws](#provider\_aws) | >= 3.71 | 13 | 14 | # How to configure VPC and EKS from scratch 15 | 16 | with this package you can have VPC and EKS from scratch. just clone it and then put the following commands. please be sure about your aws cli profile. 17 | 18 | `terraform init` 19 | 20 | `terraform fmt` 21 | 22 | `terraform validate` 23 | 24 | `terraform apply` 25 | 26 | ### create kub config 27 | check your kube config first 28 | 29 | `cat ~/.kube/config` 30 | 31 | create your context 32 | 33 | `aws eks --region eu-west-1 update-kubeconfig --name eks --profile default` 34 | 35 | ### you can find the k8s configuration on k8s folder 36 | k8s -> app.yaml 37 | you have to apply it with kubectl command 38 | and know you check your cluster -------------------------------------------------------------------------------- /k8s/app.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: nginx 6 | labels: 7 | app: nginx 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app: nginx 13 | template: 14 | metadata: 15 | labels: 16 | app: nginx 17 | spec: 18 | containers: 19 | - name: nginx 20 | image: nginx:1.14.2 21 | ports: 22 | - containerPort: 80 23 | --- 24 | apiVersion: v1 25 | kind: Service 26 | metadata: 27 | name: internal-nginx-service 28 | annotations: 29 | service.beta.kubernetes.io/aws-load-balancer-type: nlb 30 | service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: 'true' 31 | service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0 32 | spec: 33 | selector: 34 | app: nginx 35 | type: LoadBalancer 36 | ports: 37 | - protocol: TCP 38 | port: 80 39 | --- 40 | apiVersion: v1 41 | kind: Service 42 | metadata: 43 | name: external-nginx-service 44 | annotations: 45 | service.beta.kubernetes.io/aws-load-balancer-type: nlb 46 | service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: 'true' 47 | spec: 48 | selector: 49 | app: nginx 50 | type: LoadBalancer 51 | ports: 52 | - protocol: TCP 53 | port: 80 -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | ################################ 2 | # Eks cluster 3 | ################################ 4 | 5 | resource "aws_eks_cluster" "eks" { 6 | name = "eks" 7 | role_arn = aws_iam_role.eks_cluster.arn 8 | 9 | 10 | vpc_config { 11 | endpoint_private_access = false 12 | endpoint_public_access = true 13 | subnet_ids = [aws_subnet.public_1.id, aws_subnet.public_2.id, aws_subnet.private_1.id, aws_subnet.private_2.id] 14 | } 15 | 16 | # Ensure that IAM Role permissions are created before and deleted after EKS Cluster handling. 17 | # Otherwise, EKS will not be able to properly delete EKS managed EC2 infrastructure such as Security Groups. 18 | depends_on = [ 19 | aws_iam_role_policy_attachment.amazon_eks_cluster_policy, 20 | 21 | ] 22 | } 23 | 24 | ################################ 25 | # Eks cluster iam role 26 | ################################ 27 | resource "aws_iam_role" "eks_cluster" { 28 | name = "eks-cluster" 29 | 30 | # Terraform's "jsonencode" function converts a 31 | # Terraform expression result to valid JSON syntax. 32 | assume_role_policy = jsonencode({ 33 | Version = "2012-10-17" 34 | Statement = [ 35 | { 36 | Action = "sts:AssumeRole" 37 | Effect = "Allow" 38 | Sid = "" 39 | Principal = { 40 | Service = "eks.amazonaws.com" 41 | } 42 | }, 43 | ] 44 | }) 45 | 46 | 47 | } 48 | 49 | 50 | resource "aws_iam_role_policy_attachment" "amazon_eks_cluster_policy" { 51 | role = aws_iam_role.eks_cluster.name 52 | policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" 53 | } 54 | 55 | ################################ 56 | # Eks node group 57 | ################################ 58 | resource "aws_eks_node_group" "node_general" { 59 | cluster_name = aws_eks_cluster.eks.name 60 | node_group_name = "node-general" 61 | node_role_arn = aws_iam_role.node_general.arn 62 | subnet_ids = [aws_subnet.private_1.id, aws_subnet.private_2.id] 63 | 64 | scaling_config { 65 | desired_size = 1 66 | max_size = 1 67 | min_size = 1 68 | } 69 | 70 | ami_type = "AL2_x86_64" 71 | #SPOT and ON_DEMAND are valid 72 | capacity_type = "ON_DEMAND" 73 | #disk size for worker node 74 | disk_size = 20 75 | force_update_version = false 76 | instance_types = ["t3.small"] 77 | labels = { 78 | role = "nodes-general" 79 | } 80 | # #kubernetes version 81 | 82 | # version = "1.8" 83 | 84 | 85 | # Ensure that IAM Role permissions are created before and deleted after EKS Node Group handling. 86 | # Otherwise, EKS will not be able to properly delete EC2 Instances and Elastic Network Interfaces. 87 | depends_on = [ 88 | aws_iam_role_policy_attachment.amazon_eks_worker_node_policy_general, 89 | aws_iam_role_policy_attachment.amazon_eks_cni_policy_general, 90 | aws_iam_role_policy_attachment.amazon_ec2_container_registry_read_only, 91 | ] 92 | } 93 | 94 | ################################ 95 | # Role and attachments 96 | ################################ 97 | 98 | resource "aws_iam_role" "node_general" { 99 | name = "eks-node-group-general" 100 | 101 | # Terraform's "jsonencode" function converts a 102 | # Terraform expression result to valid JSON syntax. 103 | assume_role_policy = jsonencode({ 104 | Version = "2012-10-17" 105 | Statement = [ 106 | { 107 | Action = "sts:AssumeRole" 108 | Effect = "Allow" 109 | Sid = "" 110 | Principal = { 111 | Service = "ec2.amazonaws.com" 112 | } 113 | }, 114 | ] 115 | }) 116 | 117 | } 118 | 119 | resource "aws_iam_role_policy_attachment" "amazon_eks_worker_node_policy_general" { 120 | role = aws_iam_role.node_general.name 121 | policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy" 122 | } 123 | 124 | 125 | resource "aws_iam_role_policy_attachment" "amazon_eks_cni_policy_general" { 126 | role = aws_iam_role.node_general.name 127 | policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy" 128 | } 129 | 130 | 131 | resource "aws_iam_role_policy_attachment" "amazon_ec2_container_registry_read_only" { 132 | role = aws_iam_role.node_general.name 133 | policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly" 134 | } 135 | -------------------------------------------------------------------------------- /output.tf: -------------------------------------------------------------------------------- 1 | ################################ 2 | ## VPC 3 | ################################ 4 | 5 | output "vpc_id" { 6 | value = aws_vpc.main.id 7 | description = "vpc id" 8 | sensitive = false 9 | } 10 | output "vpc_arn" { 11 | value = aws_vpc.main.arn 12 | description = "vpc arn" 13 | sensitive = false 14 | } 15 | output "vpc_instance_tenancy" { 16 | value = aws_vpc.main.instance_tenancy 17 | description = "vpc instance_tenancy" 18 | sensitive = false 19 | } 20 | output "vpc_enable_dns_support" { 21 | value = aws_vpc.main.enable_dns_support 22 | description = "vpc enable_dns_support" 23 | sensitive = false 24 | } 25 | output "vpc_enable_dns_hostnames" { 26 | value = aws_vpc.main.enable_dns_hostnames 27 | description = "vpc enable_dns_hostnames" 28 | sensitive = false 29 | } 30 | output "vpc_enable_classiclink" { 31 | value = aws_vpc.main.enable_classiclink 32 | description = "vpc enable_classiclink" 33 | sensitive = false 34 | } 35 | output "vpc_default_route_table_id" { 36 | value = aws_vpc.main.default_route_table_id 37 | description = "vpc default_route_table_id" 38 | sensitive = false 39 | } 40 | output "vpc_main_route_table_id" { 41 | value = aws_vpc.main.main_route_table_id 42 | description = "vpc main_route_table_id" 43 | sensitive = false 44 | } 45 | output "vpc_default_network_acl_id" { 46 | value = aws_vpc.main.default_network_acl_id 47 | description = "vpc default_network_acl_id" 48 | sensitive = false 49 | } 50 | output "vpc_default_security_group_id" { 51 | value = aws_vpc.main.default_security_group_id 52 | description = "vpc default_security_group_id" 53 | sensitive = false 54 | } 55 | 56 | ################################ 57 | ## GW1 58 | ################################ 59 | 60 | output "aws_nat_gateway_gw1_id" { 61 | value = aws_nat_gateway.gw1.id 62 | description = "internet gateway id" 63 | sensitive = false 64 | } 65 | output "aws_nat_gateway_gw1_allocation_id" { 66 | value = aws_nat_gateway.gw1.allocation_id 67 | description = "gateway_nat1_allocation_id" 68 | sensitive = false 69 | } 70 | output "aws_nat_gateway_gw1_subnet_id" { 71 | value = aws_nat_gateway.gw1.subnet_id 72 | description = "gateway_nat1_subnet_id" 73 | sensitive = false 74 | } 75 | output "aws_nat_gateway_gw1_private_ip" { 76 | value = aws_nat_gateway.gw1.private_ip 77 | description = "gateway_nat1_private_ip" 78 | sensitive = false 79 | } 80 | output "aws_nat_gateway_gw1_public_ip" { 81 | value = aws_nat_gateway.gw1.public_ip 82 | description = "gateway_nat1_public_ip" 83 | sensitive = false 84 | } 85 | output "aws_nat_gateway_gw1_tags_all" { 86 | value = aws_nat_gateway.gw1.tags_all 87 | description = "gateway_nat1_tags_all" 88 | sensitive = false 89 | } 90 | 91 | ################################ 92 | ## GW2 93 | ################################ 94 | 95 | output "aws_nat_gateway_gw2_id" { 96 | value = aws_nat_gateway.gw2.id 97 | description = "internet gateway id" 98 | sensitive = false 99 | } 100 | output "aws_nat_gateway_gw2_allocation_id" { 101 | value = aws_nat_gateway.gw2.allocation_id 102 | description = "gateway_gw2_allocation_id" 103 | sensitive = false 104 | } 105 | output "aws_nat_gateway_gw2_subnet_id" { 106 | value = aws_nat_gateway.gw2.subnet_id 107 | description = "gateway_gw2_subnet_id" 108 | sensitive = false 109 | } 110 | output "aws_nat_gateway_gw2_private_ip" { 111 | value = aws_nat_gateway.gw2.private_ip 112 | description = "gateway_gw2_private_ip" 113 | sensitive = false 114 | } 115 | output "aws_nat_gateway_gw2_public_ip" { 116 | value = aws_nat_gateway.gw2.public_ip 117 | description = "gateway_gw2_public_ip" 118 | sensitive = false 119 | } 120 | output "aws_nat_gateway_gw2_tags_all" { 121 | value = aws_nat_gateway.gw2.tags_all 122 | description = "gateway_gw2_tags_all" 123 | sensitive = false 124 | } 125 | 126 | ################################ 127 | ## GW2 128 | ################################ 129 | 130 | output "internet_gateway_id" { 131 | value = aws_internet_gateway.main.id 132 | description = "internet gateway id" 133 | sensitive = false 134 | } 135 | output "internet_gateway_arn" { 136 | value = aws_internet_gateway.main.arn 137 | description = "internet gateway arn" 138 | sensitive = false 139 | } 140 | output "internet_gateway_owner_id" { 141 | value = aws_internet_gateway.main.owner_id 142 | description = "internet gateway owner_id" 143 | sensitive = false 144 | } 145 | output "internet_gateway_tags_all" { 146 | value = aws_internet_gateway.main.tags_all 147 | description = "internet gateway tags_all" 148 | sensitive = false 149 | } 150 | 151 | ################################ 152 | ## EKS 153 | ################################ 154 | 155 | output "aws_eks_cluster_endpoint" { 156 | value = aws_eks_cluster.eks.endpoint 157 | } 158 | output "aws_eks_cluster_arn" { 159 | value = aws_eks_cluster.eks.arn 160 | } 161 | output "aws_eks_cluster_id" { 162 | value = aws_eks_cluster.eks.id 163 | } 164 | output "aws_eks_cluster_status" { 165 | value = aws_eks_cluster.eks.status 166 | } 167 | output "aws_eks_created_at" { 168 | value = aws_eks_cluster.eks.created_at 169 | } 170 | -------------------------------------------------------------------------------- /vpc.tf: -------------------------------------------------------------------------------- 1 | ################################ 2 | ## VPC 3 | ################################ 4 | 5 | resource "aws_vpc" "main" { 6 | cidr_block = "192.168.0.0/16" 7 | instance_tenancy = "default" 8 | 9 | enable_dns_support = true 10 | enable_dns_hostnames = true 11 | enable_classiclink = false 12 | enable_classiclink_dns_support = false 13 | assign_generated_ipv6_cidr_block = false 14 | tags = { 15 | Name = "main" 16 | Terraform = "true" 17 | } 18 | } 19 | 20 | ################################ 21 | ## Private Subnet 22 | ################################ 23 | 24 | resource "aws_subnet" "private_1" { 25 | vpc_id = aws_vpc.main.id 26 | cidr_block = "192.168.128.0/18" 27 | #AZ availability zone 28 | availability_zone = "eu-west-1a" 29 | 30 | 31 | tags = { 32 | Name = "private-eu-west-1a" 33 | "kubernetes.io/cluster/eks" = "shared" 34 | "kubernetes.io/role/internal-elb" = 1 35 | Terraform = "true" 36 | } 37 | } 38 | 39 | 40 | resource "aws_subnet" "private_2" { 41 | vpc_id = aws_vpc.main.id 42 | cidr_block = "192.168.192.0/18" 43 | #AZ availability zone 44 | availability_zone = "eu-west-1b" 45 | 46 | 47 | tags = { 48 | Name = "private-eu-west-1b" 49 | "kubernetes.io/cluster/eks" = "shared" 50 | "kubernetes.io/role/internal-elb" = 1 51 | Terraform = "true" 52 | } 53 | } 54 | 55 | ################################ 56 | ## Private routing table 57 | ################################ 58 | 59 | resource "aws_route_table" "private1" { 60 | vpc_id = aws_vpc.main.id 61 | 62 | route { 63 | cidr_block = "0.0.0.0/0" 64 | nat_gateway_id = aws_nat_gateway.gw1.id 65 | } 66 | 67 | tags = { 68 | Name = "private1" 69 | } 70 | } 71 | 72 | resource "aws_route_table" "private2" { 73 | vpc_id = aws_vpc.main.id 74 | 75 | route { 76 | cidr_block = "0.0.0.0/0" 77 | nat_gateway_id = aws_nat_gateway.gw2.id 78 | } 79 | 80 | tags = { 81 | Name = "private2" 82 | } 83 | } 84 | 85 | ################################ 86 | ## Private routing table association 87 | ################################ 88 | 89 | resource "aws_route_table_association" "private1" { 90 | subnet_id = aws_subnet.private_1.id 91 | route_table_id = aws_route_table.private1.id 92 | } 93 | resource "aws_route_table_association" "private2" { 94 | subnet_id = aws_subnet.private_2.id 95 | route_table_id = aws_route_table.private2.id 96 | } 97 | 98 | ################################ 99 | ## Public Subnet 100 | ################################ 101 | 102 | resource "aws_subnet" "public_1" { 103 | vpc_id = aws_vpc.main.id 104 | cidr_block = "192.168.0.0/18" 105 | #AZ availability zone 106 | availability_zone = "eu-west-1a" 107 | map_public_ip_on_launch = true 108 | 109 | tags = { 110 | Name = "public-eu-west-1a" 111 | "kubernetes.io/cluster/eks" = "shared" 112 | "kubernetes.io/role/elb" = 1 113 | Terraform = "true" 114 | } 115 | } 116 | 117 | 118 | resource "aws_subnet" "public_2" { 119 | vpc_id = aws_vpc.main.id 120 | cidr_block = "192.168.64.0/18" 121 | #AZ availability zone 122 | availability_zone = "eu-west-1b" 123 | map_public_ip_on_launch = true 124 | 125 | tags = { 126 | Name = "public-eu-west-1b" 127 | "kubernetes.io/cluster/eks" = "shared" 128 | "kubernetes.io/role/elb" = 1 129 | Terraform = "true" 130 | } 131 | } 132 | 133 | ################################ 134 | ## Public routing table 135 | ################################ 136 | resource "aws_route_table" "public" { 137 | vpc_id = aws_vpc.main.id 138 | 139 | route { 140 | cidr_block = "0.0.0.0/0" 141 | gateway_id = aws_internet_gateway.main.id 142 | } 143 | 144 | 145 | tags = { 146 | Name = "public" 147 | Terraform = "true" 148 | } 149 | } 150 | 151 | ################################ 152 | ## Public routing table association 153 | ################################ 154 | 155 | resource "aws_route_table_association" "public1" { 156 | subnet_id = aws_subnet.public_1.id 157 | route_table_id = aws_route_table.public.id 158 | } 159 | resource "aws_route_table_association" "public2" { 160 | subnet_id = aws_subnet.public_2.id 161 | route_table_id = aws_route_table.public.id 162 | } 163 | 164 | ################################ 165 | ## NTGW 1 166 | ################################ 167 | 168 | resource "aws_nat_gateway" "gw1" { 169 | allocation_id = aws_eip.nat1.id 170 | subnet_id = aws_subnet.public_1.id 171 | 172 | tags = { 173 | Name = "gw1 NAT1" 174 | } 175 | 176 | # To ensure proper ordering, it is recommended to add an explicit dependency 177 | # on the Internet Gateway for the VPC. 178 | depends_on = [aws_internet_gateway.main] 179 | } 180 | 181 | ################################ 182 | ## NTGW 2 183 | ################################ 184 | 185 | resource "aws_nat_gateway" "gw2" { 186 | allocation_id = aws_eip.nat2.id 187 | subnet_id = aws_subnet.public_2.id 188 | 189 | tags = { 190 | Name = "NAT2 gw2" 191 | } 192 | 193 | # To ensure proper ordering, it is recommended to add an explicit dependency 194 | # on the Internet Gateway for the VPC. 195 | depends_on = [aws_internet_gateway.main] 196 | } 197 | 198 | ################################ 199 | ## IGW 200 | ################################ 201 | 202 | resource "aws_internet_gateway" "main" { 203 | vpc_id = aws_vpc.main.id 204 | 205 | tags = { 206 | Name = "main" 207 | Terraform = "true" 208 | } 209 | } 210 | 211 | ################################ 212 | ## EIP 1 213 | ################################ 214 | 215 | resource "aws_eip" "nat1" { 216 | #for availablity zone 1 ( public1 and private1 ) 217 | depends_on = [aws_internet_gateway.main] 218 | 219 | tags = { 220 | Terraform = "true" 221 | } 222 | } 223 | 224 | ################################ 225 | ## EIP 2 226 | ################################ 227 | 228 | resource "aws_eip" "nat2" { 229 | #for availablity zone 2 ( public2 and private2 ) 230 | depends_on = [aws_internet_gateway.main] 231 | 232 | tags = { 233 | Terraform = "true" 234 | } 235 | } 236 | --------------------------------------------------------------------------------