iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🗺️

[PCI DSS] No More GUI: Accurate Server Configuration Data Retrieval with PowerShell

に公開

Introduction

"Show me the current members of the local Administrators group on this server."
"Provide evidence that the password policy is being applied correctly."

If you are an infrastructure engineer, you often find yourself in situations where you are asked to provide these "current settings" for audit compliance or incident investigation.
In those moments, are you still connecting via Remote Desktop Protocol (RDP) and opening GUI setting screens to check?

In strict environments such as PCI DSS (Payment Card Industry Data Security Standard), "accuracy" and "reproducibility" are required for verifying configuration information.
Visual verification via GUI carries the risk of human error by the person in charge and is unsuitable for maintaining as a record.

In this article, I will explain how to accurately obtain and preserve major Windows Server setting items as "text data (primarily in CSV format)" using PowerShell.

Why "Command Acquisition" Instead of "GUI Verification"?

Since Windows Server has an excellent GUI, it is tempting to check settings via the screen. However, from a configuration management perspective, it has the following disadvantages:

  • Cannot copy-paste: Many text elements on the screen cannot be copied, which induces mistakes when transcribing to configuration documents.
  • Difficult to compare: When checking for "setting differences between Server A and B," you have to compare screens side-by-side.
  • Does not serve as an audit trail: Information such as "who checked it and when" is not recorded simply by looking at the screen.

By acquiring information as objects in PowerShell and exporting them to CSV, all these issues are resolved.
Specifically, the fact that you can compare them using diff tools like WinMerge is a huge operational advantage.

[Practice 1] Complete Inventory of Privileged IDs (Local & Domain)

"Management of privileged IDs" is the most scrutinized aspect of security audits.
It is inefficient to visually check who has administrative privileges by opening "Computer Management" or "Active Directory Users and Computers" in the GUI.

1-1. Verifying Local Administrators

Check whether unnecessary accounts or IDs of former employees remain in the administrator groups of individual servers.

# Get members of the local Administrators group
# *ObjectClass and PrincipalSource might be empty depending on the environment
$GroupName = "Administrators"
$Result = Get-LocalGroupMember -Group $GroupName | 
    Select-Object Name, PrincipalSource, ObjectClass, SID

# Preservation to CSV (Specify UTF8 to prevent character corruption)
$Date = Get-Date -Format "yyyyMMdd"
$Result | Export-Csv "LocalAdmins_$Date.csv" -Encoding UTF8 -NoTypeInformation

1-2. Verifying Domain Privileged IDs (Domain Admins)

Privileged groups such as "Domain Admins" can manage the entire Active Directory. These are the primary targets for attackers, and constant monitoring is required to ensure that the principle of least privilege is maintained.

In an audit, it is crucial not only to retrieve members but also to verify "whether the account is enabled (Enabled) or disabled."
(*Active Directory module is required for execution)

# Get members of the domain privileged group and retrieve detailed user information
$DomainGroup = "Domain Admins"

# Pass the result of Get-ADGroupMember to Get-ADUser to retrieve the Enabled property
$ResultDomain = Get-ADGroupMember -Identity $DomainGroup -Recursive |
    Where-Object { $_.objectClass -eq 'user' } |
    Get-ADUser -Properties Enabled |
    Select-Object Name, SamAccountName, Enabled, LastLogonDate

# Preservation to CSV
$ResultDomain | Export-Csv "DomainAdmins_$Date.csv" -Encoding UTF8 -NoTypeInformation

This allows you to create lists for both "administrators of each server" and "administrators for the entire domain," keeping them ready for immediate submission to auditors.

[Practice 2] Complete Extraction of Applied "Password Policies"

Standards such as PCI DSS v4.0 require multifaceted settings, including not only password length but also "expiration (90 days)," "history retention (4 generations)," and "lockout settings."

To prove that these are correctly applied via GPOs or other means, all active OS setting values can be exported using the secedit command.

# Export current security settings to a text file
$OutFile = "$env:TEMP\secpol.cfg"
secedit /export /cfg $OutFile /quiet

# Extract all items related to PCI DSS Requirement 8
# MinimumPasswordLength : Password length (12 or more characters recommended)
# PasswordComplexity    : Complexity, such as mixing alphanumeric characters
# MaximumPasswordAge    : Expiration (within 90 days recommended)
# PasswordHistorySize   : Password history (4 or more generations recommended)
# LockoutBadCount       : Lockout threshold (10 or fewer attempts recommended)
# ResetLockoutCount     : Lockout counter reset time
# LockoutDuration       : Lockout duration (30 minutes or more recommended)

$TargetSettings = @(
    "MinimumPasswordLength",
    "PasswordComplexity", 
    "MaximumPasswordAge", 
    "PasswordHistorySize", 
    "LockoutBadCount", 
    "ResetLockoutCount", 
    "LockoutDuration"
)

$PolicyLog = "PasswordPolicy.txt"
Get-Content $OutFile | 
    Select-String $TargetSettings | 
    Out-File $PolicyLog -Encoding UTF8

# Display results (for verification)
Get-Content $PolicyLog

With this single text file, you can answer any question from an auditor, such as "Do you meet password requirements?" or "What are the lockout settings?", using just one piece of evidence.

[Practice 3] Inventory Collection of Installed Software

Inventory collection for checking if "unnecessary tools are installed on the server." Even without opening the "Apps & Features" settings screen, you can obtain a list by referencing registry information.

Here, let's try converting the date information (YYYYMMDD string) into a human-readable format (yyyy/MM/dd) for output.

# Retrieve uninstallation information from the registry (supports both 32-bit and 64-bit)
$Paths = @(
    "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
    "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*"
)

Get-ItemProperty $Paths -ErrorAction SilentlyContinue |
    Where-Object { $_.DisplayName -ne $null } |
    Select-Object DisplayName, DisplayVersion, Publisher, @{Name='InstallDate'; Expression={
        if ($_.InstallDate -match '^\d{8}$') {
            [DateTime]::ParseExact($_.InstallDate, 'yyyyMMdd', $null).ToString('yyyy/MM/dd')
        } else { $_.InstallDate }
    }} |
    Sort-Object DisplayName |
    Export-Csv "InstalledApps_List.csv" -Encoding UTF8 -NoTypeInformation

By periodically obtaining this list and comparing it with the previous one, you can immediately identify changes such as "someone installed freeware without permission."

Operational Use: Periodic Acquisition via Task Scheduler

These scripts demonstrate their true value not only when executed individually but when registered in the Task Scheduler for "periodic execution."

  1. Output configuration information to CSV on a weekly or monthly basis
  2. Consolidate them into a specified folder on a file server
  3. Check the differences (Diff) from the "previous acquisition" using WinMerge or similar tools

Once this cycle is established, there is no need to rush to check servers during an audit.
You can complete the response simply by submitting the files in the folder, stating, "Here is last month's configuration information."

Summary

In server configuration verification, the GUI is an interface for "operation" and not for "recording."

By using PowerShell to treat settings as "data," you can eliminate dependency on specific individuals and achieve both speed and quality in audit responses.

Why not start by switching to command-based acquisition for items you check frequently, such as user lists or running services? Once you experience the convenience of understanding the status of all servers from just your local terminal, you won't want to go back to RDP.

Discussion