<?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: Sirisharaju Kamparaju</title>
    <description>The latest articles on DEV Community by Sirisharaju Kamparaju (@sirisharaju_kamparaju_c9c).</description>
    <link>https://dev.to/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>DEV Community: Sirisharaju Kamparaju</title>
      <link>https://dev.to/sirisharaju_kamparaju_c9c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sirisharaju_kamparaju_c9c"/>
    <language>en</language>
    <item>
      <title>Automating Windows Server Setup with Ansible: My DevOps Journey (Part 2)</title>
      <dc:creator>Sirisharaju Kamparaju</dc:creator>
      <pubDate>Tue, 12 May 2026 13:12:31 +0000</pubDate>
      <link>https://dev.to/sirisharaju_kamparaju_c9c/automating-windows-server-setup-with-ansible-my-devops-journey-part-2-92e</link>
      <guid>https://dev.to/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 walked through how I automated Linux server setup using Ansible — SSH hardening, roles, and playbooks. If you haven't read that yet, check out Part 1 here.&lt;br&gt;
In this post, I'll focus entirely on the Windows side — how I configured WinRM, built a reusable Windows role, and tied everything together into one master playbook that manages both Linux and Windows servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Windows Automation Feels Different at First&lt;/strong&gt;&lt;br&gt;
When I first tried to automate Windows servers with Ansible, it didn't feel anything like Linux. On Linux, Ansible just connects over SSH and you're off. Windows doesn't work that way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A few things that caught my attention early on&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows uses WinRM instead of SSH — that's how Ansible communicates with it&lt;/li&gt;
&lt;li&gt;Fresh Windows servers don't have WinRM enabled — I had to manually turn it on the first time&lt;/li&gt;
&lt;li&gt;The modules are completely different — no apt, no service — everything goes through the ansible.windows collection&lt;/li&gt;
&lt;li&gt;Once I got my head around these differences, the rest came together pretty smoothly.&lt;/li&gt;
&lt;/ul&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;&lt;strong&gt;Adding Windows Hosts to the Inventory&lt;/strong&gt;&lt;br&gt;
I added the Windows servers into the same inventory file I was already using for Linux. The connection settings are different but the structure stays clean:&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 This&lt;/strong&gt;&lt;br&gt;
Before this setup, configuring a new Windows server meant RDP-ing in, clicking through settings, and hoping I didn't miss anything. Now I just add the host to the inventory and run the playbook. Same result every time, no matter how many servers I'm dealing with.&lt;br&gt;
Combined with Part 1, I now have a single automation setup managing both Linux and Windows from one place — and it's honestly one of the most satisfying things I've built so far in my DevOps journey.&lt;/p&gt;

&lt;p&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>Sirisharaju Kamparaju</dc:creator>
      <pubDate>Tue, 12 May 2026 13:01:58 +0000</pubDate>
      <link>https://dev.to/sirisharaju_kamparaju_c9c/automating-linux-windows-server-setup-with-ansible-my-devops-journey-4ig9</link>
      <guid>https://dev.to/sirisharaju_kamparaju_c9c/automating-linux-windows-server-setup-with-ansible-my-devops-journey-4ig9</guid>
      <description>&lt;p&gt;Ansible is one of the most powerful automation tools used by DevOps engineers to manage infrastructure, configure servers, and deploy applications at scale. In this blog, I'll walk through how I automated Linux and Windows server setup using Ansible — specifically around SSH configuration, roles, and playbooks — and reduced repetitive manual work significantly.&lt;br&gt;
This project demonstrates how automation can simplify server management while improving consistency and deployment speed across both Linux and Windows environments.&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;/p&gt;

&lt;p&gt;ansible-server-setup/&lt;br&gt;
├── inventory/&lt;br&gt;
│   ├── hosts.yml&lt;br&gt;
├── roles/&lt;br&gt;
│   ├── linux_ssh/&lt;br&gt;
│   │   ├── tasks/main.yml&lt;br&gt;
│   │   ├── templates/sshd_config.j2&lt;br&gt;
│   │   ├── handlers/main.yml&lt;br&gt;
│   │   └── defaults/main.yml&lt;br&gt;
│   ├── windows_setup/&lt;br&gt;
│   │   ├── tasks/main.yml&lt;br&gt;
│   │   └── defaults/main.yml&lt;br&gt;
├── playbooks/&lt;br&gt;
│   ├── linux_setup.yml&lt;br&gt;
│   ├── windows_setup.yml&lt;br&gt;
└── site.yml&lt;/p&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;/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;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;/p&gt;

&lt;p&gt;ssh_port: 22&lt;br&gt;
permit_root_login: "no"&lt;br&gt;
password_authentication: "no"&lt;br&gt;
max_auth_tries: 3&lt;/p&gt;

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

&lt;p&gt;Port {{ ssh_port }}&lt;br&gt;
PermitRootLogin {{ permit_root_login }}&lt;br&gt;
PasswordAuthentication {{ password_authentication }}&lt;br&gt;
MaxAuthTries {{ max_auth_tries }}&lt;br&gt;
PubkeyAuthentication yes&lt;br&gt;
AuthorizedKeysFile .ssh/authorized_keys&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;p&gt;name: Deploy SSH configuration&lt;br&gt;
ansible.builtin.template:&lt;br&gt;
src: sshd_config.j2&lt;br&gt;
dest: /etc/ssh/sshd_config&lt;br&gt;
owner: root&lt;br&gt;
group: root&lt;br&gt;
mode: "0600"&lt;br&gt;
notify: Restart SSH&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;name: Ensure SSH service is running and enabled&lt;br&gt;
ansible.builtin.service:&lt;br&gt;
name: sshd&lt;br&gt;
state: started&lt;br&gt;
enabled: true&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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




&lt;ul&gt;
&lt;li&gt;name: Restart SSH
ansible.builtin.service:
name: sshd
state: restarted&lt;/li&gt;
&lt;/ul&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>
    </item>
  </channel>
</rss>
