Terraform - Kubernetes Deployment plus S3 backend with state locking in DynamoDB
Published: 2023-03-19 23:08:26

In this exercise, I will be deploying an application to a kubernetes cluster using terraform infrastructure-as-code, and also using an AWS S3 bucket as a remote backend with state-locking in an AWS DynamoDB table.

This exercise is part of the Terraform challenge administered by KodeKloud. All credits to kodekloud.com for the design and structure of the exercise.

<u>Architecture</u>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1aja4opt46gbd9fqk7a1.png)
<figcaption>Source: KodeKloud.com</figcaption>
*

A kubernetes application that will deploy 4 replicas.
  



<u>Task List</u>

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0rf6klwo8qbffohvbn6.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2x6hglxlanye5y8tkot.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v5vk7kae8svu98bj7sjf.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ak0k6oznfzy0wviam2z0.png)
<figcaption>Source: KodeKloud.com</figcaption>



<u>Infrastructure-As-Code (Terraform file)</u>

```javascript
terraform {
  required_providers {
    kubernetes = {
      source = "hashicorp/kubernetes"
      version = "2.11.0"
    }
  }
}

provider "kubernetes" {
  # config_path    = "~/.kube/config"
  # config_context = "docker-desktop"
  config_path    = "/root/.kube/config"
  config_context = "kubernetes@kubernetes_admin"
}

##Service 

resource "kubernetes_service" "webapp-service" {
  metadata {
    name = "webapp-service"
  }
  spec {
    selector = {
      name = "webapp"
    }
    port {
      port = 8080
      node_port = 30080
    }
    type = "NodePort"
  }
}

## Deployment 

resource "kubernetes_deployment_v1" "frontend" {
  metadata {
    name = "frontend"
    labels = {
      name = "frontend"
    }
  }

  spec {
    replicas = 4

    selector {
      match_labels = {
        name = "webapp"
      }
    }

    template {
      metadata {
        labels = {
          name = "webapp"
        }
      }

      spec {
        container {
          image = "kodekloud/webapp-color:v1"
          name  = "simple-webapp"
          port {
            container_port = 8080
            protocol = "TCP"
          }
        }
      }
    }
  }
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bw04frw7q3y4aitq9fyj.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hd6yrugolhj02zd2elui.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cmjqauil7uji3vsv7tnk.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ho19kk2w6bh4aoam90pk.png)
<figcaption> Images Source: KodeKloud.com</figcaption>

------------------

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmzeduag6a1y3veswe28.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vfgwploqvrv6y2akkx32.png)

<caption>Displaying in browser - both local and remote deployments</caption>

------------------


<u>Create an AWS s3 bucket for a remote backend and a DynamoDB table for state locking</u>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/343e3hljdpxkm3gawbld.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7cs2h9ysicbsddsftndi.png)

<caption>Creating an s3 bucket in AWS for the remote state, and also enabling versioning in case rollback is needed for disaster recovery.</caption>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pmuz5qcc68cn0iymk530.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c23dx2l6dyxpnqg7074b.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vobfwcfiuu1kz67q3xqg.png)
<caption>Creating a DynamoDB table for state locking. Partition key must be "LockID" (spelling and case sensitive)</caption>


----------------------

<u>Reconfigure for Terraform configuration to use S3 bucket as remote backend</u>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mrsem0csrz065106zjyd.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k6q72hwjox48i81d43v2.png)


Updated Configuration file:

```terraform
terraform {
  required_providers {
    kubernetes = {
      source = "hashicorp/kubernetes"
      version = "2.11.0"
    }

    aws = {
      source = "hashicorp/aws"
      version = "4.49.0"
    }
  }

  backend "s3" {
    bucket = "tenon-remote-backend"
    key = "terraform.tfstate"
    region = "us-east-1"
    dynamodb_table = "tenon_state_locking"
  }
}

provider "kubernetes" {
  config_path    = "~/.kube/config"
  config_context = "docker-desktop"
  # config_path    = "/root/.kube/config"
  # config_context = "kubernetes@kubernetes_admin"
}

provider "aws" {
  shared_config_files      = ["~/.aws/config"]
  shared_credentials_files = ["~/.aws/credentials"]
  profile                  = "iamadmin-general"
}



##Service 
resource "kubernetes_service" "webapp-service" {
  metadata {
    name = "webapp-service"
  }
  spec {
    selector = {
      name = "webapp"
    }
    port {
      port = 8080
      node_port = 30080
    }
    type = "NodePort"
  }
}

## Deployment 

resource "kubernetes_deployment_v1" "frontend" {
  metadata {
    name = "frontend"
    labels = {
      name = "frontend"
    }
  }

  spec {
    replicas = 4

    selector {
      match_labels = {
        name = "webapp"
      }
    }

    template {
      metadata {
        labels = {
          name = "webapp"
        }
      }

      spec {
        container {
          image = "kodekloud/webapp-color:v1"
          name  = "simple-webapp"
          port {
            container_port = 8080
            protocol = "TCP"
          }
        }
      }
    }
  }
}
```

-----------------------------

The new backend requires reconfiguration with terraform init -reconfigure




![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rpn05f7y2vsibf2galxu.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6je9m8v87dczwbe5hlg0.png)

---

State has been refreshed successfully and Terraform is now using the remote s3 bucket as the state file backend, with state locking via the DynamoDB table


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxovyl5bbeg3g9ysymzv.png)



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b8ywbveyvk5p51mi3c7n.png)