<?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>Forem: Sireesharaju Kamparaju</title>
    <description>The latest articles on Forem by Sireesharaju Kamparaju (@sirisharaju_kamparaju_c9c).</description>
    <link>https://forem.com/sirisharaju_kamparaju_c9c</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%2F1674314%2F0313cf53-8219-49d9-a754-269c60fddf45.png</url>
      <title>Forem: Sireesharaju Kamparaju</title>
      <link>https://forem.com/sirisharaju_kamparaju_c9c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sirisharaju_kamparaju_c9c"/>
    <language>en</language>
    <item>
      <title>Syncing GitLab Projects to AWX — My DevOps Journey</title>
      <dc:creator>Sireesharaju Kamparaju</dc:creator>
      <pubDate>Tue, 19 May 2026 13:24:55 +0000</pubDate>
      <link>https://forem.com/sirisharaju_kamparaju_c9c/syncing-gitlab-projects-to-awx-my-devops-journeyby-sireesha-24b8</link>
      <guid>https://forem.com/sirisharaju_kamparaju_c9c/syncing-gitlab-projects-to-awx-my-devops-journeyby-sireesha-24b8</guid>
      <description>&lt;p&gt;If you've been following my blog series, you already know I've set up Ansible AWX on Kubernetes using Helm. Now the next logical step for me was — how do I actually get my playbooks into AWX without manually uploading them every time?&lt;br&gt;
The answer is GitLab. In this blog I'll walk through how I installed GitLab on a VM, pushed my Ansible playbooks to it, and then synced that repo into AWX as a project — in two ways:&lt;/p&gt;

&lt;p&gt;Using an SSH key (SCM Private Key) — this is what I actually use in production&lt;br&gt;
Using a Personal Access Token over HTTP — good for quick lab setups&lt;/p&gt;

&lt;p&gt;Let's get into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What We're Building Here&lt;/strong&gt;&lt;br&gt;
The flow looks like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ansible Server → Push Playbooks → GitLab (self-hosted VM) → Sync → AWX Project &amp;amp; Templates&lt;br&gt;
Once this is set up, every time I update a playbook and push to GitLab, AWX syncs and picks up the latest version automatically. That's the real power of this setup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Part 1 — Installing GitLab on a VM&lt;/strong&gt;&lt;br&gt;
I installed GitLab on a separate Ubuntu 22.04 VM. Here's exactly what I ran:&lt;br&gt;
Step 1 — Install Dependencies&lt;br&gt;
      &lt;code&gt;sudo apt update&lt;/code&gt;&lt;br&gt;
      &lt;code&gt;sudo apt install -y curl openssh-server ca-certificates tzdata perl&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Step 2 — Add GitLab Repository and Install&lt;/strong&gt;&lt;br&gt;
     &lt;code&gt;curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash&lt;/code&gt;&lt;br&gt;
     &lt;code&gt;sudo apt install gitlab-ce&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Step 3 — Configure GitLab&lt;/strong&gt;&lt;br&gt;
     &lt;code&gt;sudo vi /etc/gitlab/gitlab.rb&lt;/code&gt;&lt;br&gt;
Update the external URL to your VM IP:&lt;br&gt;
rubyexternal_url '&lt;a href="http://10.*.*.**" rel="noopener noreferrer"&gt;http://10.*.*.**&lt;/a&gt;'&lt;br&gt;
Then reconfigure:&lt;br&gt;
      &lt;code&gt;sudo gitlab-ctl reconfigure&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Step 4 — Get the Initial Root Password&lt;/strong&gt;&lt;br&gt;
       &lt;code&gt;sudo cat /etc/gitlab/initial_root_password&lt;/code&gt;&lt;br&gt;
Open &lt;a href="http://10.*.*.*" rel="noopener noreferrer"&gt;http://10.*.*.*&lt;/a&gt; in your browser → log in with root and the password above.&lt;/p&gt;

&lt;p&gt;Tip: Change your root password immediately after first login — User Settings → Password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2. Creating a repo and pushing playbooks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside GitLab, I created a new blank project called:&lt;/p&gt;

&lt;p&gt;ansibleawx&lt;/p&gt;

&lt;p&gt;Set it to private and moved on.&lt;/p&gt;

&lt;p&gt;On my Ansible server, I configured Git:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git config --global user.name "Sireesha"&lt;/code&gt;&lt;br&gt;
&lt;code&gt;git config --global user.email "your@email.com"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then inside my playbook directory:&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;cd&lt;/span&gt; /etc/ansible

git init
git remote add origin http://ip/ansibleawx.git
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit - adding ansible playbooks"&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; patches
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin patches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m using a patches branch here because that’s what I decided to track in AWX later.&lt;/p&gt;

&lt;p&gt;That part matters more than it looks — branch mismatches will bite you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part3. Connecting AWX to GitLab using SSH (production way)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the setup I actually use properly. It’s more secure and avoids token expiry issues.&lt;/p&gt;

