Skip to content

Module 10 — Hardening AD as Code

Type 4 · Audit→Build→Verify — audit Corp's ACL posture with dacledit.py into a CIS-aligned scored report, codify the key remediations as an idempotent Ansible playbook, then apply the two highest-value fixes to the live DC and prove them: the AS-REP roast against svc-legacy now fails and the HIGH findings re-score clear. (Secondary: Judgment-as-Code / Gate — frame the score as a held baseline so "did we improve or regress?" is gated.) Go to the hands-on lab →

Last reviewed: 2026-06

Active Directory & Windows Securityevery misconfiguration found in modules 02-09 has a fix; this module turns those fixes into code that can be deployed, reviewed, and re-run.

Difficulty: Intermediate  ·  Estimated time: ~4–6 hrs (study + lab)  ·  Prerequisites: Foundations

In 60 seconds

Hardening that lives in a SharePoint checklist drifts; if it isn't in version control, it isn't real. This module turns the mitigations from the attack path into code: audit Corp's ACL posture with dacledit.py into a CIS-aligned scored report, then codify the key fixes as an idempotent Ansible playbook. The non-negotiable second half is proving it on the live DC — apply the two highest-value fixes, watch the AS-REP roast against svc-legacy now fail, and re-score to show the HIGH findings clear. A playbook never tested against the domain is documentation, not a control.

Why this matters

Security hardening that lives in a checklist in a SharePoint site will drift. Group Policy changes made in the console are unversioned. An AD hardening posture that cannot be measured cannot be improved. The principle is the same as detection-as-code: if the hardening isn't in version control, it isn't real. This module operationalises the mitigations from the attack path — GPO settings, ACL corrections, account attribute changes — as Ansible tasks and scored checks that can be run on a schedule.

Objective

Audit the Corp domain's ACL posture using dacledit.py, produce a scored hardening report against a CIS-aligned checklist, and write an Ansible playbook that codifies the key remediations as idempotent, reviewable tasks — then apply the two highest-value fixes to the live DC and prove them: re-run the attack (the AS-REP roast against svc-legacy now fails) and re-score to show the HIGH findings cleared. Auditing/authoring the remediation and proving it closes the path on the live domain are equal halves.

The core idea

The gap between "we know what to fix" and "it's actually fixed" in an AD environment is bridged by two practices: automated posture measurement and configuration-as-code. Automated posture measurement means running a script that queries the domain for known-bad configurations (Kerberoastable service accounts, accounts without pre-auth, unconstrained delegation, ACL misconfigurations, missing GPO settings) and produces a score. Configuration-as-code means expressing the remediations as idempotent scripts or playbooks that can be applied, reviewed in pull requests, and re-run to verify they took effect.

The mental model

This is detection-as-code pointed at hardening: measure posture as data, express fixes as idempotent code, re-run to prove. A fix you applied by clicking through a console is unversioned and unrepeatable; the same fix as an Ansible task with a human-readable name: is reviewable in a PR, runnable with --check, and its own audit trail.

Tools like PingCastle (Windows-only, freemium) do automated AD posture scoring; for a Linux-friendly, open-source equivalent, dacledit.py (from impacket) reads object ACLs, and ldapsearch-based scripts can audit the user attributes that expose the domain to attack. The posture audit is not a one-time engagement deliverable — it should run on a schedule (weekly or after any significant change) and generate a delta report: "we had three Kerberoastable accounts last week; we have one now." That delta is how you demonstrate progress.

The Ansible approach works because AD objects are ultimately LDAP objects, and Ansible's community.windows collection has modules for GPO management (win_gpo), AD user manipulation (win_user, microsoft.ad.user), and group membership (microsoft.ad.group). An Ansible task that reads "ensure svc-legacy has DONT_REQUIRE_PREAUTH=False" is precise, reviewable by a security team, and verifiable by re-running with --check. The same playbook that applies the hardening can document it — each task's name: field is the human-readable audit trail.

The CIS Benchmark for Active Directory covers hundreds of controls, but the 20 that matter most in a typical environment are all in the same territory as the attacks you've learned: service account password age, SPN hygiene, delegation settings, privileged group membership, GPO audit policy coverage, and SMB signing. Hardening-as-code that covers those 20 controls, deployed and running in CI, is more valuable than a 200-item checklist nobody re-checks.

The gotcha

A hardening playbook that has never been run against the domain is documentation, not a control. The half teams skip is the proof: apply the fix to the live DC, re-run the attack (the AS-REP roast against svc-legacy must now fail) and re-score to show the HIGH finding cleared. Authoring the remediation and proving it closes the path are equal halves — assert neither without the other.

Go deeper: a score is a priority guide, not the deliverable

PingCastle-style scoring (0–100, lower is riskier) is genuinely useful for triage — it tells you which control category to attack first. But a single number hides which control moved, and a point-in-time score says nothing about next month. Treat the score as a baseline to trend (this week vs. last week is how you show progress) and as the input to module 13's drift detector, not as the thing you hand the client.

AI caveat

A model drafts Ansible YAML well but reaches for deprecated module names (win_ad_user instead of microsoft.ad.user) and wrong parameters. Validate every module name and parameter against the Galaxy docs and run --check before you ever apply; the draft is a starting point, not a finished playbook.

Learn (~3 hrs)

AD posture assessment tools - PingCastle — AD Risk Assessment (pingcastle.com) — the most widely used AD posture tool. Free community edition runs on Windows; understand the scoring methodology and risk categories even if you use impacket equivalents for the lab. - dacledit.py (impacket GitHub) — the Python-native ACL editor and reader; used in the lab to audit ACEs on AD objects. Read the --action read usage.

CIS benchmark - CIS Microsoft Windows Server 2019 Benchmark (CIS) — free download (registration required). Focus on the Active Directory-specific sections: account policies, Kerberos settings, audit policies, group memberships. Use as the reference for the hardening checklist.

Ansible for Windows/AD - Ansible — microsoft.ad collection (Galaxy docs) — the official AD collection. The microsoft.ad.user and microsoft.ad.group modules are the primary ones for this module. Read the parameter documentation.

Key concepts

  • Posture measurement = automated query against the domain for known-bad configurations, scored and trended.
  • Configuration-as-code for AD = Ansible tasks over WinRM or LDAP; idempotent and reviewable.
  • The 20 highest-priority controls: service account password age, SPN hygiene, no-preauth accounts, unconstrained delegation, privileged group membership, GPO audit coverage, SMB signing.
  • PingCastle scores posture on a 0-100 scale (lower is better/riskier); use the risk categories as a priority guide.
  • Hardening playbooks should be in version control and run with --check before applying.
  • Delta reporting (this week vs. last week) is how you demonstrate remediation progress.
  • Apply then verify: prove a remediation works by applying it to the live DC and re-running the attack — a hardening playbook never tested against the domain is documentation, not a control.

AI acceleration

Ask a model to generate an Ansible playbook from the mitigation list in module 08. The model is good at Ansible YAML structure but will often use deprecated module names (e.g., win_ad_user instead of microsoft.ad.user) or incorrect parameter names. Validate every module name and parameter against the Galaxy docs before running --check. The draft is a starting point, not a finished playbook.

Check yourself

  • Why is "we have a hardening checklist" weaker than "the hardening is in version control"?
  • What proves a remediation actually worked, beyond the playbook reporting success?
  • Why diff posture facts over time (this week vs. last week) rather than just reporting today's score?

Comments

Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).