Infrastructure as Code Transformation

Mustafa Guler
ÇSTech
Published in
6 min readDec 26, 2022

Computer systems are expanding day by day and transformations in systems are made in micro-structures as in software. The foundations of the structures designed with today’s technology are based on similar logic and are formed by using foundations close to each other. The creation and configuration of most structures are now easily done with the most diverse technologies supported. In this article, I will explain what the Infrastructure as Code (IaC) structure is and how I implemented it with Ansible and Terraform.

What is Infrastructure as Code (IaC)?

Infrastructure as Code Structure

Infrastructure as code (IaC) is the method of defining and provisioning infrastructure using definition files containing code. It enables IT Ops teams to embrace a DevOps model and accelerate application deployment, monitor operations, optimize network performance and ensure compliance in a secure, predictable manner. Most companies are increasingly using IaC platforms like HashiCorp Terraform and Red Hat Ansible to programmatically configure data centers, VMs, and cloud infrastructures. This enables IT Ops teams to work together on a unified platform to automate the management of network, cloud, and server infrastructure.

Why Infrastructure as Code (IaC)?

The biggest advantage IaC gives us is consistency. Most of the operations without IaC are done manually by the system administrators. Therefore, it will take a lot of time and effort to develop structures that are constantly produced and configured. Obtaining stable structures with IaC will greatly save energy and time.

Saving Time

IaC also provides us with integration with simplicity as a part of the CI/CD pipeline for maintenance and configurations. Any changes in the version-controlled repositories can be safely published after validation steps. The operation of this process proceeds without human intervention and ensures continuity. It will be easier to implement steps such as rollback with the outputs of traceable and loggable structures.

What is Ansible?

Ansible is a software tool that provides simple but powerful automation for cross-platform computer support. It is primarily intended for IT professionals, who use it for application deployment, updates on workstations and servers, cloud provisioning, configuration management, intra-service orchestration, and nearly anything a systems administrator does on a weekly or daily basis. Ansible doesn’t depend on agent software and has no additional security infrastructure, so it’s easy to deploy.

What is Terraform?

HashiCorp Terraform is an infrastructure as code tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share. You can then use a consistent workflow to provision and manage all of your infrastructures throughout its lifecycle. Terraform can manage low-level components like compute, storage, and networking resources, as well as high-level components like DNS entries and SaaS features.

Until this part, I proceeded through the questions of What and Why. From here, I will share code examples and explain how I implemented the structure.

Ansible-GCP Instance Example

Ansible + GCP

There are structures that we have manually installed on the cloud before. As an example, what I will do is create an instance template, instance group and make the necessary configurations with Ansible. After the IaC transformation, these structures will be deployed without manual intervention. Another purpose here is to design parametrically and to provide control of different structures through a single file. This is possible by using different parameter files or using another tool like AWX. I will not explain how AWX works in this article. I will use the template, survey, and other features provided by AWX. AWX also schedules jobs but I don’t need to use it for this. The playbook required for creating instance templates and instance groups using ansible are as follows.

main.yml

- name: Init list for labels
set_fact:
labels: "[{% for sub in instance_template_properties_labels.split(',') %}{{sub.split(':')}},{% endfor %}] "

- name: List to dict (labels)
set_fact:
labels: "{{ dict(labels) }}"

- name: Init list for tags
set_fact:
tags: []

- name: List to dict (tags)
set_fact:
tags: "{{ tags + [item] }}"
loop: "{{ instance_template_properties_tags_items.splitlines() }}"

