Manage AD Accounts - Part 1

TLDR
  • Built a PowerShell Windows Forms GUI for managing Active Directory contractor accounts.
  • Three roles — SuperAdmin, HelpDesk, ContractorManager — each with different permissions enforced at every action.
  • Searches across multiple OUs, updates expiry dates, disables accounts, and logs everything.
  • Designed for Intune deployment: drop a .ps1 and a config.json, runs on any domain-joined machine with RSAT.
618
Lines of code
3
Roles
RBAC
Security model
Intune
Deployment

The problem

Contractor accounts in Active Directory are a recurring headache. They need expiry dates set when they start, those dates need to be extended when contracts get renewed, and the accounts need to be disabled when people leave. The team members doing this work — helpdesk staff, contractor managers — typically don't have, and shouldn't have, broad access to Active Directory to do it.

The usual options are either granting too much AD access, building delegation rules that are hard to audit, or asking IT to handle every request manually. None of those scale well. I wanted a single tool that any authorized person could run from their workstation, with the permissions enforced in the application itself.

What the tool does

It's a PowerShell script that opens a Windows Forms GUI. You search for a user, select them from a dropdown, and either update their account expiry date or disable their account. Every action goes through a permission check based on who you are. Every action — including denied ones — gets written to an audit log.

The tool identifies contractors two ways: accounts with an expiration date set, or accounts with "Contractor" in the Description field. It searches across multiple Organizational Units simultaneously, so it doesn't matter where in AD the contractor accounts live.

The security model

Everything is built around three roles, determined by Active Directory security group membership:

  • SuperAdmin — Full access. Can see and modify any account in the configured OUs.
  • HelpDesk — Can modify any non-privileged account. Domain Admins, Enterprise Admins, Schema Admins, and any account with adminCount=1 are blocked.
  • ContractorManager — Can only modify accounts identified as contractors. Everything else is denied.

If you're not in any of the three groups, the tool won't launch. It shows an access denied dialog and exits. The role check happens at startup, and then again before every write operation.

# Permission check before every write
$permCheck = Test-CanModifyAccount -samAccountName $script:currentSam -userRole $script:userRole
if (-not $permCheck.Allowed) {
    Log-Action "DENIED: $($permCheck.Reason)" -level "SECURITY"
    return
}

Admin account protection is deliberate. Even if someone in the HelpDesk group tries to disable a Domain Admin, the tool blocks it. This is a guardrail that exists regardless of what AD delegation rules are in place.

Configuration

All environment-specific values live in config.json: the security group names, the OU paths to search, and the window title. The script ships with a config.example.json template. Nothing is hardcoded.

{
  "SecurityGroups": {
    "SuperAdmin": "SG-AD-SuperAdmins",
    "HelpDesk": "SG-AD-HelpDesk",
    "ContractorManager": "SG-AD-ContractorMgr"
  },
  "SearchBaseOUs": [
    "OU=Contractors,DC=YOURDOMAIN,DC=local"
  ],
  "AppTitle": "Contractors Account Management"
}

This means a single script can be deployed across different domains or environments. Change the config, not the code.

Audit logging

Every action writes to %ProgramData%\ADAccountManagement\actions.log with four severity levels:

  • INFO — Successful operations and search queries
  • WARNING — Non-critical issues like an unreachable OU
  • ERROR — Failed operations
  • SECURITY — Authentication events and access denials

Each log entry includes the timestamp, the operator's username and domain, the computer name, and what happened. Security denials are logged with the reason — so if a ContractorManager tries to modify a non-contractor account, the log shows exactly what was attempted and why it was blocked.

Deployment

The tool was designed for Intune deployment. It's a single .ps1 file alongside a config.json. No modules to install beyond RSAT's ActiveDirectory module, which is a standard prerequisite for any AD management workstation. It runs on PowerShell 5.1, which ships with Windows 10 and later.

The GUI is built with Windows Forms — no WPF, no external dependencies. It loads fast, and it runs on any Windows machine without additional runtime requirements.

What's next

Before publishing this to GitHub, I ran the full script through an AI code review. The goal was to catch security issues, logic bugs, and anything that would look rough in a portfolio context. It found 21 issues across five severity levels — including a few critical ones I wouldn't have caught on a manual read-through. The next post covers what the review found and how I'm addressing each issue.


View the project on GitHub

PowerShell GUI for AD contractor account management — RBAC, audit logging, Intune deployment.

View on GitHub