&lt;p&gt;Generate SSH key (on AWX side)&lt;br&gt;
&lt;code&gt;ssh-keygen -t rsa -b 4096 -C "awx@ansible"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then grab the public key:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cat ~/.ssh/id_rsa.pub&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add it to GitLab&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GitLab → User Settings → SSH Keys → paste key → save&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Nothing complicated here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create AWX credential&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In AWX:&lt;/p&gt;

&lt;p&gt;Credentials → Add&lt;/p&gt;

&lt;p&gt;Name: gitlab-cred&lt;br&gt;
Type: Source Control&lt;br&gt;
Username: root&lt;br&gt;
SCM Private Key: paste private key (~/.ssh/id_rsa)&lt;/p&gt;

&lt;p&gt;Click Save.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create AWX project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AWX → Projects → Add&lt;/p&gt;

&lt;p&gt;Name: AnsibleAWX&lt;br&gt;
SCM Type: Git&lt;br&gt;
URL: http:///ansibleawx.git&lt;br&gt;
Branch: patches&lt;br&gt;
Credential: gitlab-cred&lt;/p&gt;

&lt;p&gt;Once saved, AWX syncs automatically.&lt;/p&gt;

&lt;p&gt;When it turns green, you’re good.&lt;/p&gt;

&lt;p&gt;That’s the moment everything starts clicking.&lt;/p&gt;

&lt;p&gt;This is exactly the gitlab-cred credential I have set up — you can see from the screenshot it shows Credential Type: Source Control and SCM Private Key: Encrypted.&lt;br&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%2F3vsc05yahkvlhe7ouxm8.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%2F3vsc05yahkvlhe7ouxm8.png" alt=" "&gt;&lt;/a&gt;&lt;/p&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%2Fubfmxef788hdtmf3c4wl.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%2Fubfmxef788hdtmf3c4wl.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part4. Alternative: HTTP with Personal Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the quicker setup I used in a lab environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create token in GitLab&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitLab → Settings → Access Tokens&lt;/p&gt;

&lt;p&gt;Scope: read_repository&lt;/p&gt;

&lt;p&gt;Copy it (you only see it once).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add to AWX&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Credentials → Add&lt;/p&gt;

&lt;p&gt;Name: gitlab-http-cred&lt;br&gt;
Username: root&lt;br&gt;
Password: &lt;/p&gt;

&lt;p&gt;Save.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create project&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Same as before, but use this credential instead.&lt;/p&gt;

&lt;p&gt;It works fine — just not as clean or durable as SSH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 5 — Creating Job Templates in AWX&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once the project sync works, playbooks automatically appear in AWX.&lt;/p&gt;

&lt;p&gt;So I created a simple job template:&lt;/p&gt;

&lt;p&gt;AWX → Templates → Add&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: Linux Server Setup&lt;/li&gt;
&lt;li&gt;Job Type: Run&lt;/li&gt;
&lt;li&gt;Inventory: your inventory&lt;/li&gt;
&lt;li&gt;Project: AnsibleAWX&lt;/li&gt;
&lt;li&gt;Playbook: select from dropdown&lt;/li&gt;
&lt;li&gt;Credentials: your machine credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save and launch.&lt;/p&gt;

&lt;p&gt;At this point, AWX is basically running whatever is in GitLab.&lt;/p&gt;

&lt;p&gt;No file copying. No manual updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors I Hit Along the Way&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Error 1 — Git Push Branch Mismatch&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;error: src refspec main does not match any&lt;br&gt;
Fix — rename the branch to match what you set in AWX:&lt;br&gt;
&lt;code&gt;git branch -M patches&lt;br&gt;
git push -u origin patches&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Error 2 — AWX Sync Failed on SSH Host Key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Host key verification failed&lt;br&gt;
Fix — in your AWX Project settings under Source Control Options, tick:&lt;br&gt;
✅ Disregard Host Checks&lt;br&gt;
&lt;strong&gt;Error 3 — Playbook Dropdown Empty in Job Template&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After syncing, the playbook dropdown was empty when creating a job template&lt;br&gt;
.&lt;br&gt;
Fix — go back to your Project → hit the Sync button (circular arrow) → wait for green → go back to template. Playbooks will appear.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before this, updating a playbook meant manually copying files and hoping AWX had the latest version. Now my workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Edit playbook → git push to patches branch → AWX syncs → Launch job
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is version controlled, auditable, and consistent.&lt;/p&gt;