- name: Create an instance template
google.cloud.gcp_compute_instance_template:
name: "{{ instance_template_name | default(omit) }}"
description: "{{ instance_template_description | default(omit) }}"
properties:
description: "{{ instance_template_properties_description | default(omit) }}"
labels: "{{ labels | default(omit) }}"
tags:
items: "{{ tags | default(omit) }}"
disks:
- auto_delete: "{{ instance_template_properties_disks_auto_delete | default(omit) }}"
boot: "{{ instance_template_properties_disks_boot | default(omit) }}"
initialize_params:
source_image: "{{ instance_template_properties_disks_initialize_params_source_image | default(omit) }}"
machine_type: "{{ instance_template_properties_machine_type | default(omit) }}"
network_interfaces:
- network:
selfLink: "{{ instance_template_properties_network_interfaces_network | default(omit) }}"
subnetwork:
selfLink: "{{ instance_template_properties_network_interfaces_subnetwork | default(omit) }}"
project: "{{ project | default(omit) }}"
auth_kind: "{{ auth_kind | default(omit) }}"
service_account_file: "{{ service_account_file | default(omit) }}"
state: "{{ instance_template_state | default(omit) }}"
register: instancetemplate

- name: Create an instance group manager
google.cloud.gcp_compute_instance_group_manager:
name: "{{ instance_group_manager_name | default(omit) }}"
base_instance_name: "{{ instance_group_manager_base_instance_name | default(omit) }}"
description: "{{ instance_group_manager_description | default(omit) }}"
instance_template: "{{ instancetemplate }}"
target_size: "{{ instance_group_manager_target_size | default(omit) }}"
zone: "{{ zone | default(omit) }}"
project: "{{ project | default(omit) }}"
auth_kind: "{{ auth_kind | default(omit) }}"
service_account_file: "{{ service_account_file | default(omit) }}"
state: "{{ instance_group_manager_state | default(omit) }}"

vars.yml

project: your-project
auth_kind: serviceaccount
service_account_file: "serviceaccount.json"
zone: us-central1-a
instance_template_name: "instance-template-demo"
instance_template_description: ""
instance_template_state: present
instance_template_properties_description: ""
instance_template_properties_labels: "key1:name1,key2:name2"
instance_template_properties_tags_items: |
tag1
tag2
instance_template_properties_disks_auto_delete: 'true'
instance_template_properties_disks_boot: 'true'
instance_template_properties_disks_initialize_params_source_image: "projects/centos-cloud/global/images/centos-7-v20220719"
instance_template_properties_machine_type: n1-standard-1
instance_template_properties_network_interfaces_network: "projects/your-project/global/networks/your-project-vpc"
instance_template_properties_network_interfaces_subnetwork: "projects/your-project/regions/us-central1/subnetworks/your-project-subnetwork"
instance_group_manager_name: test-object
instance_group_manager_base_instance_name: test1-child
instance_group_manager_description:
instance_group_manager_target_size: 1
instance_group_manager_state: present

default(omit) is used to make a variable optional if it is not defined. Labels and tags variables are defined by a different method to fill by using AWX survey. Therefore it is necessary to parse the values with initializing steps. All required parameters need to be defined on AWX survey. Basically, what I want to show here is how we effectively use an existing module. With this method, different modules can be used parametrically. Only some value types require extra processing. You can find alternative terraform example of this operation on my GitHub.

Terraform-GKE Example

Terraform + GCP

Another example is creating simple cloud-ready Kubernetes with terraform. There was already a manually installed Kubernetes on GCP. But at some point, we thought this might pose a problem for future repeatable operations. Which has happened. We had to recreate the system due to a problem. In a very short time, we stood up with the files we created before.

We backed up the system we installed manually using Terraformer. Terraformer is cli tool that generates tf/json and tfstate files based on existing infrastructure. In a way, it is reverse Terraform. The tool creates essential resources that you need to reuse. But it does not provide the output exactly we need. It can be improved with a parametric system. But I will go through the basic template that Terraformer creates.

It created cluster.tf and 2 node pool configuration files with output, provider, and variable files according to the system. The files were configured hard-coded without using variables. I created the bucket to store state files in the same project. After the settings for creation or modification were completed I had a partially ready Kubernetes cluster. I have obtained an effective, time-saving, and reusable structure. You can find files on my GitHub.

Conclusion

The operations that can be done with these tools, which also have other areas of use, give faster results than manual operations. Also, there are other tools like Chef, AWS CloudFormation, Puppet and etc. But we use Ansible and Terraform in our designs. The examples I mentioned above are some of the structures we use. There are other areas and topics that we can use. It changes according to need.

--

--