<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Audu Ephraim</title>
    <description>The latest articles on DEV Community by Audu Ephraim (@audu97).</description>
    <link>https://dev.to/audu97</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1089041%2Ff47b82d7-db4f-4a6b-837e-6886431e0045.jpeg</url>
      <title>DEV Community: Audu Ephraim</title>
      <link>https://dev.to/audu97</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/audu97"/>
    <language>en</language>
    <item>
      <title>Building an FIA Formula 1 Financial Regulations Chatbot with AWS S3, Kendra and Bedrock</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Wed, 23 Apr 2025 11:33:56 +0000</pubDate>
      <link>https://dev.to/audu97/building-a-formula-1-fia-formula-1-financial-regulations-chatbot-with-aws-s3-kendra-and-bedrock-3n1f</link>
      <guid>https://dev.to/audu97/building-a-formula-1-fia-formula-1-financial-regulations-chatbot-with-aws-s3-kendra-and-bedrock-3n1f</guid>
      <description>&lt;p&gt;This project was inspired by Mark Tinderholt’s post on creating a JFK assassination chatbot using the JFK files. You can find it &lt;a href="https://itnext.io/building-a-jfk-assassination-file-chatbot-with-azure-openai-and-document-intelligence-9f3dcdb5364e" rel="noopener noreferrer"&gt;Here&lt;/a&gt;. As a Formula 1 fan, I wanted to understand the sport’s complex financial regulations more, and what better way of doing that than building a project around it? So I built a chatbot that lets me query the official FIA Formula 1 Financial Regulations PDF available on the FIA official website using natural language.&lt;/p&gt;

&lt;p&gt;With AWS Kendra for document indexing and Amazon Bedrock’s Titan model for answering questions in a conversational style, I was able to turn a static document into a dynamic knowledge bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 What I Built
&lt;/h2&gt;

&lt;p&gt;A Python-based CLI application that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Queries AWS Kendra for relevant context.&lt;/li&gt;
&lt;li&gt;Uses Bedrock’s Amazon Titan Text G1 -Express model to generate chat-like answers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🧠 Why This Project?
&lt;/h2&gt;

&lt;p&gt;I wanted to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand how to combine semantic search (Kendra) with LLMs (Bedrock).&lt;/li&gt;
&lt;li&gt;understand how LLMs products are built&lt;/li&gt;
&lt;li&gt;Make a dense PDF document more explorable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 Technologies Used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS S3&lt;/strong&gt; – To store the source PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Kendra&lt;/strong&gt; – As the search engine to index and retrieve document snippets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazon Bedrock&lt;/strong&gt; – To generate natural language answers based on Kendra’s output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;boto3&lt;/strong&gt; – AWS SDK for Python.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📝 Python Script Overview
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;query_kendra(query_text, index_id)&lt;/code&gt; – Sends a query to Kendra and collects document excerpts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;query_bedrock(query, context)&lt;/code&gt; – Sends context and query to Bedrock Titan to get a conversational response.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kendra_bedrock_query(user_query, index_id)&lt;/code&gt; – Main function that stitches the two together.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🧱 Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5z2kaywciloglfxwtl80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5z2kaywciloglfxwtl80.png" alt="Image description" width="513" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI App (Python) → Kendra (document search) → Bedrock (contextual response)&lt;/li&gt;
&lt;li&gt;S3 acts as the document repository&lt;/li&gt;
&lt;li&gt;IAM roles/policies allow access to services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💡 What I Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Kendra doesn’t require Textract if the PDF text is extractable. I originally used Textract to index PDF files and store the index in S3 for Amazon Kendra to query. However, I later realized that Kendra can handle this process independently. Now, I only need Textract for specific cases, such as processing receipts or extracting text from images.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bedrock’s Titan G1 Express can process large contexts and return quality responses. I would like to do this with a more powerful model to see the difference in responses. I felt some answers would have been better.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;IAM permissions need to include both &lt;code&gt;kendra&lt;/code&gt;, &lt;code&gt;bedrock&lt;/code&gt;, &lt;code&gt;logs&lt;/code&gt;, and &lt;code&gt;s3&lt;/code&gt; access for things to sync correctly.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧾 Sample Question
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; What is cost cap administration?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Cost Cap Administration is a process of cost control that involves setting a limit on the amount of money that can be spent on a project or activity. This limit is typically set by an external organization, such as a government agency or a regulatory body, and is designed to ensure that the project or activity is completed within the allocated budget. The Cost Cap Administration process typically involves the following steps:&lt;br&gt;
    1. Establishing the cost cap: The cost cap is set by the external organization that is responsible for managing the project or activity. This limit is typically based on a variety of factors, such as the expected costs of the project, the expected revenue, and the expected risks.&lt;br&gt;
    2. Monitoring the costs: The Cost Cap Administration team monitors the costs of the project or activity on a regular basis. This includes reviewing invoices, tracking expenses, and analyzing financial data.&lt;br&gt;
    3. Adjusting the costs: If the costs of the project or activity exceed the cost cap, the Cost Cap Administration team must take action to adjust the costs. This may include reducing the scope of the project, reducing the number of employees, or reducing the amount of time that the project is scheduled to take.&lt;br&gt;
    4. Reporting the costs: The Cost Cap Administration team must report the costs of the project or activity to the external organization that is responsible for managing the project or activity. This report must include a detailed breakdown of the costs, including the amount of money that was spent, the amount of money that was saved, and the reasons for the changes in costs.&lt;br&gt;
    5. Enforcing the cost cap: The Cost Cap Administration team must enforce the cost cap. This may include issuing fines or penalties to the project or activity if the costs exceed the cost cap.&lt;br&gt;
    6. Reviewing the cost cap: The Cost Cap Administration team must review the cost cap on a regular basis to ensure that it is still appropriate. This may include adjusting the cost cap based on changes in the project or activity, or adjusting the cost cap based on feedback from the external organization that is responsible for managing the project or activity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In conclusion, the Cost Cap Administration process is a critical process for ensuring that a project or activity is completed within the allocated budget. It involves establishing the cost cap, monitoring the costs, adjusting the costs, reporting the costs, enforcing the cost cap, and reviewing the cost cap on a regular basis. By following these steps, the Cost Cap Administration team can help to ensure that the project or activity is successful and that the external organization that is responsible for managing the project or activity is satisfied with the results.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; How much is a team eligible to spend each year?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; $15,000,000 &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; What is the punishment for cost cap breach?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Based on the provided content, the punishment for cost cap breach is sanctions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q:&lt;/strong&gt; What are the sanctions for cost cap breach?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A:&lt;/strong&gt; Based on the provided content, the sanctions for cost cap breach include a financial penalty.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  📌 Future Improvements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Build a full web app around it&lt;/li&gt;
&lt;li&gt;Support multi-document uploads. This would become a full F1 rules and regulations chatbot.&lt;/li&gt;
&lt;li&gt;Save chat history&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔗 GitHub Repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/audu97/FIA-F1-financial-reg/tree/main" rel="noopener noreferrer"&gt;https://github.com/audu97/FIA-F1-financial-reg/tree/main&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Thanks for reading! Feel free to fork the project, open issues, or suggest new directions!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>llm</category>
      <category>cloud</category>
    </item>
    <item>
      <title>End-to-End workflow with Azure Devops, Docker and ArgoCD</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Sat, 17 Aug 2024 05:47:17 +0000</pubDate>
      <link>https://dev.to/audu97/end-to-end-workflow-with-azure-devops-docker-and-argocd-3hh1</link>
      <guid>https://dev.to/audu97/end-to-end-workflow-with-azure-devops-docker-and-argocd-3hh1</guid>
      <description>&lt;p&gt;This project demonstrates how an end-to-end Gitops workflow was automated from start to finish using &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure devops pipeline to build an application into a docker image and push said image to DockerHub.&lt;/li&gt;
