Pipeline example using Terraform, Jenkins, Ansible, and Docker on Google Cloud-Part 1
Tags: devops, beginners, googlecloud, tutorial
Published: 2023-02-25 13:59:14


## Project brief intro:

This project I made is for the demonstration of pipelines. I utilised Terraform Cloud provisioning infras on GCP, using Ansible to configure remote VM instances for initiation and deployment, Docker image as artifact for deployment and finally, a Jenkins server integrating webhook from GitHub and further CICD process. 

This project was split into two articles for the sack of large context, I tried to template the source code so that it should be easy to follow stepwise. The first part would be the prerequisite walkthrough, provisioning infra and initialising Jenkins on CICD server. 

## Workflow

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


## Prerequisite

- OS: Linux/MacOS
- Local installation of Terraform and Ansible
- Google Cloud Platform Account
- Terraform Cloud Account
- GCP CLI

### Enable GCP APIs

- Compute Engine API
- Google Container Registry API
- Google Cloud Storage API

## Create Organization and Workspace on Terraform Cloud

[Terraform | HashiCorp Cloud Platform](https://cloud.hashicorp.com/products/terraform)

## Create a new GCP project

go to GCP console then create a new project and have your gcloud cli configured to that new project

[Google Console Page](https://console.cloud.google.com/)

## Clone Pipeline Project repo
[Pipeline Project Links](https://github.com/aks60808/Devops-Aigames)
```bash
git clone https://github.com/aks60808/Devops-Aigames
```

## Configure GCP

To make terraform cloud provisioning infra from GCP, the following are required to provide:

- Google Service Account
- Grant permission for the role
- Create a Service Account Key for further authentication

## Login to GCP first

```bash
gcloud auth login
```

### Create Service Account
replacing SA_NAME with your preferred Service Account name.
```bash
gcloud iam service-accounts create SA_NAME \
    --description="DESCRIPTION" \
    --display-name="DISPLAY_NAME"
```

### Grant IAM as an editor role

replacing PROJECT_ID with your PROJECT_ID on gcp console

```bash
gcloud projects add-iam-policy-binding PROJECT_ID \
    --member="serviceAccount:SA_NAME@PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/editor"
```

### Create a service account key

It’s up to you to determine the location of the service account key, which typically would be stored in ~/.ssh/ directory.

```bash
gcloud iam service-accounts keys create YOUR_SA_KEY_PATH \
    --iam-account=SA_NAME@PROJECT_ID.iam.gserviceaccount.com
```

## Configure Terraform to Terraform Cloud
in these .tf files, they will provision several resources including

- Google Compute Instance *2 (e2.medium) for service hosting
- Google VPC and corresponding firewall rules allowing http and ssh
- Google Container registry for docker image push in Part 2.
- Google Cloud Storage for backing up Jenkins in Part 2.

Frist, Let's get into the project directory:

```bash
cd /Devops-Aigames
```

Modify the following main.tf

```
# store terraform states on terraform cloud
terraform {
  cloud {
    # replace with your organization
    organization = "REPLACE WITH YOUR ORGANIZATION NAME"

    workspaces {
    # replace with your workspace name
      name = "REPLACE WITH YOUR WORKSPACE NAME"
    }
  }
}
```

create a file named terraform.tfvars in project directory

```bash
touch terraform.tfvars
```

add the following content to terraform.tfvars
Regions & their corresponding zones can be referred to [Google compute Regions-Zones List](https://cloud.google.com/compute/docs/regions-zones)

```bash
project_id = "REPLACE WITH　YOUR　GCP　PROJECT　ID"
region = "REPLACE WITH　YOUR　PREFERRED　REGION"
zone = "REPLACE WITH　YOUR　PREFERRED　ZONE"

# these are variables that will be used in the terraform configuration
```

### Add your GCP service account key to Terraform Cloud

Go to the Terraform Cloud service, select your right organization/workpace, click **Variables** on the left Menu Bar


![Menu Bar on terraform cloud](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arthh3o32ofe6pro17np.png)

1. Click Add variable button
2. Select Terraform variable
3. In KEY section, you MUST type in GOOGLE_CREDENTIALS
4. COPY and PASTE all content of the Service Account key(JSON file) into the Value section.
5. Don’t forget to tick Sensitive and click Save


![The place you should put your SA key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8ke98s3mjq2vpfywagc.png)


For the reasoning of the above configuration, please refer to Terraform documentation


![GCP credential configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rjzoo00ts3wer6fn98xm.png)

[Terraform Registry](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/getting_started)

## Login to Terraform Cloud

run terraform login and have the token value pasted into terminal

```bash
terraform login
```

## Initial your local terraform to terraform cloud

run the following command to get terraform connected to terraform cloud 

```bash
terraform init
```

## Start Provisioning your infra on GCP using terraform

```bash
terraform plan
terraform apply
terraform output # to see your VM ip
```

## Ansible Configure management

Now we will utilise ansible to configure the remote CICD server

### Generate ssh key if needed

```bash
# USERNAME should match your current username
ssh-keygen -t rsa -f ~/.ssh/KEY_FILENAME -C USERNAME -b 2048
```

### Visit GCE instance page click metadata>> ssh keys>>add ssh key


![GCE metadata page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubilcxb4tcuzed7c9kwp.png)


copy your generated .pub key then saved.

### Create an inventory file in project directory

```bash
touch inventory
```

put the following content into inventory file

```plaintext
[cicd]
x.x.x.x 
# replace above with CICD server IP address
[cicd:vars]
ansible_ssh_private_key_file= /path/to/your/private/ssh/key
[depolyment]
x.x.x.x 
# replace above with depolyment server IP address
[depolyment:vars]
ansible_ssh_private_key_file= /path/to/your/private/ssh/key
```

### Configure ansible.cfg file

check the location of ansible.cfg by running the following:

```bash
ansible-config --version
```

if you saw the **config file = None**, then in current directory, run

```bash
ansible-config init --disabled > ansible.cfg
```

modify the ansible.cfg by setting:

```bash
host_key_checking=False
```

then saved.

### Try reaching out CICD and Deployment server via ansible

run the following command to test the ssh connectivity of two hosts.

```bash
# -i flag specify your inventory path
ansible all -m ping -i ./inventory
```

you should see the following output:


![Ping success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flq6j7nhzatm21x6ddyy.png)



### Configure CICD server using Ansible

before start configuring, go to **ansible_playbook/ then open**

**ansible_deployment.yml** to modify the project_id, replace it with yours.

```yaml
- name: deploy app to server
  hosts: all
  become: yes
  vars:
    project_id: YOUR_GCP_PROJECT_ID
```

**cicd_server_init_config.yml** to modify the service account key and ansible_deployment.yml playbook location(ansible_deployment.yml is in project dir/ansible_playbooks)

the Path should be in absolute path format, key_dest and playbook_dest are set to jenkins home directory, so you don't need to modify them.

```yaml
- name: Activate Service account
  hosts: cicd
  become: yes
  vars:
    key_src: YOUR/LOCAL/ABSOLUTE/PATH/TO/sa_key.json 
    key_dest: /var/lib/jenkins/key.json

......

- name: Upload playbook to CICD server
  hosts: cicd
  become: yes
  vars:
    playbook_src: YOUR/LOCAL/ABSOLUTE/PATH/TO/ansible_deployment.yml
    playbook_dest: /var/lib/jenkins/ansible_deployment.yml
```

the ansible playbook will 

- pass your service account key to CICD server
- install Docker, Jenkins, and Ansible
- Configure what’s necessary for running the operations including adding jenkins to docker group and cgroup mounting
- pass deployment playbook to CICD server

in the project directory, 

```yaml
ansible-playbook -i inventory \
					ansible_playbook/cicd_server_init_config.yml
```
After completion, you should open your browser via 
![Jenkins initialisation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c07rnz72rwly2w7t3vfq.png)
PS. Initial ADMIN PWD will can be found either in the file location or the stdout on your terminal

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


## Cleanup

DO NOT CLEAN UP UNLESS YOU WANT TO SKIP PART II!

If you wish to terminate the whole setup, in the project directory run the following command:
```shell
terraform destroy
```
DON'T FORGET TO ENTER YES TO PROCEED!
## To be continued
In the next article, I will continue building Jenkins CICD pipeline to build, test, package and finally deploy for hosting a website offering algorithm-based fun games written by my good friend, [Noah](https://github.com/Gravender/AiGames)(big shoutout to you:)). 

[Part 2 article link](https://dev.to/tommyhenchuanlin/pipeline-example-using-terraform-jenkins-ansible-and-docker-on-google-cloud-part-2-2kgj)