← All scripts

Endpoint security audit

Endpoint Security Posture Audit

Collects a device security posture (local admins, inactive and passwordless accounts, RDP, firewall, Microsoft Defender for Endpoint, and BitLocker per drive) and writes it to JSON plus the console. No external database.

Download .ps184 lines · PowerShell

Replace generic placeholder values (tenant, domain, secrets) for your own environment before running. Read it first and test safely.

<#
.SYNOPSIS
    Collects a Windows endpoint's security posture and outputs it as JSON (and to the console).

.DESCRIPTION
    Gathers, for the local machine:
      - Local administrator accounts
      - Enabled local accounts inactive for 30+ days
      - Enabled local accounts with no password required
      - RDP (remote access) enabled/disabled
      - Windows Firewall enabled on all profiles
      - Microsoft Defender for Endpoint (MDE) service running
      - BitLocker protection status per drive
    ...then writes the result to a JSON file in Downloads and prints it to the console, so
    it can be captured by an RMM, a scheduled task, or piped anywhere you like.

    Run as administrator. No external database or modules required.
#>

# --- Collect the security posture ---

# Enabled local accounts not logged in for 30+ days
$inactiveusers = Get-LocalUser | Where-Object { $_.Lastlogon -lt (Get-Date).AddDays(-30) -and $_.Enabled -eq $true } | Select-Object Name
$inactiveusersNameArray = @()
foreach ($inactiveuser in $inactiveusers) { $inactiveusersNameArray += $inactiveuser.Name }

# Local administrators (net localgroup output has 2 header lines to drop)
$localAdminOutput = net localgroup administrators
$localAdminUsers = $localAdminOutput | Select-String -Pattern "^\s*([^\s]+)\s*$" | ForEach-Object { $_.Matches[0].Groups[1].Value }
$arrayList = [System.Collections.ArrayList]$localAdminUsers
$arrayList.RemoveRange(0, 2)
$localAdminUsers = $arrayList.ToArray()

# Enabled local accounts that do not require a password
$insecureUsers = Get-LocalUser | Where-Object { $_.PasswordRequired -eq $false -and $_.Enabled -eq $true }
$insecureUsersNames = $insecureUsers.Name
if ($null -eq $insecureUsersNames) { $insecureUsersNames = "NONE" }

# RDP enabled?
$rdpenabled = Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -Name "fDenyTSConnections" | Select-Object fDenyTSConnections
$rdp = if ($rdpenabled.fDenyTSConnections -eq 1) { "Disabled" } else { "Enabled" }

# Firewall enabled on all 3 profiles?
if (((Get-NetFirewallProfile | Select-Object name, enabled) | Where-Object { $_.Enabled -eq $True } | Measure-Object).Count -eq 3) { $firewall = "Enabled" } else { $firewall = "Disabled" }

# Microsoft Defender for Endpoint service running?
$mdecheck = (Get-Service -Name "Windows Defender Advanced Threat Protection Service" -ErrorAction SilentlyContinue).Status
$mdestatus = if ($mdecheck -eq "Running") { "Enabled" } else { "Disabled" }

# BitLocker protection per drive
$drives = Get-BitLockerVolume
$enabledDrives = @()
$disabledDrives = @()
foreach ($drive in $drives) {
    if ($drive.ProtectionStatus -eq 'On') { $enabledDrives += $drive.MountPoint.TrimEnd('\') }
    elseif ($drive.ProtectionStatus -eq 'Off') { $disabledDrives += $drive.MountPoint.TrimEnd('\') }
}
if ($enabledDrives.Count -eq 0) {
    $bitlockerstatus = "Disabled: No drives with BitLocker enabled."
} elseif ($disabledDrives.Count -eq 0) {
    $bitlockerstatus = "Enabled: $($enabledDrives -join ', ')"
} else {
    $bitlockerstatus = "Enabled: $($enabledDrives -join ', ') Disabled: $($disabledDrives -join ', ')"
}

# --- Assemble and output the result ---
$results = [PSCustomObject]@{
    computerName  = $env:COMPUTERNAME
    collectedAt   = (Get-Date).ToString('s')
    adminUsers    = $localAdminUsers
    inactiveUsers = $inactiveusersNameArray
    insecureUsers = $insecureUsersNames
    firewall      = $firewall
    remoteAccess  = $rdp
    mdeStatus     = $mdestatus
    bitlocker     = $bitlockerstatus
}

# Write to a JSON file in Downloads, and print to the console
$OutputPath = "$env:USERPROFILE\Downloads\SecurityAudit_$($env:COMPUTERNAME)_$(Get-Date -Format 'yyyy-MM-dd').json"
$results | ConvertTo-Json -Depth 4 | Set-Content -Path $OutputPath -Encoding utf8
Write-Host "Security audit written to: $OutputPath" -ForegroundColor Green
$results | ConvertTo-Json -Depth 4