&lt;li&gt;configure Argocd to watch repository Kubernetes configuration files and update a Kubernetes cluster based on Kubernetes configuration files. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Azure DevOps Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;k8/*'&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dockerHub&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docker-hub-service'&lt;/span&gt;  
  &lt;span class="na"&gt;imageName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ephraimaudu/azure-argo-dev'&lt;/span&gt;
  &lt;span class="na"&gt;imageTag&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.BuildId)'&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build and push image to docker hub&lt;/span&gt;
    &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
        &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
        &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myagent&lt;/span&gt;
        &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docker@2&lt;/span&gt;
          &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;containerRegistry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(dockerHub)&lt;/span&gt;
            &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(imageName)&lt;/span&gt;
            &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;buildAndPush'&lt;/span&gt;
            &lt;span class="na"&gt;Dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/Dockerfile'&lt;/span&gt; 
            &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
              &lt;span class="s"&gt;$(imageTag)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;build is triggered when a change is made in the master branch excluding files in the k8 folder. those are Kubernetes configuration files&lt;/li&gt;
&lt;li&gt;The Azure devops pipeline was configured to run on a self-hosted agent called "myagent" while running on the local machine. installation and configuration guide is pretty straightforward and can be found &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/windows-agent?view=azure-devops" rel="noopener noreferrer"&gt;Here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63go9i9g9sxw4vl7wsdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F63go9i9g9sxw4vl7wsdy.png" alt="Image description" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ArgoCD
&lt;/h3&gt;

&lt;p&gt;ArgoCD is a continuous delivery tool for Kubernetes. It automates application deployment and lifecycle management from Git repositories to specified clusters.&lt;/p&gt;

&lt;p&gt;Essentially, it ensures that your live application state matches the desired state defined in your Git repository.&lt;/p&gt;

&lt;p&gt;The kubernetes configuration file in the specified repository is constantly being watched, when they change argocd compares with the live application in the cluster if they are out of sync argocd syncs the cluster to match the configuration files.&lt;/p&gt;

&lt;p&gt;ArgoCD runs in its own namespace running its pods&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9d4rpbwi4xmyf5u9el91.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9d4rpbwi4xmyf5u9el91.png" alt="Image description" width="800" height="120"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Argocd provides a detailed web UI to manage cluster or clusters&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeb0qo44bfqejcmxal9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeb0qo44bfqejcmxal9c.png" alt="Image description" width="800" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/audu97/argo-azure-devops" rel="noopener noreferrer"&gt;link to repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>cicd</category>
    </item>
    <item>
      <title>using Ansible to deploy an application</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Sat, 10 Aug 2024 08:35:11 +0000</pubDate>
      <link>https://dev.to/audu97/using-ansible-to-deploy-an-application-48mc</link>
      <guid>https://dev.to/audu97/using-ansible-to-deploy-an-application-48mc</guid>
      <description>&lt;p&gt;This is a simple Ansible script to install packages, and configurations and to deploy a simple go application on an Azure VM.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it installs nginx&lt;/li&gt;
&lt;li&gt;installs golang&lt;/li&gt;
&lt;li&gt;configure nginx and its services&lt;/li&gt;
&lt;li&gt;and finally copies the go application binary from the local machine to vm
&lt;a href="https://github.com/audu97/ansible-nginx-go" rel="noopener noreferrer"&gt;repository contains the various scripts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>ansible</category>
      <category>azure</category>
    </item>
    <item>
      <title>Send Slack Notifications with Go AWS Lambda Functions</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Mon, 08 Jul 2024 23:51:53 +0000</pubDate>
      <link>https://dev.to/audu97/send-slack-notifications-with-go-aws-lambda-functions-1ci5</link>
      <guid>https://dev.to/audu97/send-slack-notifications-with-go-aws-lambda-functions-1ci5</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In this article, we will discuss how to create an AWS lambda function to send Slack notifications when the CPU utilization of an AWS instance reaches 50%.&lt;/p&gt;

&lt;p&gt;AWS Lambda is a serverless compute service offered by Amazon Web Services (AWS). It lets you run code without having to provision or manage servers yourself. &lt;/p&gt;

&lt;p&gt;It is event-driven i.e. your code executes in response to events triggered by other AWS services like a file upload completed in s3, an HTTP request from Amazon API Gateway or various other triggers.&lt;/p&gt;

&lt;p&gt;In this, we will be discussing how to set up Amazon Cloudwatch to monitor and collect metrics from an EC2 instance, Cloudwatch alarms based on those metrics to trigger a notification when a certain threshold or condition is met, Amazon Simple Notification service to receive these notifications and finally a lambda function subscribed to the SNS topic, which will process the notification and send a slack message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisite
&lt;/h3&gt;

&lt;p&gt;To follow along with this, the reader should have basic knowledge and understanding of&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Golang&lt;/li&gt;
&lt;li&gt;AWS and its services
### Setting up the project
First, we will start by writing out the function to send these notifications to Slack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a new go project and call it whatever you want, I called mine “lambdaFunction” in your main.go file, paste the following piece of code&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="s"&gt;"bytes"&lt;/span&gt;
   &lt;span class="s"&gt;"context"&lt;/span&gt;
   &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
   &lt;span class="s"&gt;"fmt"&lt;/span&gt;
   &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/events"&lt;/span&gt;
   &lt;span class="s"&gt;"github.com/aws/aws-lambda-go/lambda"&lt;/span&gt;
   &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;slackMessage&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"text"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snsEvent&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SNSEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;webhookURL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"https://hooks.slack.com/services/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"&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;snsEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Records&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;snsRecord&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SNS&lt;/span&gt;
        &lt;span class="n"&gt;sendSlackNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhookURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snsRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;sendSlackNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhookURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;slackMessage&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;slackMessage&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Cpu usage is above 50%"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="n"&gt;slackBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Marshal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slackMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;webhookURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;slackBody&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error creating request: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
   &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error sending request: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error response from slack: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Successfully sent Slack notification: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;lambda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Run go mod&lt;/p&gt;

&lt;p&gt;Let's try to understand what going on&lt;br&gt;
The handleRequest function&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we create a struct named &lt;code&gt;slackMessage&lt;/code&gt; to represent the message format sent to Slack, it has a single field Text which holds the message content&lt;/li&gt;
&lt;li&gt;The handleRequest function is the main function executed by the lambda runtime. It takes in two argument context and &lt;code&gt;snsEvent events.SNSEvent&lt;/code&gt; containing details about the incoming SNS notification.&lt;/li&gt;
&lt;li&gt;The function iterates through each snsRecord within the  snsEvent, it retrieves the message content from the sns.message field and calls &lt;code&gt;sendSlackNotification&lt;/code&gt; with the slack webhook URL and the message content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;sendSlackNotification&lt;/code&gt; function&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function takes two arguments &lt;code&gt;webhookURL&lt;/code&gt;: the URL where the Slack notifications are sent and &lt;code&gt;message&lt;/code&gt;: the message content to be sent to Slack. &lt;/li&gt;
&lt;li&gt;I provided a predefined message “cpu usage is above 50%” appended to the provided message.
It then marshals the the slackMessage struct into JSON format using &lt;code&gt;json.marshal&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;An HTTP post request is created using &lt;code&gt;http.NewRequest&lt;/code&gt; with the slack webhook URL, the JSON formatted body and the content type header set to &lt;code&gt;application.json&lt;/code&gt;
*The &lt;code&gt;request&lt;/code&gt; is sent using an http.client and the response is received
The main function is only used for local testing. In a lambda environment lamba.start function is automatically called with handleRequest as the entry point&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting slack webhook URL
&lt;/h3&gt;

&lt;p&gt;To obtain the Slack webhook URL that allows you to send messages to Slack, navigate to &lt;a href="https://api.slack.com/apps" rel="noopener noreferrer"&gt;https://api.slack.com/apps&lt;/a&gt;. Make sure you are signed in to your Slack account before proceeding.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on “Create new app” on the top right side 
A dialog box will appear. Select "From scratch" to create a new app.  Following that, another dialog box will pop up. Here, you can name your app "cpu-alert" and then choose the specific Slack workspace where you want the messages to be sent. I already created a test workspace “site reliability test”&lt;/li&gt;
&lt;li&gt;Click “Create app”&lt;/li&gt;
&lt;li&gt;In the “Add features and functionality” section select “Incoming webhooks”&lt;/li&gt;
&lt;li&gt;Toggle the activate incoming webhook button to “on” Navigate back again and scroll to the “install app section”&lt;/li&gt;
&lt;li&gt;Click "install to Workspace" then we will choose the channel we want Slack to send messages to. Then click allow.&lt;/li&gt;
&lt;li&gt;Go back to “Add features and functionality” and select “Incoming webhooks”&lt;/li&gt;
&lt;li&gt;Scroll down to find our webhook URL, then copy and paste it into our code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step is to create a deployment package for our Go app&lt;br&gt;
We will build the application. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open a terminal in the project's working directory.run &lt;code&gt;GOOS=linux go build -o main main.go&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create a ‘bootstrap’ file
Create a file named ‘bootstrap’ in the project root directory with the following content 
```
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  !/bin/sh
&lt;/h1&gt;

&lt;p&gt;./main&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Make the bootstrap file executable
* Run `chmod +x bootstrap`
* Zip the executable and the bootstrap file by running `zip function.zip main bootstrap`

Uploading the lamba function

* Navigate to the AWS management console
* Search for lambda, and create a new function
* Give it a name of your choice
* Select “Author from scratch”
* For the runtime, select Amazon linux 2023
* Click select function
* When the function is done creating scroll down and locate the “Upload from” option
* Select your function.zip file NOT the entire folder containing the code
* Save it
* Locate the runtime setting section and click on edit
* Set the handler to bootstrap and save it

In the next step, we'll configure a trigger for the Lambda function. This trigger defines the event that will prompt the function to send a message to Slack

As I mentioned earlier that trigger will be when cpu usage of a virtual machine is &amp;gt;= 50%

To achieve this functionality, the first step involves creating an EC2 instance.

When this is done we need to configure Cloudwatch to monitor and collect metrics

* Search for Cloudwatch and open it
* Select create alarms
* Choose Select metrics
* Select ec2
* Select per instance metrics
* Select CPUUtilization metric

In the condition section
* Select Greater/Equal for the threshold
* Define the threshold value as “50”
* Click next
On the next page locate the notification section
* We will leave the alarm state trigger as it is “In Alarm”
* Select the “create new topic” option
* Enter a unique name, you can also enter an email to receive notifications
* Select Create topic
* On the next page enter a unique alarm name
Then create alarm

We will head back to our lambda function
* Select “add trigger”
* In the “Select a source” field, Search for "sns" and select it
* Select the topic you created earlier and click "add"
### Testing
We’ve finished putting together the different parts of our simple infrastructure, now it's time to test.

To test that this works, we need to put our VM under a sort of stress test. This test generates a high CPU load. To perform this test we are going to be using the “stress” tool in linux.

First and foremost we need to install "stress" tool in our EC2 inatance.connect to the EC2 instance and Run the following commands
`sudo apt-get update`
`sudo apt-get install stress`
Use the following command to stress test your CPU
`stress --cpu 4 --timeout 300`
This example uses 4 CPU workers(number of parallel processes or threads) for 300 seconds(5 mins). You can adjust the number of workers and seconda as it suits you.

Open Slack and wait you should get an alert that looks like this

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/taas6tf5hvlp27qymsg3.png)
### Common Errors you might Encounter

While running your stress test, you might notice the state of Cloudwatch change to  “insufficient data” which might cause the alarm to delay for a bit. To fix this
* Open the Cloudwatch console
* Navigate to alarms and select your specific alarm
* Click on action then edit
* Scroll down to the missing data treatment section
* Select “Treat missing data as ignore(maintain the current state)”
* Save the alarm
### Conclusion
So far, we have explored how to write and set up a simple Lambda function in Go. Additionally, we’ve configured CloudWatch to monitor and collect metrics, set up CloudWatch alarms to trigger when specific thresholds are met, and established an SNS topic to receive these alarms. The purpose of the SNS topic is to trigger our Lambda function, which sends a Slack message.

I trust you found this enjoyable and informative. Should there be any errors or if any part was not explained clearly or you think I missed something, please feel free to reach out. Your feedback is highly valued. Thank You!

the link to the github repository is found [Here](https://github.com/audu97/lambdaFunction)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>aws</category>
      <category>go</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>Jenkins Ci/Cd Pipeline to Build a Go Application into a Docker Image with Multistage build</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Sat, 29 Jun 2024 13:58:44 +0000</pubDate>
      <link>https://dev.to/audu97/jenkins-cicd-pipeline-to-build-a-go-application-into-a-docker-image-with-multistage-build-394j</link>
      <guid>https://dev.to/audu97/jenkins-cicd-pipeline-to-build-a-go-application-into-a-docker-image-with-multistage-build-394j</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;In this article, i will be discussing how I implemented a ci/cd pipeline from scratch to build a simple Golang application into a docker image and push said image to Docker hub&lt;/p&gt;

&lt;p&gt;The stages of the said pipeline include checking out the source code repository, in this case, git, running analysis on the source code with sonar cube to check for vulnerabilities, building the source code into a docker image using multi-stage build to reduce the size of the image, and finally pushing the image into a docker hub repository&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites:
&lt;/h3&gt;

&lt;p&gt;Because the installation and configuration of these tools are long and a subject of another topic, I won't be discussing them here today. &lt;/p&gt;

&lt;p&gt;The reader should have the following installed and configured and have basic knowledge and understanding of these tools if they wish to follow along.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Golang&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;IDE&lt;/li&gt;
&lt;li&gt;Jenkins&lt;/li&gt;
&lt;li&gt;Sonarcube&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reader should also have the following accounts signed in&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker hub&lt;/li&gt;
&lt;li&gt;Sonarcloud&lt;/li&gt;
&lt;li&gt;Jenkins&lt;/li&gt;
&lt;li&gt;GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up Project Files
&lt;/h3&gt;

&lt;h3&gt;
  
  
  The Application
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier I’ll be using a simple application written in Golang, this application has three different routes that print three different messages to the browser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;firstEndPointHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"this is the first endpoint"&lt;/span&gt;
   &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;secondEndPointHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"second endpoint"&lt;/span&gt;
   &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;thirdEndPointHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"second endpoint"&lt;/span&gt;
   &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/first"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;firstEndPointHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/second"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;secondEndPointHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/third"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thirdEndPointHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8081"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dockerfile
&lt;/h3&gt;

&lt;p&gt;As you all know to build an application into a docker image I’ll need to use a docker file, where I'll specify the build for the image&lt;/p&gt;

&lt;p&gt;In this docker file, I’ll be using a multistage build. With multistage builds, you can drastically reduce the size of a docker image and optimize the image by using multiple FROM statements, and each of them begins a new stage of a build. Each FROM statement can use a different base image.&lt;/p&gt;

&lt;p&gt;The essence of this is to be able to copy only the necessary artefacts from one stage to another and leave the ones you don't need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:1.22.4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;


&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; go.mod ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download


&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; *.go ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-o&lt;/span&gt; /test-app


&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /test-app /test-app&lt;/span&gt;


&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8081&lt;/span&gt;


&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/test-app"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first stage uses the golang:1.22.4 as the base image and it is named builder&lt;br&gt;
Sets the working directory to /app&lt;br&gt;
Copies &lt;code&gt;go.mod&lt;/code&gt; file and download all dependencies&lt;br&gt;
Copies all files that end with the .go extension&lt;br&gt;
Builds the go binary with &lt;code&gt;CGO_ENABLED=0&lt;/code&gt; and &lt;code&gt;GOOS=linux&lt;/code&gt; and produces an output image &lt;code&gt;/test-app&lt;/code&gt;&lt;br&gt;
The second stage of the build:&lt;br&gt;
Uses the base image: scratch which is an empty image&lt;br&gt;
Copies the built &lt;code&gt;/test-app&lt;/code&gt; binary from the builder stage&lt;br&gt;
Exposes port 8081&lt;br&gt;
Specifies the command to run the binary: &lt;code&gt;CMD[“/test-app”]&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Jenkinsfile
&lt;/h3&gt;

&lt;p&gt;The Jenkinsfile defines the pipeline stages and the steps executed during the pipeline. It must be placed in the root directory of the project for Jenkins to discover and initiate the pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;


   &lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;go&lt;/span&gt; &lt;span class="s1"&gt;'golang'&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="n"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;DOCKERHUB_CREDENTIALS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dockerhub'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;DOCKER_IMAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ephraimaudu/test-app'&lt;/span&gt;
       &lt;span class="n"&gt;GITHUB_CREDENTIALS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'git-secret'&lt;/span&gt;
       &lt;span class="n"&gt;SONAR_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'SONAR_TOKEN'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;


   &lt;span class="n"&gt;stages&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Checkout'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
           &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
               &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"checking out repo"&lt;/span&gt;
               &lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="nl"&gt;url:&lt;/span&gt; &lt;span class="s1"&gt;'https://github.com/audu97/test-project'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;branch:&lt;/span&gt; &lt;span class="s1"&gt;'master'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
               &lt;span class="nl"&gt;credentialsId:&lt;/span&gt; &lt;span class="s2"&gt;"${GITHUB_CREDENTIALS}"&lt;/span&gt;
           &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Run SonarQube Analysis'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
               &lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'starting analysis'&lt;/span&gt;
                   &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s1"&gt;'/usr/local/sonar/bin/sonar-scanner -X -Dsonar.organization=eph-test-app -Dsonar.projectKey=eph-test-app-test-go-app -Dsonar.sources=. -Dsonar.host.url=https://sonarcloud.io'&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
           &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Run Docker Build'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
           &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
               &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"starting docker build"&lt;/span&gt;
                    &lt;span class="n"&gt;sh&lt;/span&gt; &lt;span class="s2"&gt;"docker build build -t ${DOCKER_IMAGE}:${env.BUILD_ID} ."&lt;/span&gt;
                    &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"docker built successfully"&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
           &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="n"&gt;stage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'push to docker hub'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
           &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
               &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"pushing to docker hub"&lt;/span&gt;
               &lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
                   &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRegistry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://index.docker.io/v1/'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'dockerhub'&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
                       &lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"${DOCKER_IMAGE}:${env.BUILD_ID}"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                   &lt;span class="o"&gt;}&lt;/span&gt;
               &lt;span class="o"&gt;}&lt;/span&gt;
               &lt;span class="n"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"done"&lt;/span&gt;
           &lt;span class="o"&gt;}&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;


   &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;always&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;cleanWs&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt;  To use these credentials in the environment variables, you should add them in the “Credentials” section within the “Manage Jenkins” option of the Jenkins UI. This way, your Jenkins jobs can securely access the necessary credentials during their execution.&lt;br&gt;
The agent specifies that the pipeline can run on any available executor in the Jenkins environment&lt;br&gt;
Environment variables: defines various environment variables used in the pipeline execution. &lt;code&gt;DOCKERHUB_CREDENTIALS&lt;/code&gt; -contains credentials for signing in to docker hub, &lt;code&gt;GITHUB_CREDENTIALS&lt;/code&gt; -contains credentials to use to sign in to GitHub to check out the specified repository, &lt;code&gt;SONAR_TOKEN&lt;/code&gt;- also serves as credentials for the Sonar cloud, where I can view the code analysis, DOCKER_IMAGE-specifies the name I want for the docker image.&lt;br&gt;
Stages: the pipeline consists of several stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checkout: This stage checks out the code from the specified GitHub repository&lt;/li&gt;
&lt;li&gt;Run sonarqube analysis: executes sonar cube analysis on the code base. Sonar cube is a tool for static analysis on a code base by analyzing it statically. It detects bugs, vulnerabilities and code smells. &lt;/li&gt;
&lt;li&gt;Run docker build: builds a docker image using the specified dockerfile.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Push to docker hub: pushes the built docker image to docker hub&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Post processing:  the post section ensures that the workspace is cleaned up after the pipeline execution, even if the pipeline fails&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;p&gt;The biggest challenge I faced, which took considerable time to resolve, was that Jenkins could not locate Docker to execute the Docker build stage in my pipeline because I had installed both Jenkins and Docker using snaps. This resulted in repeated pipeline failures.&lt;br&gt;
To overcome this issue, I uninstalled the snap versions of both Jenkins and Docker. Following that, I installed them following the instructions provided in their documentation. This approach solved my problem by allowing Jenkins to interact with Docker successfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This project has provided valuable insights into the importance and the need for CI/CD pipelines. Additionally, it emphasizes multistage Docker image builds and includes security considerations during the build stage (shifting security left) by using SonarQube.&lt;/p&gt;

&lt;p&gt;The link to the repository containing the entire project can be located &lt;a href="https://github.com/audu97/test-project"&gt;HERE&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cicd</category>
      <category>jenkins</category>
      <category>go</category>
    </item>
    <item>
      <title>Deploying a Kubernetes Cluster on Azure Kubernetes Service(AKS) with Terraform</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Sun, 16 Jun 2024 15:43:15 +0000</pubDate>
      <link>https://dev.to/audu97/deploying-a-kubernetes-cluster-on-azure-kubernetes-serviceaks-with-terraform-1b8g</link>
      <guid>https://dev.to/audu97/deploying-a-kubernetes-cluster-on-azure-kubernetes-serviceaks-with-terraform-1b8g</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Kubernetes, often abbreviated as K8s, is an open-source platform designed to automate deploying, scaling, and operating application containers. It allows you to manage containerized applications across a cluster of machines efficiently&lt;/p&gt;

&lt;p&gt;Azure Kubernetes Service(AKS): Is a managed Kubernetes service offered by Microsoft as part of the Azure cloud platform. It provides a way for organizations to deploy and manage their containerized applications at scale, leveraging the powerful features of Kubernetes. &lt;/p&gt;

&lt;p&gt;By providing a fully managed service that handles many of the underlying infrastructure and management chores, AKS makes it easier to build and operate Kubernetes clusters. &lt;/p&gt;

&lt;p&gt;Because of this, businesses can concentrate on their apps and services rather than worrying about the infrastructure as a whole.&lt;/p&gt;

&lt;p&gt;Terraform is an open-source infrastructure-as-code software tool created by HashiCorp. It allows users to define and provision their infrastructure on different cloud platforms and define services using a high-level configuration language known as HashiCorp Configuration Language (HCL), or optionally JSON.&lt;/p&gt;

&lt;p&gt;In this article, I will be discussing how I created an AKS cluster on Azure entirely using Terraform, provisioned an NGINX image and set up Prometheus and Grafana for monitoring and alerting.&lt;/p&gt;

&lt;p&gt;This article assumes the reader has a basic understanding of Azure and Kubernetes and also has the Azure CLI installed and signed in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the cluster
&lt;/h3&gt;

&lt;p&gt;To begin, I created a directory named &lt;code&gt;azure-aks&lt;/code&gt; to store my Terraform scripts. Then, I created a &lt;code&gt;main.tf&lt;/code&gt; file where I will write the Terraform configurations for my resources.&lt;/p&gt;

&lt;p&gt;In the main.tf file, I began by defining the providers that I will need for this project. &lt;/p&gt;

&lt;p&gt;Terraform Providers are plugins that implement resource types and data sources. They serve as a bridge between Terraform and a service or platform, such as Azure, AWS, Kubernetes, etc.&lt;/p&gt;

&lt;p&gt;In my case, I will need providers for Azure to communicate with Azure services, a provider for Kubernetes to create Kubernetes services, and Helm to access Helm charts for Prometheus and Grafana.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;azurerm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/azurerm"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"3.107.0"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;


   &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/kubernetes"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.30.0"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;


   &lt;span class="nx"&gt;helm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/helm"&lt;/span&gt;
     &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.13.2"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that i ran &lt;code&gt;terraform init&lt;/code&gt; so terraform downloads the plugin and the neccessary dependencies needed.&lt;br&gt;
Next  creating resource groups and the Kubernetes cluster&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_resource_group"&lt;/span&gt; &lt;span class="s2"&gt;"aks-resource"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aks-resources"&lt;/span&gt;
 &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"France Central"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_kubernetes_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"test_cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-aks1"&lt;/span&gt;
 &lt;span class="nx"&gt;location&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_resource_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aks-resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;
 &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_resource_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aks-resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
 &lt;span class="nx"&gt;dns_prefix&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"testaks"&lt;/span&gt;


 &lt;span class="nx"&gt;default_node_pool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
   &lt;span class="nx"&gt;node_count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
   &lt;span class="nx"&gt;vm_size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Standard_D2_v2"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SystemAssigned"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Created a resource group with the name &lt;code&gt;aks-resource&lt;/code&gt;  in the “France Central” region and an Azure Kubernetes Service(AKS) named “example-aks1” within that resource group. The AKS cluster will have a default node pool with 2(which can be increased to suit your need) nodes of size “Standard_D2_v2” and it will use a system-assigned managed identity. The cluster is also tagged “Environment: Production”.&lt;/p&gt;

&lt;p&gt;Once this is done, I ran the &lt;code&gt;terraform plan&lt;/code&gt; command to see the resources that will be provisioned then &lt;code&gt;terraform apply&lt;/code&gt; to provision these resources. &lt;/p&gt;

&lt;p&gt;The reason I did this was, in other to get the kubeconfig file I have to run the command &lt;br&gt;
&lt;code&gt;az aks get-credentials --resource-group &amp;lt;ResourceGroupName&amp;gt; --name &amp;lt;AKSClusterName&amp;gt;&lt;/code&gt; (replaced ResourceGroupName and AKSClusterName with the name of my resource group and my cluster name)&lt;/p&gt;

&lt;p&gt;The kubeconfig file is used to allow access to the Kubernetes clusters. It contains the necessary details to connect to the cluster, such as cluster API server addresses, user credentials, and namespaces. &lt;/p&gt;

&lt;p&gt;This file is used by Kubectl and other Kubernetes client applications to communicate with the cluster’s API server and manage Kubernetes resources. &lt;/p&gt;

&lt;p&gt;Essentially, it’s like a key that allows you to access and control your Kubernetes cluster on the cloud.&lt;/p&gt;

&lt;p&gt;After running the &lt;code&gt;az aks get-credentials --resource-group &amp;lt;ResourceGroupName&amp;gt; --name &amp;lt;AKSClusterName&amp;gt;&lt;/code&gt; command the terminal outputs where the kubeconfig file is saved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_kubernetes_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"test_cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_kubernetes_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
 &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_resource_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aks-resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data block is used to fetch data about an existing aks cluster. It retrieves information about the aks cluster with the specified name and resource group, which is to be used somewhere else in the terraform configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azurerm_kubernetes_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kube_config_raw&lt;/span&gt;
 &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ephraim/.kube/config"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resource "local_file" "kubeconfig" block creates a local file that contains the kubeconfig of the retrieved AKS cluster. This kubeconfig is necessary to interact with my Kubernetes cluster using kubectl or other Kubernetes tools. &lt;/p&gt;

&lt;p&gt;The content of the file is the raw kubeconfig data from the AKS cluster, and it’s being saved to a specified path on my local machine (/home/ephraim/.kube/config)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"wait_for_kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sleep 10"&lt;/span&gt;


 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;config_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;config_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The null resource block is used to introduce a delay in the terraform execution, which pauses the execution for 10 seconds to ensure that the kubeconfig file is fully written.&lt;br&gt;
The provider "kubernetes" block configures the Kubernetes provider for Terraform, which allows me to manage my Kubernetes resources with Terraform. It uses the kubeconfig file created by the &lt;code&gt;local_file.kubeconfig&lt;/code&gt; resource to connect to my AKS cluster.&lt;br&gt;
Similarly, the provider "helm" block configures the Helm provider, which lets me deploy Helm charts to my Kubernetes cluster. It also uses the same kubeconfig file for connectivity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_namespace"&lt;/span&gt; &lt;span class="s2"&gt;"test_namespace"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"monitoring"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;


 &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resource “kubernetes namespace” creates a kubernetes namespace called “monitoring”&lt;/p&gt;

&lt;p&gt;A Kubernetes namespace is a way to divide cluster resources between multiple users. It is some sort of cluster within the Kubernetes cluster but for similar tasks or similar resources. Or let me say, it is used for grouping similar cluster resources to bolster organisation. &lt;br&gt;
In this namespace, I’m going to be provisioning Prometheus and Grafana.&lt;br&gt;
The “depends_on” attribute ensures that the namespace is not created until the &lt;code&gt;local_file.kubeconfig&lt;/code&gt; file is applied. This means that Terraform will wait for the kubeconfig file to be available before it attempts to create the namespace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"helm_release"&lt;/span&gt; &lt;span class="s2"&gt;"prom-helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prometheus"&lt;/span&gt;
   &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://prometheus-community.github.io/helm-charts"&lt;/span&gt;
   &lt;span class="nx"&gt;chart&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prometheus"&lt;/span&gt;
   &lt;span class="nx"&gt;namespace&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
   &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"helm_release"&lt;/span&gt; &lt;span class="s2"&gt;"graf-helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grafana"&lt;/span&gt;
   &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://grafana.github.io/helm-charts"&lt;/span&gt;
   &lt;span class="nx"&gt;chart&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grafana"&lt;/span&gt;
   &lt;span class="nx"&gt;namespace&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
   &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resource "helm_release" "prom-helm" block will deploy Prometheus from the specified Helm chart repository. It sets the release name to Prometheus, uses the chart from the Prometheus community Helm repository, and deploys it to the monitoring namespace created by the &lt;code&gt;kubernetes_namespace.test_namespace&lt;/code&gt; resource.&lt;/p&gt;

&lt;p&gt;The resource "helm_release" "graf-helm" block does the same for Grafana, deploying it from the Grafana Helm chart repository with the release name Grafana to the same monitoring namespace.&lt;br&gt;
Both resources have a depends_on attribute that ensures they are created after the monitoring namespace has been created. Since they are both being deployed in the monitoring namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_deployment"&lt;/span&gt; &lt;span class="s2"&gt;"nginx_depl"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx-deployment"&lt;/span&gt;
     &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;replicas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;


     &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;match_labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;


     &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;


       &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
           &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx:latest"&lt;/span&gt;


           &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;container_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This defines a Kubernetes deployment named “nginx-deployment” that will be created in the monitoring namespace. This deployment will set up two NGINX pods running in my Kubernetes cluster within the monitoring namespace serving content on port 80. &lt;br&gt;
The depends_on attribute also makes sure it is created only after the namespace has been created&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_service"&lt;/span&gt; &lt;span class="s2"&gt;"nginx-service"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx-service"&lt;/span&gt;
     &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;


   &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;


     &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
       &lt;span class="nx"&gt;target_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;


     &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"LoadBalancer"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This resource creates a Kubernetes service called “nginx-service” also in the monitoring namespace of type LoadBalancer. This also depends on the namespace created earlier&lt;br&gt;
the full code looks like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;azurerm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/azurerm"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"3.107.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/kubernetes"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.30.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;helm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/helm"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2.13.2"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"azurerm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# Configuration options&lt;/span&gt;
  &lt;span class="nx"&gt;features&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_resource_group"&lt;/span&gt; &lt;span class="s2"&gt;"aks-resource"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aks-resources"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"France Central"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_kubernetes_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"test_cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"example-aks1"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_resource_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aks-resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_resource_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aks-resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;dns_prefix&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"testaks"&lt;/span&gt;

  &lt;span class="nx"&gt;default_node_pool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
    &lt;span class="nx"&gt;node_count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;vm_size&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Standard_D2_v2"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


  &lt;span class="nx"&gt;identity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SystemAssigned"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"azurerm_kubernetes_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"test_cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_kubernetes_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;resource_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_resource_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aks-resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"local_file"&lt;/span&gt; &lt;span class="s2"&gt;"kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azurerm_kubernetes_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kube_config_raw&lt;/span&gt;
  &lt;span class="nx"&gt;filename&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/home/ephraim/.kube/config"&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"wait_for_kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sleep 10"&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;config_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;config_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_namespace"&lt;/span&gt; &lt;span class="s2"&gt;"test_namespace"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"monitoring"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;local_file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"helm_release"&lt;/span&gt; &lt;span class="s2"&gt;"prom-helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prometheus"&lt;/span&gt;
    &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://prometheus-community.github.io/helm-charts"&lt;/span&gt;
    &lt;span class="nx"&gt;chart&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prometheus"&lt;/span&gt;
    &lt;span class="nx"&gt;namespace&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"helm_release"&lt;/span&gt; &lt;span class="s2"&gt;"graf-helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grafana"&lt;/span&gt;
    &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://grafana.github.io/helm-charts"&lt;/span&gt;
    &lt;span class="nx"&gt;chart&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"grafana"&lt;/span&gt;
    &lt;span class="nx"&gt;namespace&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_deployment"&lt;/span&gt; &lt;span class="s2"&gt;"nginx_depl"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx-deployment"&lt;/span&gt;
      &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;replicas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

      &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;match_labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
            &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx:latest"&lt;/span&gt;

            &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;container_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes_service"&lt;/span&gt; &lt;span class="s2"&gt;"nginx-service"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx-service"&lt;/span&gt;
      &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
        &lt;span class="nx"&gt;target_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"LoadBalancer"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;kubernetes_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_namespace&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"client_certificate"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_kubernetes_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kube_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;client_certificate&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"kube_config"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;azurerm_kubernetes_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kube_config_raw&lt;/span&gt;

  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deployment
&lt;/h3&gt;

&lt;p&gt;To deploy the finalized infrastructure to Azure, I will need to run &lt;code&gt;terraform plan&lt;/code&gt; to preview the resources that will be created. Following this, I'll execute &lt;code&gt;terraform apply&lt;/code&gt; to provision these resources. This process may take some time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Verify Deployment
&lt;/h3&gt;

&lt;p&gt;To verify the successful deployment, I navigated to the ‘Connect’ tab in the cluster portal, where Azure provides commands for authentication and connection to my cluster. After executing these commands, I successfully connected to my cluster. To view all my deployments, I ran the command &lt;code&gt;kubectl get deployments --namespace monitoring&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Everything seems to be up and running correctly&lt;/p&gt;

&lt;p&gt;Additionally, I needed to verify if the Nginx service was set up correctly. Once Nginx, of type LoadBalancer, had been deployed, Kubernetes provisioned an external IP. To access it, I ran the command &lt;code&gt;kubectl get svc nginx-service --namespace monitoring&lt;/code&gt;. In the ‘External IP’ column, I copied and pasted the IP address into a browser. Nginx is running correctly!&lt;/p&gt;

&lt;p&gt;Ran “terraform destroy” to delete and remove all resources&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges
&lt;/h3&gt;

&lt;p&gt;The major challenge I faced was obtaining and using the kubeconfig file. I later realized that I needed to create the resource group and cluster first, then retrieve the kubeconfig file, before proceeding to create the other resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this article, I’ve walked through the process of deploying a Kubernetes cluster on Azure Kubernetes Service (AKS) using Terraform. I deployed an AKS cluster and configured Kubernetes resources, including Nginx, Prometheus, and Grafana for my application’s needs&lt;/p&gt;

&lt;p&gt;To take this further I intend to explore topics such as auto-scaling, continuous deployment pipelines, and multi-region clusters to further enhance your Kubernetes infrastructure.&lt;/p&gt;

&lt;p&gt;the github repo for the full code can be found &lt;a href="https://github.com/audu97/azure-aks"&gt;Here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aure</category>
      <category>devops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Azure Resume Challenge Using Pulumi, Golang, and Azure Blob Storage</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Thu, 06 Jun 2024 12:56:03 +0000</pubDate>
      <link>https://dev.to/audu97/azure-resume-challenge-using-pulumi-golang-and-azure-blob-storage-299k</link>
      <guid>https://dev.to/audu97/azure-resume-challenge-using-pulumi-golang-and-azure-blob-storage-299k</guid>
      <description>&lt;p&gt;I undertook the resume challenge on Azure. More about the resume challenge can be found in the AWS resume challenge which can be found &lt;a href="https://dev.to/audu97/aws-resume-challenge-using-pulumi-golang-aws-s3-and-aws-cloudfront-3005"&gt;Here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The whole process was almost the same as the AWS challenge except for a few differences, which I will be pointing out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up the environment
&lt;/h3&gt;

&lt;p&gt;Setting up the environment remained consistent except for a few changes when starting the project, I used “pulumi new azure-go” for an Azure project&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Infrastructure
&lt;/h3&gt;

&lt;p&gt;Because in Azure, every resource belongs to a resource group and blob storages belong to storage groups, I had to create a resource group and a storage group, also I had to create a static website on an Azure storage account.&lt;/p&gt;

&lt;p&gt;Since Azure Blob Storage is an object store similar to AWS S3, I need to handle each file in my website folder, which consists of JavaScript, jQuery, and CSS code, as an object. To do this, I'll need to traverse the folder and create a corresponding blob for each file.&lt;/p&gt;

&lt;p&gt;I created a new file &lt;code&gt;blobFolder.go&lt;/code&gt; and added the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;BlobFolder&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceState&lt;/span&gt;

   &lt;span class="n"&gt;containerName&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringOutput&lt;/span&gt; &lt;span class="s"&gt;`pulumi:"containerName"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewBlobFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;containerName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;siteDir&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;FolderArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;BlobFolder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;BlobFolder&lt;/span&gt;
   &lt;span class="c"&gt;// Stack exports&lt;/span&gt;
   &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterComponentResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pulumi:example:BlobFolder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c"&gt;//creating a resource group&lt;/span&gt;
   &lt;span class="n"&gt;resourceGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resources&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewResourceGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"new-resource-group"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c"&gt;//creating an azure storage account&lt;/span&gt;
   &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStorageAccount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"newstorage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageAccountArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;resourceGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;Sku&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SkuArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Standard_LRS"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StorageV2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c"&gt;//creating a static website&lt;/span&gt;
   &lt;span class="n"&gt;staticWebsite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStorageAccountStaticWebsite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"staticWebsite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StorageAccountStaticWebsiteArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;AccountName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;resourceGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;IndexDocument&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c"&gt;// For each file in the directory, create a blob object&lt;/span&gt;
   &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BlobArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;resourceGroup&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;AccountName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ContainerName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;staticWebsite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;            &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFileAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeByExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
         &lt;span class="p"&gt;})&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"staticWebsite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PrimaryEndpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;folderArgs&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;FolderArgs&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FolderArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ElementType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;reflect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reflect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeOf&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;folderArgs&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Elem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then in &lt;code&gt;main.go&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewBlobFolder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"resume-container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"./website"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;FolderArgs&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bucketName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This project provided valuable insights into how both cloud platforms, Azure and AWS, are different yet very similar. Over time, I plan to extend this project and explore using different infrastructures.&lt;/p&gt;

&lt;p&gt;link to the github repo &lt;a href="https://github.com/audu97/azure-resume-challenge.git"&gt;https://github.com/audu97/azure-resume-challenge.git&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>go</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>AWS Resume Challenge using Pulumi, Golang, AWS S3 and AWS CloudFront</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Mon, 03 Jun 2024 01:07:34 +0000</pubDate>
      <link>https://dev.to/audu97/aws-resume-challenge-using-pulumi-golang-aws-s3-and-aws-cloudfront-3005</link>
      <guid>https://dev.to/audu97/aws-resume-challenge-using-pulumi-golang-aws-s3-and-aws-cloudfront-3005</guid>
      <description>&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;I recently undertook the AWS resume challenge, which was quite interesting, so I decided to write about it. &lt;/p&gt;

&lt;p&gt;The primary goal of the AWS resume challenge is for participants to build a static personal website using cloud services. &lt;/p&gt;

&lt;p&gt;it encourages hands-on experience with various cloud technologies including infrastructure as code(IaC), serverless computing and storage devices.&lt;/p&gt;

&lt;p&gt;For this challenge, I utilised the following:&lt;/p&gt;

&lt;p&gt;Amazon Web Service(AWS): Served as my cloud provider.&lt;br&gt;
AWS S3: Acted as a storage unit to host my static files.&lt;br&gt;
AWS Cloudfront: as a content delivery network, to serve the static webpage. A content delivery network(CDN) is a geographically distributed group of servers that caches content close to users. &lt;br&gt;
Pulumi: Used as my infrastructure as code(IaC) tool. Pulumi is an IaC that lets you describe and provision your cloud infrastructure using various programming languages.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Setting Up the Environment
&lt;/h3&gt;

&lt;p&gt;The first steps were to install the necessary tools and software including Golang, an IDE(Goland) or any IDE of your choice and pulumi.&lt;br&gt;
Next was to configure pulumi to work with my AWS account using the AWS CLI&lt;br&gt;
Made sure I had the necessary permission to create and manage AWS resources in my AWS account.&lt;br&gt;
Created a directory to initialise the Pulumi project. To do this I opened my terminal and ran the following command &lt;code&gt;mkdir aws-resume-challenge &amp;amp;&amp;amp; cd aws-resume-challenge&lt;/code&gt; This creates a new folder and navigates into it. &lt;br&gt;
Then I ran &lt;code&gt;pulumi new aws-go&lt;/code&gt; This creates a new pulumi aws project and downloads the go sdk. This also generates some files that are necessary for the program to execute successfully. &lt;/p&gt;
&lt;h3&gt;
  
  
  3.Creating the infrastructure
&lt;/h3&gt;

&lt;p&gt;I copied my static website folder which contains some HTML, CSS, JavaScript, and jQuery code and my index.html file into the root of my project.&lt;/p&gt;

&lt;p&gt;Because I have a folder containing about 6 different folders with different files I need an object for each file(each file is a separate resource). I need a program to crawl the directory and add a resource (Bucket object) for each file. I’ll use a component that will handle the folder, to do this I created another go file and called it s3folder.go which contains the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Folder&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceState&lt;/span&gt;

   &lt;span class="n"&gt;bucketName&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IDOutput&lt;/span&gt;     &lt;span class="s"&gt;`pulumi:"bucketName"`&lt;/span&gt;
   &lt;span class="n"&gt;websiteUrl&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StringOutput&lt;/span&gt; &lt;span class="s"&gt;`pulumi:"websiteUrl"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewS3Folder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;siteDir&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;FolderArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Folder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="n"&gt;Folder&lt;/span&gt;
   &lt;span class="c"&gt;// Stack exports&lt;/span&gt;
   &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterComponentResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pulumi:example:S3Folder"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c"&gt;// Create a bucket and expose a website index document&lt;/span&gt;
   &lt;span class="n"&gt;siteBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;Website&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketWebsiteArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;IndexDocument&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"index.html"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="c"&gt;// For each file in the directory, create an S3 object stored in `siteBucket`&lt;/span&gt;
   &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Rel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBucketObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BucketObjectArgs&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;siteBucket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;                                     
            &lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFileAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;                           
            &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeByExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt; 
         &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;folderArgs&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;FolderArgs&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FolderArgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ElementType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;reflect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;reflect&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TypeOf&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;folderArgs&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Elem&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;FolderStruct: is a custom pulumi resource that has two properties, bucket name and website name. These are populated after the resource is created.&lt;/li&gt;
&lt;li&gt;newS3folder function: this function creates a new instance of the folder resource. The function takes in a pulumi context, a bucket name, a directory path and arguments for the folder resource.
It first creates a new component resource of type &lt;code&gt;pulumi:example:s3folder&lt;/code&gt;
The s3 bucket is created with a website configuration that sets the index.html file as the index of the document
It goes through the provided directory for each file, it creates a new s3 bucket object. &lt;code&gt;BucketobjectArgs&lt;/code&gt; specifies the bucket, using the bucket id, it also specifies the source file and the content type of the file which is determined by the file extension.
In &lt;code&gt;main.go&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;pulumi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c"&gt;// Create a bucket and expose a website index document&lt;/span&gt;
      &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewS3Folder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"resume-bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"./website"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;FolderArgs&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bucketName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Export&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"websiteUrl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;websiteUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
   &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the entry point of a pulumi program, using the pulumi.Run func. This is where the infrastructure is defined. Firstly&lt;br&gt;
It calls the newS3Folder function to create a new bucker called “resume bucket” and uploads the contents of the &lt;code&gt;/website&lt;/code&gt; directory to this bucket. It returns a folder resource&lt;br&gt;
When the folder resource is created successfully it exports two outputs bucketname and websiteurl in the pulumi CLI&lt;/p&gt;

&lt;p&gt;To deploy this infrastructure, I ran &lt;code&gt;pulumi up&lt;/code&gt; command which creates these resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Configuring AWS Cloudfront for the s3Bucket:
&lt;/h3&gt;

&lt;p&gt;I created a new Cloudfront distribution to properly serve this web page with low latency&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chose the resume bucket as the origin domain&lt;/li&gt;
&lt;li&gt;Under origin access, I selected Origin access control settings and created a new AoC, which will be provided when the distribution is created&lt;/li&gt;
&lt;li&gt;Under the viewer protocol policy, I selected “https only” which means CloudFront will only accept HTTPS requests
Created the distribution and copied the policy&lt;/li&gt;
&lt;li&gt;Navigated to the bucket, then to the permission tab I edited the bucket policy, which says only traffic from Cloudfront should be allowed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, participating in the AWS resume challenge has been rewarding, it allowed me to use my skills in Golang especially in crawling through the website folder and creating a bucket object for every one of the files, also My AWS skills were sharpened.&lt;/p&gt;

&lt;p&gt;I intend to extend this and add further features to this static website like ci/cd. To automatically update the website when I make and push changes to it. Bye for now!🤗&lt;/p&gt;

&lt;p&gt;the github link to the full project can be found &lt;a href="https://github.com/audu97/aws-resume-challenge"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>awschallenge</category>
      <category>cloud</category>
      <category>devops</category>
    </item>
    <item>
      <title>Efficiently Deploy Cloud Resources on AWS with Terraform</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Thu, 02 May 2024 23:52:46 +0000</pubDate>
      <link>https://dev.to/audu97/efficiently-deploy-cloud-resources-on-aws-with-terraform-2cbc</link>
      <guid>https://dev.to/audu97/efficiently-deploy-cloud-resources-on-aws-with-terraform-2cbc</guid>
      <description>&lt;p&gt;Infrastructure as code(IaC) tools has become very popular and useful in cloud engineering. Tools like Chef, puppet, and Ansible are configuration management tools, focusing on installing and managing applications on existing servers. &lt;/p&gt;

&lt;p&gt;Provisioning tools like Pulumi, Crossplane and Terraform create new servers and other infrastructure components.&lt;/p&gt;

&lt;p&gt;Terraform is a declarative coding tool that uses a high-level configuration language called Hashicorp configuration language(HCL) to describe the desired cloud or on-premise infrastructure for running an application.&lt;/p&gt;

&lt;p&gt;In this article, we will build a simple cloud architecture with a virtual private cloud(VPC), subnets, route tables, security groups and an ec2 instance and deploy a web server all using a Terraform script. &lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Basic knowledge of cloud environments(AWS)&lt;/li&gt;
&lt;li&gt;An AWS account&lt;/li&gt;
&lt;li&gt;Terraform. You can download it from here&lt;/li&gt;
&lt;li&gt;VS Code with terraform plugin. VS code can be downloaded from its &lt;a href="https://code.visualstudio.com/Download"&gt;official website&lt;/a&gt;. Once installed navigate to the plugin marketplace and search for “terraform” and install.
&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9l73vvhr839ur50mkqt1.png" alt="screenshot of vs code with the terraform plugin installation page" width="800" height="399"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Understanding the infrastructure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;VPC:&lt;/strong&gt; Virtual Private Cloud is an isolated section of the AWS cloud where you can launch AWS resources in a defined virtual network. It provides isolation and control over our network environment which includes IP address range selection, subnet creation and route table configurations.&lt;br&gt;
&lt;strong&gt;Subnets:&lt;/strong&gt; Subnets are a range of IP addresses in our VPC that you designate to group resources based on security requirements.&lt;br&gt;
&lt;strong&gt;Route Table:&lt;/strong&gt; is a set of rules, known as routes that are used to determine where traffic is directed within a VPC or to an external network&lt;br&gt;
&lt;strong&gt;Internet Gateway:&lt;/strong&gt; An Internet gateway enables communications between instances within a VPC and the Internet. It allows them to send data to and from the internet.&lt;br&gt;
&lt;strong&gt;Security groups:&lt;/strong&gt; Security groups act as virtual firewalls for your server instances. It controls inbound and outbound traffic. It allows us to define which traffic is allowed to reach our instances.&lt;br&gt;
&lt;strong&gt;Elastic IP:&lt;/strong&gt; Elastic IP is a static IPv4 address assigned to your instance. When an AWS instance is restarted, the IP address changes. An elastic IP enables us to attach a persistent IP address to our instance.&lt;br&gt;
&lt;strong&gt;EC2 Instance:&lt;/strong&gt; An EC2 instance is a virtual server in AWS that hosts our application.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up Terraform configuration
&lt;/h3&gt;

&lt;p&gt;To begin writing our terraform script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a folder, lets call it "terraform-project"&lt;/li&gt;
&lt;li&gt;Open this folder in vs code and create a file within this folder, we'll call it &lt;code&gt;main.tf&lt;/code&gt; for the sake of this guide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's write some scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Provider&lt;/strong&gt;&lt;br&gt;
The first step in any Terraform script is to define the provider. In our case, we're using AWS.&lt;/p&gt;

&lt;p&gt;The provider block is used to specify the details of the cloud provider, such as Azure, GCP, Oracle Cloud, and even container orchestration platforms like Kubernetes, where all the resources will be created. Here's what our provider block would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;access_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="nx"&gt;secret_key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;provider "aws"&lt;/code&gt; - This line declares a provider block for AWS. Terraform uses a plugin-based architecture and “aws” is the plugin for Amazon Web Service.&lt;br&gt;
&lt;code&gt;region = "us-east-1"&lt;/code&gt; - This line sets the AWS region where the resources will be created. In this case, it’s set to “us-east-1”, which corresponds to the N. Virginia region.&lt;br&gt;
&lt;code&gt;access_key = “”&lt;/code&gt; - this line is where you should put your AWS access key. It is part of your AWS security credentials&lt;br&gt;
&lt;code&gt;Secret_key = “”&lt;/code&gt;- this line is where you should put your AWS access key.&lt;br&gt;
To obtain your secret and access key, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the AWS Management Console.&lt;/li&gt;
&lt;li&gt;Click on your name in the top right corner and select “Security Credentials”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1blt4xrmgbc737gx4fe3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1blt4xrmgbc737gx4fe3.png" alt="Image description" width="328" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select “Create Access Key”. Your access and secret key will be provided. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lubz9pkvjl4hebetpza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lubz9pkvjl4hebetpza.png" alt="Image description" width="800" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t forget to download the CSV file for safekeeping.
Remember, these keys are sensitive information. Store them securely and do not share them publicly. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Virtual Private Cloud(VPC)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"prod-vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"production"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource ”aws vpc” “prod-vpc”&lt;/code&gt;- this line declares a resource of type “aws vpc” (an AWS VPC) and gives it the name &lt;code&gt;"prod-vpc"&lt;/code&gt;. This name is used to refer to this resource in other parts of our terraform script&lt;br&gt;
&lt;code&gt;Cidr_block = “10.0.0.0/16”&lt;/code&gt; - This line sets the IP address range for the VPC in CIDR notation. The range “10.0.0.0/16” includes all IP addresses from 10.0.0.0 to 10.0.255.255.&lt;br&gt;
&lt;code&gt;Tags{Name = production}&lt;/code&gt; - Tags are key-value pairs that you can attach to AWS resources to easily organize, search, and identify them. In our case, our VPC has the tag “production”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internet Gateway&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"gw"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prod-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource “aws_internet_gateway” “gw”&lt;/code&gt; - This line declares a resource of type “aws_internet_gateway” (an AWS Internet Gateway), and gives it the name “gw”. This name is used to refer to this resource in other parts of our Terraform script&lt;br&gt;
&lt;code&gt;vpc_id = aws_vpc.prod-vpc.id&lt;/code&gt; - This line attaches the Internet Gateway to the VPC that was created earlier in the script. It does this by setting the vpc_id property of the Internet Gateway to the ID of the VPC. &lt;code&gt;The aws_vpc.prod-vpc.id&lt;/code&gt; syntax is used to get the ID of the VPC named &lt;code&gt;prod-vpc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Route Table&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"prod-route-table"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prod-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
 &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
   &lt;span class="nx"&gt;gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource “aws_route_table” “prod-route-table”&lt;/code&gt; - This line is declaring a resource of type “aws_route_table” and the name of this particular resource is “prod-route-table”.&lt;br&gt;
&lt;code&gt;vpc_id = aws_vpc.prod_vpc.id&lt;/code&gt; - this line associates the route table with the VPC that has been created earlier in the script with the name &lt;code&gt;prod-vpc&lt;/code&gt;. The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that VPC.&lt;br&gt;
The route block within the resource defines a route for the route table.&lt;br&gt;
&lt;code&gt;cidr_block = "0.0.0.0/0"&lt;/code&gt;- This line specifies the destination for the route as 0.0.0.0/0, which represents all IPv4 addresses. In the context of a route table, this is often used to define the default route.&lt;br&gt;
&lt;code&gt;gateway_id = aws_internet_gateway.gw.id&lt;/code&gt; - This line is setting the Internet Gateway (gw which was declared earlier) as the target of the route, meaning that any traffic destined for 0.0.0.0/0 will be directed to this Internet Gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subnet&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"subnet-1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prod-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
   &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;
   &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1a"&lt;/span&gt;
   &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource “aws_subnet” “subnet-1”&lt;/code&gt; - This line declares a resource of type aws_subnet and the name of this particular resource is subnet-1.&lt;br&gt;
&lt;code&gt;vpc_id = aws_vpc.prod-vpc.id&lt;/code&gt; - This line associates the subnet with the VPC that has been created earlier in the script with the name &lt;code&gt;prod-vpc&lt;/code&gt;. The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that VPC.&lt;br&gt;
&lt;code&gt;cidr_block = "10.0.1.0/24"&lt;/code&gt; - This line specifies the IP address range for the subnet in CIDR notation. The range 10.0.1.0/24 includes all IP addresses from 10.0.1.0 to 10.0.1.255.&lt;br&gt;
&lt;code&gt;availability_zone = "us-east-1a"&lt;/code&gt; - This line specifies the availability zone in which to create the subnet. In this case, it’s us-east-1a.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Route Table Association&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"a"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;subnet_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet-1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
   &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prod-route-table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource “aws_route_table_association” ”a”&lt;/code&gt; - This line is declaring a resource of type &lt;code&gt;aws_route_table_association&lt;/code&gt; and the name of this particular resource is &lt;code&gt;a&lt;/code&gt;.&lt;br&gt;
 &lt;code&gt;subnet_id = aws_subnet.subnet-1.id&lt;/code&gt; - This line associates the route table with the subnet that has been created earlier in the script with the name &lt;code&gt;subnet-1&lt;/code&gt;. The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that subnet.&lt;br&gt;
&lt;code&gt;route_table_id = aws_route_table.prod-route-table.id&lt;/code&gt; This line specifies the route table to associate with the subnet. It refers to the route table that was created earlier with the name &lt;code&gt;prod-route-table&lt;/code&gt;.&lt;br&gt;
This block means that the routing policies defined in the &lt;code&gt;prod-route-table&lt;/code&gt; will now apply to any resources that are deployed within subnet-1&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Group&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"allow-web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"allow_web_traffic"&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"allow traffic"&lt;/span&gt;
    &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prod-vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

    &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https"&lt;/span&gt;
        &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
        &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;
        &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http"&lt;/span&gt;
        &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
        &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; 
        &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
        &lt;span class="nx"&gt;from_port&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
        &lt;span class="nx"&gt;to_port&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
        &lt;span class="nx"&gt;protocol&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
        &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"allow web"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource “aws_security_group” “allow-web”&lt;/code&gt; - This line declares a resource of type &lt;code&gt;aws_security_group&lt;/code&gt; and the name of this particular resource is "allow-web".&lt;br&gt;
&lt;code&gt;name = "allow_web_traffic"&lt;/code&gt; This line is setting the security group's name to "allow_web_traffic".&lt;br&gt;
&lt;code&gt;description = "allow traffic"&lt;/code&gt; - This line sets the description of the security group to allow traffic.&lt;br&gt;
&lt;code&gt;vpc_id = aws_vpc.prod-vpc.id&lt;/code&gt; - This line associates the security group with the VPC that has been created earlier in the script with the name "prod-vpc". The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that VPC.&lt;/p&gt;

&lt;p&gt;The ingress blocks within the resource define inbound rules for the security group&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first ingress block allows inbound HTTPS traffic on port 443 from any IP address (0.0.0.0/0).&lt;/li&gt;
&lt;li&gt;The second ingress block allows inbound HTTP traffic on port 80 from any IP address (0.0.0.0/0).&lt;/li&gt;
&lt;li&gt;The third ingress block allows inbound SSH traffic on port 22 from any IP address (0.0.0.0/0).
** Network Interface**
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_network_interface"&lt;/span&gt; &lt;span class="s2"&gt;"web-server-nic"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subnet-1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;private_ips&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.0.1.50"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allow-web&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;resource "aws_network_interface" "web-server-nic"&lt;/code&gt; -  This line declares a resource of type &lt;code&gt;aws_network_interface&lt;/code&gt; and the name of this particular resource is "web-server-nic".&lt;br&gt;
&lt;code&gt;subnet_id = aws_subnet.subnet-1.id&lt;/code&gt; - This line associates the network interface with the subnet that has been created earlier in the script with the name "subnet-1." The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that subnet.&lt;br&gt;
&lt;code&gt;private_ips = ["10.0.1.50"]&lt;/code&gt; - This line assigns the private IP address 10.0.1.50 to the network interface.&lt;br&gt;
&lt;code&gt;security_groups = [aws_security_group.allow-web.id]&lt;/code&gt; This line associates the network interface with the security group that has been created earlier in the script with the name "allow-web" The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that security group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Elastic IP&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"eip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;network_interface&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_network_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web-server&lt;/span&gt; &lt;span class="nx"&gt;nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;associate_with_private_ip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.50"&lt;/span&gt;
    &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gw&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource "aws_eip" "eip"&lt;/code&gt; - This line declares a resource of type aws_eip and the name of this particular resource is true.&lt;br&gt;
&lt;code&gt;network_interface = aws_network_interface.web-server-nic.id&lt;/code&gt; - This line associates the EIP with the network interface that has been created earlier in the script with the name "web-server-nic". The &lt;code&gt;.id&lt;/code&gt; attribute fetches the ID of that network interface.&lt;br&gt;
&lt;code&gt;associate_with_private_ip = "10.0.1.50"&lt;/code&gt; - This line specifies the private IP address with which the EIP should be associated. In this case, it’s 10.0.1.50, which is the same private IP address assigned to the network interface.&lt;br&gt;
&lt;code&gt;depends_on = [ aws_internet_gateway.gw ]&lt;/code&gt; This line specifies a dependency on the Internet Gateway resource named "gw". This means that Terraform will ensure the Internet Gateway is created before attempting to create the EIP.&lt;br&gt;
&lt;strong&gt;EC2 Instance&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web-server"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ami&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-04b70fa74e45c3917"&lt;/span&gt;
    &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
    &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1a"&lt;/span&gt;
    &lt;span class="nx"&gt;key_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-key2"&lt;/span&gt;

    &lt;span class="nx"&gt;network_interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;device_index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="nx"&gt;network_interface_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_network_interface&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web-server-nic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;user_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
      #!/bin/bash
      sudo apt update -y
      sudo apt install apache2 -y
      sudo systemctl enable apache2
      sudo systemctl start apache2
      echo "&amp;lt;h1&amp;gt;Welcome to my server &amp;lt;/h1&amp;gt;" &amp;gt; /var/www/html/index.html
&lt;/span&gt;&lt;span class="no"&gt;    EOF

&lt;/span&gt;    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web-server"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;resource "aws_instance" "web-server"&lt;/code&gt; - This line declares a resource of type aws_instance and the name of this particular resource is "web-server".&lt;br&gt;
&lt;code&gt;ami = "ami-04b70fa74e45c3917"&lt;/code&gt; - This line specifies the Amazon Machine Image (AMI) to use for the instance. The AMI determines the operating system and other software for the instance.&lt;br&gt;
&lt;code&gt;instance_type = "t2.micro"&lt;/code&gt; - This line specifies the type of instance to launch. In this case, it’s "t2.micro", which is a general-purpose instance type.&lt;br&gt;
&lt;code&gt;availability_zone = "us-east-1a"&lt;/code&gt; - This line specifies the availability zone in which to launch the instance.&lt;br&gt;
&lt;code&gt;key_name = "main-key2"&lt;/code&gt;- This line specifies the name of the key pair to use for the instance. This key pair is used for SSH access to the instance.&lt;br&gt;
The &lt;code&gt;network_interface&lt;/code&gt; block within the resource associates the instance with the network interface that has been created earlier in the script with the name "web-server-nic".&lt;br&gt;
The user_data block within the resource specifies a startup script to run on the instance when it launches. This script updates the package lists for upgrades and new package installations, installs Apache2, enables and starts the Apache2 service, and creates a simple HTML file as the index page for the web server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying the Infrastructure
&lt;/h3&gt;

&lt;p&gt;Deploying the infrastructure to AWS is pretty straightforward. To do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open a terminal in Visual Studio Code. &lt;/li&gt;
&lt;li&gt;Ensure that it points to the directory containing the Terraform file.&lt;/li&gt;
&lt;li&gt;Execute the following command:
&lt;code&gt;terraform apply&lt;/code&gt;
If everything is set up correctly, the system will prompt you to enter “yes”. If there’s an error, it will point you to the location of the issue. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, open the AWS console and locate the EC2 instance that we just deployed.&lt;/p&gt;

&lt;p&gt;Copy the IPv4 public address and paste it into a new browser tab. The HTML file that we deployed on our web server will be displayed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So far, we’ve discussed how to deploy various resources on AWS using Terraform. We’ve also examined how these resources can be made dependent on each other, enabling them to form a unified infrastructure that serves a specific purpose in our case display a simple HTML page.&lt;/p&gt;

&lt;p&gt;In an ideal situation, more than one instance would be deployed across different availability zones. Additional resources such as load balancers, S3 buckets, Content Delivery Networks (CDNs) like CloudFront, and Domain Name Services (DNS) like Route53, among others, would also be utilized.&lt;/p&gt;

&lt;p&gt;All these components come together to create an infrastructure that is reliable, dependable, and operates smoothly.&lt;/p&gt;

&lt;p&gt;I hope you won’t stop here. Continue exploring Terraform and even extend your knowledge beyond AWS to other cloud service providers. Good luck!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>terraform</category>
      <category>cloudcomputing</category>
      <category>devops</category>
    </item>
    <item>
      <title>Provisioning Ansible on Ubuntu with Vagrant and Virtual box</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Sat, 27 Apr 2024 07:51:52 +0000</pubDate>
      <link>https://dev.to/audu97/provisioning-ansible-on-ubuntu-with-vagrant-and-virtual-box-4bjj</link>
      <guid>https://dev.to/audu97/provisioning-ansible-on-ubuntu-with-vagrant-and-virtual-box-4bjj</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Automation tools play a crucial role in day-to-day software development jobs. And it is on that note that Ansible has proven to be one of the most used technologies in today's software development world.&lt;/p&gt;

&lt;p&gt;Ansible is an open-source software development tool that enables infrastructure as code (IaC). Ansible helps in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Software provisioning - Ansible can prepare your servers with the necessary software and settings.&lt;/li&gt;
&lt;li&gt;Configuration management - Ansible can manage system configurations to ensure your systems and servers are in the desired state&lt;/li&gt;
&lt;li&gt;Application deployment - Ansible can automate the process of deploying applications to servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ansible is agentless. It relies on temporary remote connections via SSH. Ansible uses a human-readable script called Playbook to automate tasks and manage and maintain system configurations.&lt;/p&gt;

&lt;p&gt;Vagrant on the other hand is an open-source software that provides a convenient way to manage virtual environments making it easier to replicate virtual environments across different hosts.&lt;/p&gt;

&lt;p&gt;Meanwhile, VirtualBox is a free open-source virtualization platform. Together with Vagrant, they provide a seamless solution for creating and maintaining virtual machines.&lt;br&gt;
Objective&lt;/p&gt;

&lt;p&gt;This article aims to guide us through the process of setting up a local environment with Ansible on Ubuntu with Vagrant and VirtualBox where we will run a simple Ansible playbook on the local machine to print “Hello World”.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Software requirements&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu&lt;/li&gt;
&lt;li&gt;Vagrant&lt;/li&gt;
&lt;li&gt;Virtual Box
Basic Knowledge of Linux commands&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Setting up Ubuntu virtual machine with Vagrant and VirtualBox
&lt;/h3&gt;

&lt;p&gt;To begin we will need to download and install Vagrant and VirtualBox&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installing VirtualBox
Virtual Box is a free and open-source technology available for download for different operating systems.&lt;/li&gt;
&lt;li&gt;Download VirtualBox: visit the VirtualBox download page to download the appropriate version for your operating system&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install virtual box: Install the virtual box application from the downloaded binary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Installing Vagrant&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Download Vagrant: Download Vagrant from the Vagrant download page&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Vagrant: Follow the installation guide to install Vagrant for your specific operating system. Vagrant will be automatically added to your system path, so you can start using vagrant commands from your terminal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify Installation: to verify that Vagrant was installed correctly execute the command below to see if it prints out the installed version.&lt;br&gt;
&lt;code&gt;vagrant -version&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Creating an Ubuntu Virtual Machine
&lt;/h3&gt;

&lt;p&gt;For the sake of this article, we will be using PowerShell. Run it as an administrator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We will need to create a folder to store all our vagrant-related files, we will call the file. To do that run the following command.&lt;br&gt;
&lt;code&gt;mkdir ansibleProject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cd into the directory with&lt;br&gt;
&lt;code&gt;cd ansibleProject&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Initialise a vagrant file with an Ubuntu image. This will contain all the necessary configurations needed for our virtual machine. We are going to use the Ubuntu image available in the Vagrant cloud.&lt;br&gt;
&lt;code&gt;vagrant init&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Start the virtual machine with the following command &lt;br&gt;
&lt;code&gt;Vagrant up&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above command will download the ubuntu/trusty64 virtual machine image from the vagrant cloud and start the VM. &lt;/p&gt;

&lt;p&gt;It also creates a key pair so we can SSH into the machine once it is done setting up.&lt;/p&gt;

&lt;p&gt;You can check the status of the virtual machine with the following command &lt;code&gt;vagrant status&lt;/code&gt;. It should print out “running (virtualbox)”. we can also check the status by opening VirtualBox to see if our virtual machine is up and running.&lt;br&gt;
SSH into the virtual machine by running the following command &lt;code&gt;vagrant ssh&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing Ansible
&lt;/h3&gt;

&lt;p&gt;To install Ansible on our virtual machine running on Ubuntu we need to run the following commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;software-properties-common
&lt;span class="nb"&gt;sudo &lt;/span&gt;add-apt-repository &lt;span class="nt"&gt;--yes&lt;/span&gt; &lt;span class="nt"&gt;--update&lt;/span&gt; ppa:ansible/ansible
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As soon as Ansible is done installing, we will verify if it was installed correctly.&lt;/p&gt;

&lt;p&gt;To verify that Anisble was installed correctly run the following command &lt;br&gt;
&lt;code&gt;ansible –version&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a playbook
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;To create a playbook, we will first create a new directory. This will contain our ansible playbook.&lt;br&gt;
&lt;code&gt;mkdir ansible-project&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new file called playbook.yml. This file will contain the task you want to perform on your machine.&lt;br&gt;
&lt;code&gt;Touch playbook.yml&lt;/code&gt;&lt;br&gt;
Run &lt;code&gt;ls&lt;/code&gt; to see if it was created successfully.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write your playbook in the playbook.yml file&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the playbook.yml by running &lt;code&gt;vi playbook.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Press “I” on your keyboard to enter Insert mode. Copy and paste the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
- name: Basic playbook
  hosts: localhost
  tasks:
    - name: Print a message
      debug:
        msg: "Hello, World!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Hit the “esc” key when done, to exit insert mode.&lt;/li&gt;
&lt;li&gt;Type “:wq!” to exit and save the playbook.yml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a simple playbook script that tells Ansible to connect to a machine, in this case, localhost which is the machine Ansible is installed on.&lt;/p&gt;

&lt;p&gt;Then tells the machine to print “Hello world”&lt;/p&gt;

&lt;p&gt;run the playbook by running &lt;code&gt;ansible-playbook playbook.yml&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this simple guide, we’ve seen how to set up a development environment on Ubuntu using Vagrant and VirtualBox, followed by installing Ansible for automation and configuration management.&lt;/p&gt;

&lt;p&gt;With Ansible installed we have a powerful tool that can help reduce repetitive tasks and increase productivity.&lt;/p&gt;

&lt;p&gt;We can take this further by automating configurations on the cloud with Ansible rather than on our local machines. &lt;/p&gt;

&lt;p&gt;As we continue our journey, whether we are managing a small infrastructure or orchestrating a complex deployment Ansible simplicity makes it a great addition in our DevOps toolkit. &lt;br&gt;
Don’t stop learning!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>ansible</category>
    </item>
    <item>
      <title>Harnessing App Flavors and Shared Preferences in Android: A Ride-Sharing App Case Study</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Sat, 02 Mar 2024 14:23:54 +0000</pubDate>
      <link>https://dev.to/audu97/harnessing-app-flavors-and-shared-preferences-in-android-a-ride-sharing-app-case-study-118i</link>
      <guid>https://dev.to/audu97/harnessing-app-flavors-and-shared-preferences-in-android-a-ride-sharing-app-case-study-118i</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we will explore the concept of app flavors in Android applications and how to customize these flavors. We will build a demo ride-sharing application with two flavors, one for the driver and one for passengers.&lt;/p&gt;

&lt;p&gt;We will also be looking at saving a user's choice at the first launch with shared preferences so that, on the next launch, the user's choice will be remembered. &lt;/p&gt;

&lt;p&gt;By the end of this article, you will have a solid foundational understanding of how to use app flavors in Android development and how to apply this knowledge to create more flexible applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Basic knowledge of Android development, Gradle and Jetpack Compose is required to follow along with this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding App Flavors
&lt;/h2&gt;

&lt;p&gt;App flavors or build configurations are distinct ways to create distinct environments or “flavors” for your app, each representing a distinct variant of your application but from the same codebase.&lt;/p&gt;

&lt;p&gt;App flavors are a way to implement different versions of an application with minor changes.&lt;/p&gt;

&lt;p&gt;Common use cases of app flavors include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Different versions of an application&lt;/strong&gt;- you might have a free and premium version of your app. The free version could have ads and limited features, while the premium version has no ads and more features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Geographical variations&lt;/strong&gt;: if your app provides content or features based on geographical location, you can use app flavors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Different UI for different user roles&lt;/strong&gt;: in a ride-sharing app, like in our demo. You have one UI for drivers and one for passengers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A/B Testing&lt;/strong&gt;: you can use app flavors to create slightly different versions of your app for A/B testing. This can help you understand which features or designs users prefer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building different flavors for your app can be very useful, they also come with some level of complexity, so you need to think carefully about your needs before setting up a project to use app flavors.&lt;/p&gt;

&lt;p&gt;If your separate apps require minor differences and theme changes but are still the same app, multiple flavors should be considered. However, if both apps require a lot of custom code differences you should rethink using app flavors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Types
&lt;/h2&gt;

&lt;p&gt;In the process of building Android apps, besides “flavors”, there’s another crucial concept known as “build types”. &lt;/p&gt;

&lt;p&gt;A build type is responsible for defining settings related to the app’s build and packaging process, such as whether it’s debuggable and what signing keys to use. The standard build types are “debug” and “release”.&lt;/p&gt;

&lt;p&gt;On the other hand, a flavor is used to specify features, devices and API prerequisites like custom code and layout, as well as minimum and target API levels, among other things.&lt;/p&gt;

&lt;p&gt;The term “Build Variant” is used to describe the combination of build types and flavors. &lt;/p&gt;

&lt;p&gt;For every combination of a defined flavor and build type, there exists a corresponding build variant. In the case of our ride-sharing application, we will have these corresponding build variants.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;driverAppDebug&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;driverAppRelease&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;passengerAppDebug&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;passengerAppRelease&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, it is important not to mix up the concept of app flavors, build types and build variants with each other as each has its unique role to play.&lt;/p&gt;

&lt;p&gt;Let’s get started!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the project
&lt;/h2&gt;

&lt;p&gt;We do not need to do anything special to create our project. We’ll create a new regular Android project and call it myRideSharingApp and wait for the project to be done building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Project Flavors
&lt;/h2&gt;

&lt;p&gt;There are two ways to do this. &lt;br&gt;
One way is to click on the build option in Android Studio and select “edit flavors”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffurmy9mdkjs5z3w3nbmy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffurmy9mdkjs5z3w3nbmy.png" alt="screenshot of android studio showing edit flavor option" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can now create a flavor dimension, this is essential when creating multiple flavors as each flavor belongs to a dimension. We can also edit applicationID, add a signing config among other things.&lt;/p&gt;

&lt;p&gt;Then click “Okay” A Gradle sync will occur and when it is done our flavors will be created.&lt;/p&gt;

&lt;p&gt;Another method to create our flavors involves opening our module’s build.gradle file. Inside the Android block of this file, we can add the specifications for our flavors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;flavorDimensions&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"passenger"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;productFlavors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"driverApp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"driver"&lt;/span&gt;
       &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.example.myridesharingapp.driver"&lt;/span&gt;
       &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;".driver"&lt;/span&gt;
       &lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
       &lt;span class="n"&gt;versionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;
       &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-driver"&lt;/span&gt;
       &lt;span class="n"&gt;targetSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;
       &lt;span class="n"&gt;minSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"passengerApp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;dimension&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"passenger"&lt;/span&gt;
       &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.example.myridesharingapp.passenger"&lt;/span&gt;
       &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;".passenger"&lt;/span&gt;
       &lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
       &lt;span class="n"&gt;versionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;
       &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-passenger"&lt;/span&gt;
       &lt;span class="n"&gt;targetSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt;
       &lt;span class="n"&gt;minSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's try to understand what's going on: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;flavourDimension+= listOf("driver", "passenger")&lt;/code&gt;: This line creates dimensions of your product flavors. Each dimension represents a different characteristic that a product flavor can modify. In this case we have two dimensions: driver and passenger.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;productFlavors&lt;/code&gt;: In this block, you define your product flavors. Each product flavor is a variant of your app that you can build&lt;br&gt;
&lt;code&gt;create("driverApp") { ... }&lt;/code&gt; and &lt;code&gt;create("passengerApp") { ... }&lt;/code&gt; these blocks define the driverApp and passengerApp flavors respectively. Each block sets properties specific to that flavor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;dimension&lt;/code&gt;: This property sets which flavor dimension this product flavor belongs to&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;applicationId&lt;/code&gt;: This property sets the unique ID for this version of the app. It’s used by the Android system to identify your app among all others. Two applications with the same applicationID cannot exist on the same device.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;applicationIdSuffix&lt;/code&gt;: This property adds a suffix to the &lt;code&gt;applicationId&lt;/code&gt; for this flavor. This allows you to install multiple flavors of the app on the same device.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;versionCode&lt;/code&gt;: This property sets the version code for this flavor. The version code is an integer value that represents the version of the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;versionName&lt;/code&gt;: This property sets the version name for this flavor. The version name is a string that represents the version of the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;versionNameSuffix&lt;/code&gt;: This property adds a suffix to the versionName for this flavor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;targetSdk&lt;/code&gt;: This property sets the target SDK version for this flavor. The target SDK version is the latest Android version that your app has been tested with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;minSdk&lt;/code&gt;: This property sets the minimum SDK version for this flavor. The minimum SDK version is the lowest Android version that your app supports&lt;br&gt;
Once done, sync the project and we've created our flavors.&lt;br&gt;
&lt;strong&gt;Note:&lt;/strong&gt; All these details and specifications can be entered using the ‘Edit Flavor’ dialog.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  File Structure
&lt;/h2&gt;

&lt;p&gt;Every flavor file structure should look exactly like the main directory file structure. In our case it should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5184rkxheawsssz5wk47.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5184rkxheawsssz5wk47.png" alt="screenshot of android studio showing file structure" width="765" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every flavor should have its res folder that should contain values specific to each flavors like drawables and the like.&lt;/p&gt;

&lt;p&gt;Also, each flavor can have its manifest.xml file to specify settings and configurations particular to each flavor.&lt;/p&gt;

&lt;p&gt;For example, you might have different services or permissions for the driverApp flavor compared to the passengerApp flavor. &lt;/p&gt;

&lt;p&gt;These differences would be reflected in each flavor’s respective &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file. &lt;br&gt;
It’s a powerful feature that adds a lot of flexibility to your app’s configuration.&lt;br&gt;
Each flavor should contain its own &lt;code&gt;MainActivity.kt&lt;/code&gt; file. In our case &lt;code&gt;passengerActivity.kt&lt;/code&gt; and &lt;code&gt;DriverActivity.kt&lt;/code&gt; serve as an entry point to each application (Flavor).&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up Activities and Manifest Files
&lt;/h2&gt;

&lt;p&gt;We create our &lt;code&gt;driverActivity.kt&lt;/code&gt; in our &lt;code&gt;driverApp&lt;/code&gt; directory which contains a simple composable screen that indicates that this is the driver screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DriverActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;DriverScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;DriverScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is the driver Screen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our &lt;code&gt;passengerApp&lt;/code&gt; directory, we do the same: we create a &lt;code&gt;passengerActivity&lt;/code&gt; and a composable to indicate that this is the passenger screen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PassengerActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;PassengerScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;PassengerScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is the passenger Screen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we are done setting up our &lt;code&gt;passengerActivity&lt;/code&gt; and &lt;code&gt;driverActivity&lt;/code&gt;, we need to declare each activity in its manifest file so that the Android systems know about it. The passenger manifest file will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;manifest&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;application&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;".PassengerActivity"&lt;/span&gt;
            &lt;span class="na"&gt;android:exported=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the driver's manifest file will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;manifest&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;application&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;".DriverActivity"&lt;/span&gt;
            &lt;span class="na"&gt;android:exported=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/manifest&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system must know that this activity is part of your app so it can start the activity when requested to do so through an intent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating to Individual Flavors
&lt;/h2&gt;

&lt;p&gt;In our &lt;code&gt;mainActivity.kt&lt;/code&gt; file in our main directory we are going to create a simple composable with two buttons to navigate to our desired application (Flavor).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;SharedComponents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;
   &lt;span class="nc"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="n"&gt;contentAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;){&lt;/span&gt;
       &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
           &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
           &lt;span class="n"&gt;horizontalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CenterHorizontally&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;verticalArrangement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrangement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Center&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is the shared module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;fontSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt;
           &lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="nc"&gt;Spacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
           &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DriverActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;
               &lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
           &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nc"&gt;Spacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
           &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PassengerActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"passenger"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Upon running the application, you should be able to navigate to any of the flavors by clicking a button. If you close the application and reopen it, you’ll notice that it will return to the screen of the shared module.&lt;/p&gt;

&lt;p&gt;This current setup is not ideal. We would prefer our application to remember our selection. That way, the next time we open our application, we will be directed to the flavor of our initial choice.&lt;/p&gt;

&lt;p&gt;To achieve this, we will be using shared preferences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up Shared Preferences
&lt;/h2&gt;

&lt;p&gt;We are going to create an object called preference in our main module and add the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Preference&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;PREFERENCE_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"app_preference"&lt;/span&gt;

   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;USER_CHOICE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user_choice"&lt;/span&gt;

   &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;saveUserChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sharedPref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSharedPreferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PREFERENCE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MODE_PRIVATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="n"&gt;sharedPref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;putString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;USER_CHOICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?{&lt;/span&gt;
       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sharedPref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSharedPreferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PREFERENCE_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MODE_PRIVATE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sharedPref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;USER_CHOICE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We create two functions to save and get our user’s choice. &lt;br&gt;
In our &lt;code&gt;mainActivity&lt;/code&gt; class we retrieve a user’s choice from sharedPreference and launch the appropriate activity when the app starts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;userChoice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Preference&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="nc"&gt;MyRideSharingAppTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
               &lt;span class="nc"&gt;Surface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                   &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                   &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colorScheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;
               &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userChoice&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DriverActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                       &lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                       &lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                   &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userChoice&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"passenger"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PassengerActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                       &lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                       &lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                   &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                       &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                           &lt;span class="nc"&gt;MyRideSharingAppTheme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                               &lt;span class="nc"&gt;Surface&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                   &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                                   &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colorScheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;
                               &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                   &lt;span class="nc"&gt;SharedComponents&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                               &lt;span class="p"&gt;}&lt;/span&gt;

                           &lt;span class="p"&gt;}&lt;/span&gt;

                       &lt;span class="p"&gt;}&lt;/span&gt;

                   &lt;span class="p"&gt;}&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also modify our &lt;code&gt;onClick&lt;/code&gt; lambda to be able to save a user's choice at the click of the button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;**&lt;/span&gt;&lt;span class="nc"&gt;Preference&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveUserChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;)**&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;DriverActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;
   &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;Spacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="p"&gt;**&lt;/span&gt;&lt;span class="nc"&gt;Preference&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveUserChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"passenger"&lt;/span&gt;&lt;span class="p"&gt;)**&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;intent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PassengerActivity&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActivity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"passenger"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the application and make a selection. After closing and reopening the application, you’ll find that it remembers your initial choice, just as intended!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So far, we’ve discussed app flavors and their use cases. We’ve examined how to set up app flavors and customize each one to meet specific requirements. &lt;/p&gt;

&lt;p&gt;We’ve also explored how to establish file structures and navigate to a particular app flavor. &lt;/p&gt;

&lt;p&gt;Finally, we’ve looked at how to save a user’s preferred flavor with shared preferences for subsequent runs.&lt;/p&gt;

&lt;p&gt;In conclusion, the concept of app flavors in Android development offers a powerful and flexible way to manage different versions of an application. &lt;/p&gt;

&lt;p&gt;Through the example of a ride-sharing app, we’ve seen how to create and customize ‘driver’ and ‘passenger’ flavors, each tailored to meet specific user needs. &lt;/p&gt;

&lt;p&gt;We’ve also explored how to use shared preferences to enhance the user experience by remembering their preferred flavor.&lt;/p&gt;

&lt;p&gt;This not only streamlines the development process but also provides a more personalized user experience. Happy Coding!&lt;/p&gt;

&lt;p&gt;Questions and suggestions would be highly appreciated.&lt;/p&gt;

&lt;p&gt;You can find the link to the whole project &lt;a href="https://github.com/audu97/MyRideSharingApp"&gt;Here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>androiddev</category>
      <category>kotlin</category>
      <category>programming</category>
      <category>android</category>
    </item>
    <item>
      <title>Building Blocks: A Beginners Guide to REST APIs with Kotlin and Ktor</title>
      <dc:creator>Audu Ephraim</dc:creator>
      <pubDate>Fri, 26 Jan 2024 11:16:26 +0000</pubDate>
      <link>https://dev.to/audu97/building-blocks-a-beginners-guide-to-rest-apis-with-kotlin-and-ktor-30mc</link>
      <guid>https://dev.to/audu97/building-blocks-a-beginners-guide-to-rest-apis-with-kotlin-and-ktor-30mc</guid>
      <description>&lt;p&gt;In the world of software development, Rest APIs have emerged as fundamental building blocks of modern web applications.&lt;/p&gt;

&lt;p&gt;If you're a novice or an experienced developer, looking to experience new territories, this guide is your starting point for understanding and creating REST APIs using Kotlin and Ktor.&lt;/p&gt;

&lt;p&gt;Kotlin is a modern language that offers more concise syntax than Java. ktor is a Kotlin framework for building asynchronous servers and clients are gaining a lot of traction in the tech industry. &lt;/p&gt;

&lt;p&gt;This powerful duo simplifies building lightweight, scalable, and efficient REST APIs.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll create a basic REST API. This API will return a list of countries and their details. It will also allow us to view information for a specific country and add a new country with its details. &lt;/p&gt;

&lt;p&gt;Our goal is to simplify the concept of REST APIs and provide a clear, step-by-step method to build one using Kotlin and Ktor. So, let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Basic knowledge of Kotlin and basic HTTP Knowledge&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a REST API: A Brief Introduction
&lt;/h2&gt;

&lt;p&gt;An API(Application Programming Interface) is a set of rules and protocols that define how applications, devices, and services can communicate with themselves over the Internet using different types of requests to get different types of information.&lt;/p&gt;

&lt;p&gt;A REST(REpresentation Stateful Transfer) API is an API that conforms to the design principles of the REST architectural style. You can read more about this architecture and its principles HERE.&lt;/p&gt;

&lt;h2&gt;
  
  
  KTOR
&lt;/h2&gt;

&lt;p&gt;As we earlier said, Ktor is a Kotlin framework for building asynchronous server-side and client-side applications with ease. It is entirely written in Kotlin and supports coroutines for asynchronous programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Ktor and Kotlin
&lt;/h2&gt;

&lt;p&gt;To start working with Ktor and Kotlin we need to set up our development environment. &lt;/p&gt;

&lt;p&gt;To do that we will be making use of IntelliJ IDEA which is the leading Kotlin and Java IDE you can get it &lt;a href="https://www.jetbrains.com/idea/promo/?msclkid=940eef746e3d18112d7f8ac77ffdecf1&amp;amp;utm_source=bing&amp;amp;utm_medium=cpc&amp;amp;utm_campaign=EMEA_en_AFRICA_IDEA_Branded&amp;amp;utm_term=intellij%20IDEA&amp;amp;utm_content=intellij%20idea" rel="noopener noreferrer"&gt;Here&lt;/a&gt;. You can download the ultimate edition which is paid or you can just get the community edition which I will be using. After downloading just follow the prompts to install.&lt;/p&gt;

&lt;p&gt;To get started with Ktor, we need to head over to the &lt;a href="https://start.ktor.io/#/settings" rel="noopener noreferrer"&gt;Ktor project generator&lt;/a&gt;, if you are using the Ultimate Edition of IntelliJ you will be able to create your Ktor project right from inside your IDE. &lt;br&gt;
In the Ktor project generator, you should see a page like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqtz7te942ltva2zwllj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foqtz7te942ltva2zwllj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give a project name of your choice, In this article, we will name our project "SampleKtorProject" &lt;/li&gt;
&lt;li&gt;Click on Adjust Project Settings, and you will see a drop-down with a bunch of options that should help you customize your project even more. But for the sake of this guide, we will leave everything as it is.&lt;/li&gt;
&lt;li&gt;Then click on Add plugins
These plugins allow us to have access to specific back-end behaviors that we typically use in backend frameworks. If you scroll down the list, you should see all available plugins, but for this project, you will be adding just four plugins. &lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Routing- allows us to define the routes we respond with data. In our case countries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ContentNegotiation- used for JSON parsing. When we respond with a Kotlin data class it will automatically be parsed to a JSON&lt;br&gt;
CallLogging- this will just log when a client requests a server so we can see what's happening.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kotlinx.serialization- which is used by the content negotiation for serializing and deserializing the data&lt;br&gt;
When we are done you can go ahead and click on generate project, which automatically downloads the project in a zip file. Unzip and save in any location of your choice, then open the project in IntelliJ. Wait for the project to finish building.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s go over to the project&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Structure
&lt;/h2&gt;

&lt;p&gt;We have the build.gradle.kts file. It is the Kotlin version of Gradle. Gradle is a build automation tool, if you are coming from an Android background this should be familiar. It contains our project dependencies and some project settings.&lt;/p&gt;

&lt;p&gt;Navigate to src-&amp;gt;main-&amp;gt; Kotlin in which we have our Application.kt file which is the main entry point of our program. Similar to mainActivity in Android. In this file, we have our main function as we know from plain kotlin.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;head over to the plugins folder, here you will see all the plugins we added in our initial project setup, which created some default files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the call Monitoring.kt, you’ll see a lambda block where you can customise your logging. Like how detailed you want your logs to be and if you want to filter your log messages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Routing.kt file this is where the routes are defined, that our server responds at. Here we define and configure our routes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test that everything is working perfectly, head over to Application.kt, and click on the run button beside fun main(){...} to start the project, head over to any browser of your choice and hit the address returned in your IntelliJ console. &lt;/p&gt;

&lt;p&gt;This would return “hello world” as seen in your routing file. Now that we are familiar with our project, let’s start building. &lt;/p&gt;

&lt;p&gt;Since this is a very simple project we won't go deep into structuring our project.&lt;/p&gt;

&lt;p&gt;Let us create two new packages in our root folder, Let's call them “model” and “routes” To do this: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;right-click on your root package-&amp;gt;new-&amp;gt;package&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Inside the model package let's create a new data class and call it “country”. In our API each country has an id, a name, a continent, and a capital. The data class will end up looking like this:&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Serializable&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;continent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;capital&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The “@Serializable” annotation enables us to parse this content of data class content to JSON and send it over the network.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Next inside the route package create a new file, named countriesRoute.kt, and add the following code.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;countries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
   &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Japan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Asia"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Tokyo"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Egypt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Africa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cairo"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Canada"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"North America"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ottawa"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Brazil"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"South America"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Brasilia"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"New Zealand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Oceania"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Wellington"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"England"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Europe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"London"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
   &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/countries"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
       &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;countries&lt;/span&gt;
           &lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id?}"&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="s"&gt;"missing id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BadRequest&lt;/span&gt;
           &lt;span class="p"&gt;)&lt;/span&gt;

           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;country&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;?:&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="nd"&gt;@get&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
               &lt;span class="s"&gt;"country with id $id not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotFound&lt;/span&gt;
           &lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;country&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receive&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Country&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
           &lt;span class="n"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"country Added "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Created&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s go over it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The data that we return to a client upon a request does not originate from a database, we create a mutable list of Country objects. Each country object has an id, name, continent, and capital.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;fun Route.countries()&lt;/code&gt;: It is an extension function of the route class in kotlin. It is a way to group related routes. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;route("/countries")&lt;/code&gt;: It sets up a route group called countries with the path &lt;code&gt;/countries&lt;/code&gt;. All routes defined within the block will have &lt;code&gt;/countries&lt;/code&gt; as their base path.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GET: It sets up a get request. When a get request is made to &lt;code&gt;/countries&lt;/code&gt;, the server responds with a status code of ok and a list of countries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;get("{id?}")&lt;/code&gt;: This sets up another get request, but this one expects an optional id parameter in the URL. If an ID is provided e.g. &lt;code&gt;countries/1&lt;/code&gt;. It tries to find a country with that id. If it finds one it responds with the country data; otherwise, it responds with a not found status and the message “country with id not found”. If no ID is provided for example: &lt;code&gt;/countries/&lt;/code&gt; it responds with a bad request status code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Post: this sets up a POST request. It expects the request to contain a country object. It adds the new country to the countries list and responds with a created status and a confirmation message&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Registering routes
&lt;/h2&gt;

&lt;p&gt;Now all we have to do is head on to our plugin package in the Routing.kt file, remove the default, hello world get request, and call &lt;code&gt;countries()&lt;/code&gt; like this:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configureRouting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
   &lt;span class="nf"&gt;routing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
       &lt;span class="nf"&gt;countries&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;br&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Testing with Postman&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Our API is ready for testing. We are going to use Postman to see how it works. &lt;br&gt;
Postman is a tool for testing API endpoints and seeing how it works. You can get it &lt;a href="https://www.postman.com/downloads/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Once you’ve downloaded, installed, and launched Postman, you should see a window like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg91g7u6r53pu6kf09xiw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg91g7u6r53pu6kf09xiw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;click on the + to open a new tab. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;After that, head back to IntelliJ, and launch your server. Once it is up and running without any errors, return to Postman. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing GET requests
&lt;/h2&gt;

&lt;p&gt;First, let's test out GET to return the list of every country and details about them. To do this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select GET&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the empty text field, put the address we want to request, in our case, it is &lt;a href="http://127.0.0.1:8080/countries" rel="noopener noreferrer"&gt;http://127.0.0.1:8080/countries&lt;/a&gt; then hit send.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see a response like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2npparhnro9ko6ukxx0g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2npparhnro9ko6ukxx0g.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great!! Our get request is working properly. &lt;/p&gt;

&lt;p&gt;Let's try and get a country with ID 3. Edit the URL, to look like this &lt;a href="http://127.0.0.1:8080/countries/3" rel="noopener noreferrer"&gt;http://127.0.0.1:8080/countries/3&lt;/a&gt;  and hit send. &lt;/p&gt;

&lt;p&gt;You should get a response like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcrxutaku11yg6wj5hgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhcrxutaku11yg6wj5hgg.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Post request
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Change the request type to POST&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit the URL to &lt;a href="http://127.0.0.1:8080/countries" rel="noopener noreferrer"&gt;http://127.0.0.1:8080/countries&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select “body” and “raw” in the options below the URL text field&lt;br&gt;
Enter the data of the country you want to add&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnruo9b16fp9k59g5o85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnruo9b16fp9k59g5o85.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Click send, and you will see a response “country Added”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz68gggbk4raeud1wmmii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz68gggbk4raeud1wmmii.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Change the request type to get and send the request, you should see the newly added country&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kf7ctk5zuttazbd6mhs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kf7ctk5zuttazbd6mhs.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our API is now working and it's returning the desired results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we've taken our first steps into the world of API development with Kotlin and Ktor. We've seen how Kotlin's concise syntax and Ktor's robust features can make the process of building APIs more efficient and enjoyable.&lt;/p&gt;

&lt;p&gt;We started with the basics of setting up a Ktor project and gradually moved on to creating our first API endpoints with Ktor. We've also learned about key concepts such as routing and request handling.&lt;/p&gt;

&lt;p&gt;However, this is just the beginning. There's much more to explore in the realm of Kotlin and Ktor, such as authentication, database integration, and testing. As you continue your journey, remember that the key to mastering any technology is consistent practice and curiosity.&lt;/p&gt;

&lt;p&gt;So, keep building, keep exploring, and most importantly, have fun doing it. &lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>programming</category>
      <category>backend</category>
      <category>api</category>
    </item>
  </channel>
</rss>