&lt;p&gt;Drop your questions in the comments — happy to help!&lt;br&gt;
— Sireesha&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>awx</category>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Deploying Ansible AWX on Kubernetes Using Helm - By Sireesha</title>
      <dc:creator>Sireesharaju Kamparaju</dc:creator>
      <pubDate>Tue, 19 May 2026 10:51:57 +0000</pubDate>
      <link>https://forem.com/sirisharaju_kamparaju_c9c/deploying-ansible-awx-on-kubernetes-using-helm-6i5</link>
      <guid>https://forem.com/sirisharaju_kamparaju_c9c/deploying-ansible-awx-on-kubernetes-using-helm-6i5</guid>
      <description>&lt;p&gt;In this blog, I'm going to walk you through how I deployed Ansible AWX on a Kubernetes cluster using Helm. This was one of the most hands-on projects I've worked on — it involves setting up the K8s cluster from scratch, installing the container runtime, deploying Helm, and finally getting AWX up and running. I also hit a few real errors along the way, so I'll share exactly how I fixed them.&lt;br&gt;
It was one of those projects that looks simple on paper but turns into a chain of small problems once you actually start doing it.&lt;br&gt;
Here's the overall flow I followed:&lt;br&gt;
The goal was pretty straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up a Kubernetes cluster on Ubuntu 22.04&lt;/li&gt;
&lt;li&gt;Install containerd&lt;/li&gt;
&lt;li&gt;Get Helm working&lt;/li&gt;
&lt;li&gt;Deploy AWX using the AWX Operator&lt;/li&gt;
&lt;li&gt;Fix whatever broke along the way (this part took the longest 😅)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting Up the Kubernetes Cluster on Ubuntu 22.04&lt;/strong&gt;&lt;br&gt;
Before anything else, I want to quickly explain the two types of nodes in a K8s cluster since this matters for the setup:&lt;br&gt;
Master Node — manages the control plane, API calls, pods, services, and everything else in the cluster.&lt;br&gt;
Worker Node — runs the actual containers. Pods can spread across multiple worker nodes for better resource management.&lt;br&gt;
&lt;strong&gt;Step 1 — Update and Upgrade (All Nodes)&lt;/strong&gt;&lt;br&gt;
I started by logging in as root and running updates on all nodes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hostnamectl set-hostname siri-awx-01   # run on master node&lt;br&gt;
hostnamectl set-hostname siri-awx-02   # run on worker node&lt;br&gt;
exec bash&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
apt update&lt;br&gt;
apt upgrade&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Disable Swap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
swapoff -a&lt;br&gt;
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Step 3 — Load Kernel Modules&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tee /etc/modules-load.d/containerd.conf &amp;lt;&amp;lt;EOF&lt;br&gt;
overlay&lt;br&gt;
br_netfilter&lt;br&gt;
EOF&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;br&gt;
modprobe overlay&lt;br&gt;
modprobe br_netfilter&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4 — Configure Kernel Parameters&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;tee /etc/sysctl.d/kubernetes.conf &amp;lt;&amp;lt;EOF&lt;br&gt;
net.bridge.bridge-nf-call-ip6tables = 1&lt;br&gt;
net.bridge.bridge-nf-call-iptables = 1&lt;br&gt;
net.ipv4.ip_forward = 1&lt;br&gt;
EOF&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sysctl --system&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing Containerd Runtime (All Nodes)&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apt update&lt;br&gt;
apt install -y containerd.io&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;containerd config default | sudo tee /etc/containerd/config.toml &amp;gt;/dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;systemctl restart containerd&lt;br&gt;
systemctl enable containerd&lt;/code&gt;&lt;/p&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%2Fng0uc7627se2r9y2697e.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%2Fng0uc7627se2r9y2697e.png" alt=" " width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing Kubernetes Components (All Nodes)&lt;/strong&gt;&lt;br&gt;
`sudo apt-get install -y apt-transport-https ca-certificates curl gpg&lt;/p&gt;

&lt;p&gt;curl -fsSL &lt;a href="https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key" rel="noopener noreferrer"&gt;https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key&lt;/a&gt; | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg`&lt;/p&gt;

&lt;p&gt;&lt;code&gt;echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo apt-get update&lt;br&gt;
sudo apt-get install -y kubelet kubeadm kubectl&lt;br&gt;
sudo apt-mark hold kubelet kubeadm kubectl&lt;/code&gt;&lt;/p&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%2Fcko3xo457fbcdulv08c4.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%2Fcko3xo457fbcdulv08c4.png" alt=" " width="799" height="290"&gt;&lt;/a&gt;&lt;/p&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%2F8oy60bjcl886fjb2sldw.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%2F8oy60bjcl886fjb2sldw.png" alt=" " width="799" height="110"&gt;&lt;/a&gt;&lt;/p&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%2F7tstvvzji18txzjbnfsy.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%2F7tstvvzji18txzjbnfsy.png" alt=" " width="799" height="313"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initialising the Cluster (Master Node — siri-awx-01 Only)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;systemctl enable kubelet &amp;amp;&amp;amp; systemctl start kubelet&lt;br&gt;
kubeadm init&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdir -p $HOME/.kube&lt;br&gt;
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config&lt;br&gt;
chown $(id -u):$(id -g) $HOME/.kube/config&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Tip: If you forget to save the kubeadm join output, regenerate it with:&lt;br&gt;
bashkubeadm token create --print-join-command&lt;/p&gt;

