iran@portfolio:~$ cat projects/ansible-azure-hardening
~/projects/ansible-azure-hardening
AutomationAnsibleCI/CD

Ansible-Based Azure Server Configuration & Hardening

Post-provisioning configuration and security hardening of a 3-server Windows cluster (WEB, Job, and DB roles), fully automated with Ansible and triggered through an Azure Release Pipeline, replacing 3-4 hours of hands-on, interactive manual setup with a single unattended release.

83%+ faster configurationProduction
// key highlights
[01]

Solely designed and built role-based Ansible playbooks for a 3-server Windows cluster (WEB, Job, DB), covering both shared and role-specific configuration, driven by a single server_type variable

[02]

Reused one Service Principal identity across both Azure Storage and Azure Key Vault access, rather than managing separate credentials per service

[03]

Designed dependency installation to download, install, and then clean up every installer artifact, leaving nothing behind on target servers

[04]

Automated security hardening (Antivirus Software deployment and baseline compliance) plus SSL certificate retrieval from Azure Key Vault for web servers, as part of the same playbook run

[05]

Deployed a centrally-positioned Ansible controller server onboarded into an Azure DevOps deployment group

[06]

Ran configuration jobs in parallel across multiple agents, cutting a 3-4 hour manual process down to under 30 minutes for the full cluster

[07]

Built an Azure Release Pipeline requiring zero human interaction beyond triggering the release

[08]

Achieved full traceability of every configuration change through version-controlled playbooks

// overview

Post-provisioning configuration and security hardening for a 3-server Windows cluster (WEB, Job, and DB roles) that hosts the organization's product, fully automated with Ansible and triggered through an Azure Release Pipeline.

I designed and built this automation entirely on my own: role-based and shared playbooks covering every server's specific and common setup needs, a centrally-positioned Ansible controller onboarded into an Azure DevOps deployment group, and a release pipeline that runs the whole process against all three servers in parallel with no manual steps beyond creating the release.

// problem

The organization deploys its product across a cluster of three Windows servers, each with a distinct role (WEB, Job, and DB), provisioned together as a set. After provisioning, each server needed a specific combination of dependent software and tools installed before it was ready to host the product: some tools were common across all three servers, others were specific to each server's role.

Every time the cluster was provisioned, this setup had to be repeated by hand, requiring an engineer's constant attention to click through installers and respond to prompts rather than being able to start it and walk away. The process took 3 to 4 hours per cluster, partly because configuring all three servers by hand at the same time wasn't practical. Servers also needed to be hardened afterward, including installing antivirus software and applying other security baseline tasks, none of which was automated either.

// approach

  1. Started by understanding exactly what each server role needed: which tools and dependencies were common across all three servers, and which were specific to the WEB, Job, or DB role.

  2. Worked through the install and configuration steps for each tool manually on a test environment first, to understand exactly what each step involved before automating any of it.

  3. Designed an Ansible project structured around role-based playbooks, one per server class, plus shared roles for common tasks, driven by a single server_type variable (web, db, or job) that determines which set of tasks runs for each server, so adding or changing a server type only means changing one variable rather than the underlying logic.

  4. Authenticated the Ansible project against Azure using a Service Principal, and used that same identity for both Azure Storage access (to pull down installer dependencies) and Azure Key Vault access (to retrieve SSL certificates and secrets), rather than managing separate credentials for each service.

  5. Designed the dependency-installation tasks to download only the required .exe and .msi files from Azure Storage onto the controller, copy them across to the target server, run the installation, and then remove those files from the target server once installation completed, so no installer artifacts are left sitting on production servers afterward.

  6. Built the hardening and web-specific steps into the same playbooks: Antivirus deployment and the rest of the security baseline for every server, plus SSL certificate retrieval from Azure Key Vault and IIS binding configuration for web servers specifically, so every server is compliant and, where relevant, certificate-ready the moment configuration finishes.

  7. Deployed an Ansible controller server positioned centrally in the network so it has reach to every target server, and onboarded it into an Azure DevOps deployment group so Azure Pipelines could dispatch jobs to it directly.

  8. Configured multiple agents on the controller server so it could run Ansible jobs against all three servers in parallel instead of one at a time; this is what took the process from hours down to under 30 minutes for the whole cluster.

  9. Built an Azure Release Pipeline where creating a release is the only manual action required: the pipeline dispatches the job to the controller server, which runs the Ansible project against the newly provisioned servers with no further human interaction, including any restarts individual tasks require along the way.

  10. Integrated this with provisioning: ARM Templates and Azure CLI provision the raw VMs, and the release pipeline then takes each provisioned server the rest of the way to fully configured and hardened.

// architecture

// outcome

What used to take 3 to 4 hours of hands-on, constant-interaction work per server cluster, partly because all three servers couldn't practically be configured by hand at the same time, now completes in under 30 minutes for the entire cluster. Running multiple agents on the Ansible controller lets the release pipeline configure and harden all three servers in parallel instead of one at a time, including waiting through any required restarts, with zero human interaction beyond triggering the release itself. Configuration is consistent and compliant across every cluster, and every change to server setup is version-controlled and traceable through the playbooks themselves. Dependency installers are pulled from Azure Storage, used, and removed automatically, leaving target servers clean rather than cluttered with leftover installation files.

// references