From 1425c9807273ea514a34ce67872511fffe4c22a3 Mon Sep 17 00:00:00 2001 From: Patrick MARIE Date: Sat, 19 Feb 2022 17:03:41 +0100 Subject: [PATCH] Use a lockable state repository. Also, make use of terraform workspaces. --- README.md | 68 +++++++++++++++++++++++++++++++++++---- eks/init.tf | 9 ++++++ k8s/init.tf | 9 ++++++ k8s/nginx.tf | 8 ++--- k8s/variables.tf | 9 +++--- state/.terraform.lock.hcl | 21 ++++++++++++ state/README.md | 17 ++++++++++ state/dynamo.tf | 9 ++++++ state/main.tf | 15 +++++++++ state/s3.tf | 19 +++++++++++ state/variables.tf | 10 ++++++ 11 files changed, 179 insertions(+), 15 deletions(-) create mode 100644 eks/init.tf create mode 100644 k8s/init.tf create mode 100644 state/.terraform.lock.hcl create mode 100644 state/README.md create mode 100644 state/dynamo.tf create mode 100644 state/main.tf create mode 100644 state/s3.tf create mode 100644 state/variables.tf diff --git a/README.md b/README.md index 5d47759..e44335b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,19 @@ For all the next commands, make sure to use the `AWS_PROFILE` environment variab $ export AWS_PROFILE=infra-test ``` +### State space initialization + +This section can be skipped; If so, make sure to disable other projects' `init.tf`. + +The `state` infra will create a s3 & dynamo space to store terraform state. + +```sh +$ cd state +$ terraform init +$ terraform plan -var "aws_profile=$AWS_PROFILE" -out tf.plan +$ terraform apply tf.plan +... +``` ### First: EKS @@ -100,7 +113,9 @@ Once eks is deployed, and kubectl correctly configured, we can continue by deplo ```sh $ cd ../k8s $ terraform init -$ terraform plan -var enable_nginx=1 -out tf.plan +# By default, it will install nginx; To disable it, use prod's workspace by: +# $ terraform workspace new prod +$ terraform plan -out tf.plan $ terraform apply ... Apply complete! Resources: 3 added, 0 changed, 1 destroyed. @@ -109,19 +124,58 @@ Apply complete! Resources: 3 added, 0 changed, 1 destroyed. As a result, let's verify there is our stuff deployed: ```sh -$ kubectl get pods --namespace testaroo +$ kubectl get pods --namespace testaroo-default NAME READY STATUS RESTARTS AGE alpine 1/1 Running 0 5m3s nginx-98cf9b965-l785s 1/1 Running 0 5m3s nginx-98cf9b965-smpkr 1/1 Running 0 5m3s -$ kubectl get deploy -n testaroo nginx -o wide +$ kubectl get deploy -n testaroo-default nginx -o wide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR nginx 2/2 2 2 5m46s nginx-container nginx app=Nginx -$ kubectl get svc -n testaroo -o wide -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR -nginx NodePort 172.20.10.182 80:31234/TCP 6m8s app=Nginx +$ kubectl get svc -n testaroo-default -o wide +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR +nginx-lb LoadBalancer 172.20.0.75 a3a176133964a463db33dafb6c6e06a1-480398782.eu-west-3.elb.amazonaws.com 80:30512/TCP 57s app=Nginx +nginx-np NodePort 172.20.227.6 80:31234/TCP 57s app=Nginx +``` + +And now, as the `default` workspace was deployed, it is possible to switch to prod's: + +```sh +$ terraform workspace new prod +$ terraform plan -out tf-prod.plan +$ terraform apply tf-prod.plan +$ kubectl get ns +NAME STATUS AGE +default Active 18m +kube-node-lease Active 18m +kube-public Active 18m +kube-system Active 18m +testaroo-default Active 3m10s +testaroo-prod Active 14s + +$ kubectl get pods -n testaroo-prod +NAME READY STATUS RESTARTS AGE +alpine 1/1 Running 0 39s + +``` + +No `nginx` for `prod`'s workspace, as it was disabled! + +After using workspaces, it is possible to check the state files in s3: + +```sh +$ aws s3 ls terraform-state-infra-aws-eks + PRE env:/ + PRE global/ + +$ aws s3 ls terraform-state-infra-aws-eks/global/s3/ +2022-02-19 16:29:43 33800 terraform.eks.tfstate +2022-02-19 16:40:25 18754 terraform.k8s.tfstate + +$ aws s3 ls terraform-state-infra-aws-eks/env:/prod/global/s3/ +2022-02-19 16:43:03 8392 terraform.k8s.tfstate ``` ### Reaching the app. @@ -157,7 +211,7 @@ $ kubectl get svc -n testaroo nginx-lb NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-lb LoadBalancer 172.20.149.132 a34059e68106b41a292730b5defe734b-581837320.eu-west-3.elb.amazonaws.com 80:31698/TCP 3m50s -$ terraform output 09:59:47 +$ terraform output lb-address = "a34059e68106b41a292730b5defe734b-581837320.eu-west-3.elb.amazonaws.com" ``` diff --git a/eks/init.tf b/eks/init.tf new file mode 100644 index 0000000..168d015 --- /dev/null +++ b/eks/init.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "terraform-state-infra-aws-eks" + key = "global/s3/terraform.eks.tfstate" + region = "eu-west-3" + dynamodb_table = "terraform-state-locks-infra-aws-eks" + encrypt = true + } +} \ No newline at end of file diff --git a/k8s/init.tf b/k8s/init.tf new file mode 100644 index 0000000..d1408c8 --- /dev/null +++ b/k8s/init.tf @@ -0,0 +1,9 @@ +terraform { + backend "s3" { + bucket = "terraform-state-infra-aws-eks" + key = "global/s3/terraform.k8s.tfstate" + region = "eu-west-3" + dynamodb_table = "terraform-state-locks-infra-aws-eks" + encrypt = true + } +} \ No newline at end of file diff --git a/k8s/nginx.tf b/k8s/nginx.tf index f5ae8c3..d1b3df4 100644 --- a/k8s/nginx.tf +++ b/k8s/nginx.tf @@ -1,6 +1,6 @@ resource "kubernetes_namespace" "testaroo" { metadata { - name = "testaroo" + name = "testaroo-${terraform.workspace}" } } @@ -20,7 +20,7 @@ resource "kubernetes_pod" "basic-pod" { } resource "kubernetes_deployment" "testaroo" { - count = var.enable_nginx + count = terraform.workspace == "prod" ? 0 : 1 metadata { name = "nginx" @@ -56,7 +56,7 @@ resource "kubernetes_deployment" "testaroo" { } resource "kubernetes_service" "testaroo" { - count = var.enable_nginx + count = length(kubernetes_deployment.testaroo) metadata { name = "nginx-np" @@ -78,7 +78,7 @@ resource "kubernetes_service" "testaroo" { } resource "kubernetes_service" "testaroo-lb" { - count = var.enable_nginx + count = length(kubernetes_deployment.testaroo) metadata { name = "nginx-lb" diff --git a/k8s/variables.tf b/k8s/variables.tf index 8f7a975..d057232 100644 --- a/k8s/variables.tf +++ b/k8s/variables.tf @@ -1,4 +1,5 @@ -variable "enable_nginx" { - type = number - default = 0 -} +# Superseeded by terraform's workspaces. +# variable "enable_nginx" { +# type = number +# default = 0 +# } diff --git a/state/.terraform.lock.hcl b/state/.terraform.lock.hcl new file mode 100644 index 0000000..fa60a4f --- /dev/null +++ b/state/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "3.74.3" + constraints = "~> 3.27" + hashes = [ + "h1:h4TYqgRKTuuWfZtxJnEGcs/NxGCaxZ4jr0IwTfgZDRM=", + "zh:25401cd4667d0496caf7e92e74ecef7c98cf74465570705cda2207770c27ff6c", + "zh:2d154527a9b2585f72fc5eceac635257e3f50f68de8a519e71c795d5166a0a22", + "zh:499fa5201804a5a33a90d683147fb2f81da91bfcd8ed20293f88f6f39cedbf97", + "zh:730284250fd949a59afb6935b3a68a33709d5a78b686fa98f351ad32c919cfc3", + "zh:7461ebd6fb35900d620cfa3f42126d988ea1e604ee3828d1c64d5727f908bd26", + "zh:7c85743b31c7459f8e74aaa98471ba82c54517eb908603411808a12982d89b1c", + "zh:8ed977b7fb97de624f5414b08cab36fd973a624072e0e9082c0c822e0864c7b9", + "zh:94ae7313bb0b425d4007a0b70601a337972c4f0f7a323487acf69215e74b4425", + "zh:b5a1589672d709da725a72c46d28bf5b2dea71325f6e0b44a0049f644cd09eba", + "zh:c7e8e7ce59e4578416557fc2f138137af3c8365ac3e34f0ff5166323c7d641a1", + "zh:ccf2e286b207e749fff76bb4075deddb9e7e237936d8654f34828c54e7035455", + ] +} diff --git a/state/README.md b/state/README.md new file mode 100644 index 0000000..fc6291d --- /dev/null +++ b/state/README.md @@ -0,0 +1,17 @@ +# state + +This terraform infra creates mandatory s3 bucket & dynamo db for locks to handle terraform's states. + +## Usage + +```sh +$ export AWS_PROFILE=infra-test +$ terraform init +$ terraform plan -var "aws_profile=$AWS_PROFILE" -out tf.plan +$ terraform apply tf.plan +... + +$ +``` + +Once created, other terraform infras' states can be saved in those s3/dynamo's repositories. diff --git a/state/dynamo.tf b/state/dynamo.tf new file mode 100644 index 0000000..ed4c711 --- /dev/null +++ b/state/dynamo.tf @@ -0,0 +1,9 @@ +resource "aws_dynamodb_table" "terraform_locks" { + name = "terraform-state-locks-infra-aws-eks" + billing_mode = "PAY_PER_REQUEST" + hash_key = "LockID" + attribute { + name = "LockID" + type = "S" + } +} \ No newline at end of file diff --git a/state/main.tf b/state/main.tf new file mode 100644 index 0000000..b0b8a8d --- /dev/null +++ b/state/main.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.27" + } + } + + required_version = ">= 0.14.9" +} + +provider "aws" { + profile = var.aws_profile + region = var.aws_region +} diff --git a/state/s3.tf b/state/s3.tf new file mode 100644 index 0000000..7d62bb2 --- /dev/null +++ b/state/s3.tf @@ -0,0 +1,19 @@ +resource "aws_s3_bucket" "terraform_state" { + bucket = "terraform-state-infra-aws-eks" + + # lifecycle { + # prevent_destroy = true + # } + + versioning { + enabled = true + } + + server_side_encryption_configuration { + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } + } +} \ No newline at end of file diff --git a/state/variables.tf b/state/variables.tf new file mode 100644 index 0000000..86d282e --- /dev/null +++ b/state/variables.tf @@ -0,0 +1,10 @@ +variable "aws_profile" { + type = string + default = "aws-infra" +} + +variable "aws_region" { + type = string + default = "eu-west-3" +} +