&lt;p&gt;$&lt;code&gt;kubectl get nodes&lt;/code&gt;&lt;/p&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%2F2ti6x33goq0fikkxgse8.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%2F2ti6x33goq0fikkxgse8.png" alt=" " width="796" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Join Worker Node (siri-awx-02)&lt;/strong&gt;&lt;br&gt;
Run this on siri-awx-02 (use your actual token from kubeadm init output):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubeadm join 192.168.237.144:6443 --token uhkvxh.mjp6aiyhy18vaxh0 \&lt;br&gt;
  --discovery-token-ca-cert-hash sha256:c0200907cc68957eea6d9cc4ad314282fb58ac92bceef2416e8212040441f130&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install Calico Network Plugin (siri-awx-01 Only)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml -O&lt;br&gt;
kubectl apply -f calico.yaml&lt;br&gt;
kubectl get nodes&lt;br&gt;
kubectl get pods -n kube-system&lt;/code&gt;&lt;/p&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%2Fe0y08ohsy3fjkp4sa79f.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%2Fe0y08ohsy3fjkp4sa79f.png" alt=" " width="799" height="53"&gt;&lt;/a&gt;&lt;/p&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%2Fiup0qhujr6fxuesgctwr.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%2Fiup0qhujr6fxuesgctwr.png" alt=" " width="799" height="481"&gt;&lt;/a&gt;&lt;/p&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%2Fuljbj7zmcupkhbnp5gcn.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%2Fuljbj7zmcupkhbnp5gcn.png" alt=" " width="799" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Installing Helm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3&lt;br&gt;
chmod 700 get_helm.sh&lt;br&gt;
./get_helm.sh&lt;/code&gt;&lt;/p&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%2Fphsrge2r3zqvo4zqpnns.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%2Fphsrge2r3zqvo4zqpnns.png" alt=" " width="796" height="88"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying Ansible AWX Using Helm&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;helm install ansible-awx-operator awx-operator/awx-operator -n awx --create-namespace&lt;br&gt;
kubectl get pods -n awx&lt;/code&gt;&lt;/p&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%2Frn3jlam9rbi5i1ubokjo.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%2Frn3jlam9rbi5i1ubokjo.png" alt=" " width="800" height="177"&gt;&lt;/a&gt;&lt;/p&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%2Fwae7au3k9gz6a9r5yxqi.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%2Fwae7au3k9gz6a9r5yxqi.png" alt=" " width="800" height="135"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting Up Storage — StorageClass, PV and PVC&lt;br&gt;
local-storage-class.yaml&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apiVersion: storage.k8s.io/v1&lt;br&gt;
kind: StorageClass&lt;br&gt;
metadata:&lt;br&gt;
  name: local-storage&lt;br&gt;
  namespace: awx&lt;br&gt;
provisioner: kubernetes.io/no-provisioner&lt;br&gt;
volumeBindingMode: WaitForFirstConsumer&lt;br&gt;
&lt;/code&gt;&lt;/p&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%2Fd52c2imv1q2lwtmnf7bh.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%2Fd52c2imv1q2lwtmnf7bh.png" alt=" " width="720" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;kubectl create -f local-storage-class.yaml&lt;br&gt;
kubectl get sc -n awx&lt;/p&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%2Fgkpipdg4y3xfffeh7vwg.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%2Fgkpipdg4y3xfffeh7vwg.png" alt=" " width="800" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pv.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolume&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;postgres-pv&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awx&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;capacity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2Gi&lt;/span&gt;
  &lt;span class="na"&gt;volumeMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Filesystem&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class="na"&gt;persistentVolumeReclaimPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Delete&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-storage&lt;/span&gt;
  &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/mnt/storage&lt;/span&gt; &lt;span class="c1"&gt;# Mount point should available on worker node&lt;/span&gt;
  &lt;span class="na"&gt;nodeAffinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;nodeSelectorTerms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;matchExpressions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.io/hostname&lt;/span&gt;
          &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;In&lt;/span&gt;
          &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;siri-awx-02&lt;/span&gt;    &lt;span class="c1"&gt;# worker node hostname&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fxmlmbipfu9r51523go5c.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%2Fxmlmbipfu9r51523go5c.png" alt=" " width="660" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pvc.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;postgres-13-ansible-awx-postgres-13-0&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;awx&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;storageClassName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local-storage&lt;/span&gt;
  &lt;span class="na"&gt;accessModes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F8quma9bw7i341nzvu9de.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%2F8quma9bw7i341nzvu9de.png" alt=" " width="664" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl apply -f pv.yaml&lt;br&gt;
kubectl create -f pvc.yaml&lt;br&gt;
kubectl get pv,pvc -n awx&lt;/code&gt;&lt;/p&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%2Fewmqxs63hi1ptikrbsby.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%2Fewmqxs63hi1ptikrbsby.png" alt=" " width="796" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying AWX&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;`kubectl create -f ansible-awx.yaml&lt;/p&gt;

&lt;p&gt;kubectl logs -f deployments/awx-operator-controller-manager -c awx-manager -n awx&lt;br&gt;
`&lt;/p&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%2Flcrjll7cqz41pzl8vbv9.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%2Flcrjll7cqz41pzl8vbv9.png" alt=" " width="674" height="274"&gt;&lt;/a&gt;&lt;/p&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%2Fr1c7xvwyf8a0xihm5ihh.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%2Fr1c7xvwyf8a0xihm5ihh.png" alt=" " width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessing the AWX Web Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl expose deployment ansible-awx-web --name ansible-awx-web-svc --type NodePort -n awx&lt;br&gt;
kubectl get svc ansible-awx-web-svc -n awx&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Get admin password
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;kubectl get secret ansible-awx-admin-password -o jsonpath="{.data.password}" -n awx | base64 --decode ; echo&lt;/code&gt;&lt;/p&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%2Fcaqjzwldzxafhih4uarh.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%2Fcaqjzwldzxafhih4uarh.png" alt=" " width="800" height="69"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWX is now accessible at http://: — in my case port 32418. Log in with username admin and the decoded password.&lt;/p&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%2Fqai8m7fjm2mm5414g7v1.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%2Fqai8m7fjm2mm5414g7v1.png" alt=" " width="796" height="37"&gt;&lt;/a&gt;&lt;/p&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%2F2br4ud54li4wt3qq9vkv.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%2F2br4ud54li4wt3qq9vkv.png" alt=" " width="799" height="76"&gt;&lt;/a&gt;&lt;/p&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%2Fsnxexnj3ipn9xo2x71ik.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%2Fsnxexnj3ipn9xo2x71ik.png" alt=" " width="800" height="322"&gt;&lt;/a&gt;&lt;/p&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%2Fdt350o14luh93wdw4zc1.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%2Fdt350o14luh93wdw4zc1.png" alt=" " width="799" height="297"&gt;&lt;/a&gt;&lt;/p&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%2F5y849mzbfsc6suvlp4fx.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%2F5y849mzbfsc6suvlp4fx.png" alt=" " width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Errors I Hit and How I Fixed Them&lt;br&gt;
Error 1 — Node Taint Issue (Pods Stuck in Pending)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Warning  FailedScheduling  2m37s  default-scheduler&lt;br&gt;&lt;br&gt;
0/1 nodes are available: 1 node(s) had untolerated taint &lt;br&gt;
{node.kubernetes.io/not-ready: }&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, check the actual taint on your node:&lt;/strong&gt;&lt;br&gt;
kubectl describe node siri-awx-01 | grep Taints&lt;/p&gt;

&lt;h1&gt;
  
  
  Taints: node.kubernetes.io/not-ready:NoSchedule
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Remove it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;kubectl taint nodes siri-awx-01 node.kubernetes.io/not-ready:NoSchedule-&lt;/p&gt;

&lt;h1&gt;
  
  
  node/siri-awx-01 untainted
&lt;/h1&gt;

&lt;p&gt;After removing the correct taint, pods started coming up properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error 2 — ImagePullSecret Error&lt;/strong&gt;&lt;br&gt;
FailedToRetrieveImagePullSecret  Unable to retrieve some image pull secrets &lt;br&gt;
(redhat-operators-pull-secret); attempting to pull the image may not succeed.&lt;/p&gt;

&lt;p&gt;Debug with:&lt;br&gt;
&lt;code&gt;kubectl logs awx-operator-controller-manager-6586fccbdd-xtdvj -n awx&lt;br&gt;
kubectl get events -n awx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Fix — edit the deployment and comment out the imagePullSecrets section:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl edit deployment awx-operator-controller-manager -n awx&lt;/code&gt;&lt;br&gt;
Kubernetes automatically picks up the changes. No manual restart needed.&lt;br&gt;
&lt;strong&gt;Accessing the AWX Web Interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl expose deployment ansible-awx-web --name ansible-awx-web-svc --type NodePort -n awx&lt;br&gt;
kubectl get svc ansible-awx-web-svc -n awx&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Get admin password
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;kubectl get secret ansible-awx-admin-password -o jsonpath="{.data.password}" -n awx | base64 --decode ; echo&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Verification&lt;/strong&gt;&lt;br&gt;
Once logged in, I ran a demo playbook to make sure everything was wired up correctly. Seeing that first successful job run in the AWX UI after all of this setup was genuinely satisfying.&lt;/p&gt;

&lt;p&gt;This wasn’t a smooth “follow steps → done” kind of project.&lt;/p&gt;

&lt;p&gt;It was more like:&lt;/p&gt;

&lt;p&gt;set up cluster → something breaks&lt;br&gt;
fix networking → something else breaks&lt;br&gt;
fix storage → something else breaks again&lt;br&gt;
finally get AWX running → feels like victory&lt;/p&gt;

&lt;p&gt;But I did learn a lot about how Kubernetes actually behaves when things aren’t ideal.&lt;/p&gt;

&lt;p&gt;If you’re doing this yourself and hit weird issues, you’re probably not alone—most of the fixes I found were pieced together from random threads and trial-and-error.&lt;/p&gt;

&lt;p&gt;Still worth it though.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;happy to help!
— Sireesha&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>ansible</category>
    </item>
    <item>
      <title>Automating Windows Server Setup with Ansible: My DevOps Journey (Part 2)</title>
      <dc:creator>Sireesharaju Kamparaju</dc:creator>
      <pubDate>Tue, 12 May 2026 13:12:31 +0000</pubDate>
      <link>https://forem.com/sirisharaju_kamparaju_c9c/automating-windows-server-setup-with-ansible-my-devops-journey-part-2-92e</link>
      <guid>https://forem.com/sirisharaju_kamparaju_c9c/automating-windows-server-setup-with-ansible-my-devops-journey-part-2-92e</guid>
      <description>&lt;p&gt;In my previous blog, I covered how I automated Linux server provisioning using Ansible — things like SSH hardening, reusable roles, and playbooks. That setup worked really well, so the next thing I wanted to tackle was Windows.&lt;/p&gt;

&lt;p&gt;This post is about the Windows side of the setup — enabling WinRM, creating a reusable Windows role, and finally combining both Linux and Windows automation into a single Ansible workflow..&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows Automation Feels Different at First&lt;/strong&gt;&lt;br&gt;
I’ll be honest — moving from Linux automation to Windows automation was a little frustrating in the beginning.&lt;/p&gt;

&lt;p&gt;With Linux, Ansible over SSH just works. Most tutorials online assume that setup, so things feel straightforward pretty quickly. Windows was different.&lt;/p&gt;

&lt;p&gt;The first thing I learned was that Ansible talks to Windows through WinRM instead of SSH. That meant extra configuration before I could even run my first playbook.&lt;/p&gt;

&lt;p&gt;A few things tripped me up early on:&lt;/p&gt;

&lt;p&gt;WinRM isn’t enabled by default on fresh Windows servers&lt;br&gt;
The modules are completely different from Linux&lt;br&gt;
Even simple tasks use different syntax and collections&lt;br&gt;
Some errors from WinRM are vague and take time to troubleshoot&lt;/p&gt;

&lt;p&gt;At one point I thought my firewall rules were wrong, but it turned out I had messed up the WinRM transport settings in the inventory file.&lt;/p&gt;

&lt;p&gt;Once I got past those initial issues though, the setup became much smoother.&lt;br&gt;
Note: Port 5985 should be allowed in firewall.&lt;/p&gt;

&lt;p&gt;**First Thing — Bootstrap WinRM (Just Once)&lt;br&gt;
Before Ansible can do anything on a Windows server, WinRM needs to be enabled. I ran this PowerShell script once on each new Windows machine — after that, Ansible handles everything:&lt;/p&gt;

&lt;p&gt;[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12&lt;br&gt;
$url = "&lt;a href="https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1&lt;/a&gt;"&lt;br&gt;
$file = "$env:temp\ConfigureRemotingForAnsible.ps1"&lt;br&gt;
(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)&lt;br&gt;
powershell.exe -ExecutionPolicy ByPass -File $file&lt;/p&gt;

&lt;p&gt;The first successful win_ping honestly felt like progress because I’d already spent a while debugging connection issues before getting there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding Windows Hosts to the Inventory&lt;/strong&gt;&lt;br&gt;
I kept windows and linux in the same inventory file because I wanted one central place to manage everything.&lt;/p&gt;

&lt;p&gt;The structure stayed mostly the same, but the connection settings were obviously different:&lt;/p&gt;

&lt;p&gt;all:&lt;br&gt;
  children:&lt;br&gt;
    linux_servers:&lt;br&gt;
      hosts:&lt;br&gt;
        linux-01:&lt;br&gt;
          ansible_host: 10.0.1.10&lt;br&gt;
          ansible_user: ec2-user&lt;br&gt;
          ansible_ssh_private_key_file: ~/.ssh/id_rsa&lt;br&gt;
    windows_servers:&lt;br&gt;
      hosts:&lt;br&gt;
        win-01:&lt;br&gt;
          ansible_host: 10.0.2.10&lt;br&gt;
          ansible_user: Administrator&lt;br&gt;
          ansible_password: "{{ vault_win_password }}"&lt;br&gt;
          ansible_connection: winrm&lt;br&gt;
          ansible_winrm_transport: ntlm&lt;br&gt;
          ansible_port: 5985&lt;/p&gt;

&lt;p&gt;The Windows password is vaulted using ansible-vault — I never put credentials in plain text. That's just a habit I've built early on and I'd recommend everyone do the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building the Windows Role&lt;/strong&gt;&lt;br&gt;
I kept the same role-based structure I used for Linux. Here's how the Windows role looks:&lt;/p&gt;

&lt;p&gt;roles/&lt;br&gt;
  windows_setup/&lt;br&gt;
    ├── tasks/main.yml&lt;br&gt;
    └── defaults/main.yml&lt;/p&gt;

&lt;p&gt;roles/windows_setup/defaults/main.yml&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;p&gt;name: Ensure WinRM service is running and set to auto start&lt;br&gt;
ansible.windows.win_service:&lt;br&gt;
name: WinRM&lt;br&gt;
state: started&lt;br&gt;
start_mode: auto&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;name: Disable unencrypted WinRM traffic&lt;br&gt;
ansible.windows.win_shell: |&lt;br&gt;
winrm set winrm/config/service '@{AllowUnencrypted="false"}'&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;name: Configure Windows Firewall to allow WinRM&lt;br&gt;
ansible.windows.win_firewall_rule:&lt;br&gt;
name: WinRM HTTP&lt;br&gt;
localport: "{{ winrm_port }}"&lt;br&gt;
action: allow&lt;br&gt;
direction: in&lt;br&gt;
protocol: tcp&lt;br&gt;
state: present&lt;br&gt;
enabled: true&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: Check for available Windows Security Updates&lt;br&gt;
ansible.windows.win_updates:&lt;br&gt;
category_names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SecurityUpdates
state: searched
register: update_result&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;name: Display available updates&lt;br&gt;&lt;br&gt;
ansible.builtin.debug:&lt;br&gt;&lt;br&gt;
msg: "{{ update_result.updates | length }} security update(s) available"&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Windows Playbook&lt;/strong&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;name: Configure Windows Servers
hosts: windows_servers
roles:

&lt;ul&gt;
&lt;li&gt;windows_setup&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;One thing I noticed here — there's no become: true like I used on Linux. Windows doesn't use sudo. The Administrator account takes care of privilege escalation directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bringing It All Together — site.yml&lt;/strong&gt;&lt;br&gt;
This is the part I enjoyed the most. One playbook, one command, both Linux and Windows configured together:&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;import_playbook: playbooks/linux_setup.yml&lt;/li&gt;
&lt;li&gt;import_playbook: playbooks/windows_setup.yml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;And to run everything:&lt;/strong&gt;&lt;br&gt;
ansible-playbook site.yml -i inventory/hosts.yml --ask-vault-pass&lt;/p&gt;

&lt;p&gt;That's it. Ansible runs through Linux first, then Windows — clean and consistent every single time.&lt;/p&gt;

&lt;p&gt;ansible windows_servers -i inventory/hosts.yml -m ansible.windows.win_ping&lt;/p&gt;

&lt;p&gt;If I get pong back, I know I'm good to go.&lt;br&gt;
&lt;strong&gt;WinRM transport depends on your environment&lt;/strong&gt;. I used ntlm since my servers weren't in a domain. If you're working in an Active Directory setup, kerberos is the better and more secure option.&lt;br&gt;
&lt;strong&gt;Don't mix Linux and Windows modules.&lt;/strong&gt; Early on I made the mistake of trying to use a Linux module on a Windows host — it fails and the error isn't always obvious. Stick to ansible.windows.* for everything Windows-related.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Changed After Automating This&lt;/strong&gt;&lt;br&gt;
Before automation, setting up a Windows server usually meant opening RDP, clicking through configuration screens, enabling services manually, and hoping I didn’t forget something important.&lt;/p&gt;

&lt;p&gt;Now the process is much simpler:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;add the server to inventory,&lt;/li&gt;
&lt;li&gt;run the playbook,&lt;/li&gt;
&lt;li&gt;wait a few minutes,&lt;/li&gt;
&lt;li&gt;done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That consistency alone made the effort worth it.&lt;/p&gt;

&lt;p&gt;Combined with the Linux setup from Part 1, I now have a single Ansible environment managing both Linux and Windows servers from one place. Getting both platforms working together took more troubleshooting than I expected, but finishing it felt genuinely rewarding.&lt;br&gt;
Coming Up in Part 3&lt;br&gt;
I'm planning to cover:&lt;/p&gt;

&lt;p&gt;User management across Linux and Windows&lt;br&gt;
Scheduling automated patching&lt;br&gt;
Plugging Ansible into a CI/CD pipeline&lt;/p&gt;

&lt;p&gt;Drop your questions or thoughts in the comments — always happy to discuss!&lt;br&gt;
— Sireesha&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>automation</category>
    </item>
    <item>
      <title>Automating Linux &amp; Windows Server Setup with Ansible: My DevOps Journey</title>
      <dc:creator>Sireesharaju Kamparaju</dc:creator>
      <pubDate>Tue, 12 May 2026 13:01:58 +0000</pubDate>
      <link>https://forem.com/sirisharaju_kamparaju_c9c/automating-linux-windows-server-setup-with-ansible-my-devops-journey-4ig9</link>
      <guid>https://forem.com/sirisharaju_kamparaju_c9c/automating-linux-windows-server-setup-with-ansible-my-devops-journey-4ig9</guid>
      <description>&lt;p&gt;I started this project because managing servers manually was getting repetitive — especially when it came to setup and configuration across both Linux and Windows machines.&lt;/p&gt;

&lt;p&gt;So I decided to automate the whole workflow using Ansible. The goal wasn’t just to “use Ansible,” but to actually standardize how servers are configured and reduce the amount of manual SSH and RDP work I was doing every time a new system came up.&lt;/p&gt;

&lt;p&gt;In this blog, I’ll walk through how I used Ansible playbooks and roles to automate server setup, including SSH configuration on Linux and basic provisioning tasks across Windows systems.&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Chose Ansible for This
&lt;/h2&gt;

&lt;p&gt;**&lt;br&gt;
Before I started using Ansible, server setup meant logging into each machine manually, running the same commands over and over, and hoping nothing was missed. One wrong step and the configuration was inconsistent across environments. Sound familiar?&lt;br&gt;
What drew me to Ansible was its simplicity — no agents to install, no complex setup. Just SSH into Linux, WinRM into Windows, and you're managing your entire fleet from a single control node. That agentless architecture was a game changer for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The goal was straightforward: automate the initial setup of both Linux and Windows servers using a clean, reusable Ansible structure. Here's what I set out to configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH hardening and configuration on Linux servers&lt;/li&gt;
&lt;li&gt;WinRM configuration to enable Ansible to talk to Windows servers&lt;/li&gt;
&lt;li&gt;Reusable roles for both OS types&lt;/li&gt;
&lt;li&gt;A master playbook to tie everything together.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Setting Up the Project Structure&lt;/strong&gt;&lt;br&gt;
The first thing I did was organize everything into roles. Roles keep your code clean, reusable, and easy to share across projects. Here's the structure I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-server-setup/
├── inventory/
│   ├── hosts.yml
├── roles/
│   ├── linux_ssh/
│   │   ├── tasks/main.yml
│   │   ├── templates/sshd_config.j2
│   │   ├── handlers/main.yml
│   │   └── defaults/main.yml
│   ├── windows_setup/
│   │   ├── tasks/main.yml
│   │   └── defaults/main.yml
├── playbooks/
│   ├── linux_setup.yml
│   ├── windows_setup.yml
└── site.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keeping Linux and Windows roles separate made everything much easier to maintain and debug independently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Inventory&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I defined both Linux and Windows hosts in a single YAML inventory, with the right connection settings for each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;all&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;linux_servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;linux-01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ansible_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.1.10&lt;/span&gt;
          &lt;span class="na"&gt;ansible_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ec2-user&lt;/span&gt;
          &lt;span class="na"&gt;ansible_ssh_private_key_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.ssh/id_rsa&lt;/span&gt;
    &lt;span class="na"&gt;windows_servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;win-01&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ansible_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10.0.2.10&lt;/span&gt;
          &lt;span class="na"&gt;ansible_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Administrator&lt;/span&gt;
          &lt;span class="na"&gt;ansible_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;vault_win_password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;ansible_connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;winrm&lt;/span&gt;
          &lt;span class="na"&gt;ansible_winrm_transport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ntlm&lt;/span&gt;
          &lt;span class="na"&gt;ansible_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5985&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice I vaulted the Windows password using ansible-vault — never hardcode credentials in plain text. Learned that lesson early!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH Hardening Role for Linux&lt;/strong&gt;&lt;br&gt;
This was the core of my Linux setup. The linux_ssh role handles SSH daemon configuration to make servers secure and consistent from day one.&lt;br&gt;
roles/linux_ssh/defaults/main.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ssh_port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;22&lt;/span&gt;
&lt;span class="na"&gt;permit_root_login&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no"&lt;/span&gt;
&lt;span class="na"&gt;password_authentication&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no"&lt;/span&gt;
&lt;span class="na"&gt;max_auth_tries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;roles/linux_ssh/templates/sshd_config.j2&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;Port &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;ssh_port&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
PermitRootLogin &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;permit_root_login&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
PasswordAuthentication &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;password_authentication&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
MaxAuthTries &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;max_auth_tries&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&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;Deploy SSH configuration&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sshd_config.j2&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/ssh/sshd_config&lt;/span&gt;
    &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0600"&lt;/span&gt;
  &lt;span class="na"&gt;notify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restart SSH&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;Ensure SSH service is running and enabled&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.service&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;sshd&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;started&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;roles/linux_ssh/handlers/main.yml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="nn"&gt;---&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;Restart SSH&lt;/span&gt;
  &lt;span class="na"&gt;ansible.builtin.service&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;sshd&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The handler ensures SSH only restarts when the config actually changes — not on every run. That small detail matters a lot in production.&lt;/p&gt;

</description>
      <category>ansible</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
