Tag Archives: Start-Transcript

How To: Powershell Get Local Administrators and Active Directory Nested Groups – SOX

For the past few years, I’ve been tasked with ensuring that our in-scope servers for Sarbanes-Oxley (SOX) have the correct users and groups in them. Before we would have to screen shot the members of the local admins and all the groups that are in the local admins. I decided to take the time to create a script that gets the local admins of the server and write the output to a transcript file.

Script to get Server Local Administrators:

I named the script Get-ServerLocalAdministrators:

Get-ServerLocalAdministrators.ps1 -domain domain -computername computername

The steps the PowerShell script goes through are:

  • Get domain and server
  • Import PowerShell Modules
  • Get Date and Time
  • Get the working directory
  • Convert the domain and computername to upper and lower for pattern matches
  • Start the transcript in the current directory and use some variables for naming the transcript
  • Get the server’s local administrators object using Get-WmiObject and a where cause for the group component
  • Get the members of the local admins and determine if it’s a user or group.
  • If user, put it in the transcript and tag the user as uer
  • If Group, put the group in a variable to loop through later
  • In the transcript, I’m doing a Write-Host to tag each group. If the group has a sub group, (nested group), then put that in another variable to loop through that as well

Since we have Group Memebers that are cross domain, I had to include the -Server paramater get the object’s distinguished Name and use that for -Server.
The only line you have to change in this script (because I was lazy) is to change line 32 to your domain. So, if your server FQDN is server.subdomain.domain.com you would call this “Domain.com” if it was simply “server.domain.com” then you would call this “com”.

[CmdletBinding()]
Param(
    [Parameter(Mandatory=$True,Position=1)]
        [string]$domain,
    [Parameter(Mandatory=$True,Position=2)]
        [string]$computername
    )

# Importing Modules
if (! @(get-module -name ActiveDirectory).count) 
{
	import-module ActiveDirectory
}

if (! @(get-module -name Microsoft.Powershell.Host).count) 
{
	import-module Microsoft.Powershell.Host
}

if (! @(get-module -name Microsoft.WSMan.Management).count) 
{
	import-module Microsoft.WSMan.Management
}

# Variables
$TimeStamp = get-date -Format yyyyMMddHHmmss
$workingDirectory = (Resolve-Path .\).Path
$ComputerNameLower = $computername.ToLower()
$DomainLower = $domain.ToLower()
$ComputerNameUpper = $computername.ToUpper()
$DomainUpper = $domain.ToUpper()
$parentDomain = "domain.com"
[string]$strcomputer = $computername

Start-Transcript -Path $workingDirectory\AREA6_$DomainUpper.$ComputerNameUpper.$TimeStamp.txt -force

# Getting the Local Admins Group
$admins = Get-WmiObject win32_groupuser –computer "$computername.$domain.$parentDomain"
$admins = $admins | where {$_.groupcomponent –like '*"Administrators"'} 
[array]$DomainGroups = @()
[array]$IsGroup = @()

Write-Host "----------------------------------------------------------------"
Write-Host $computername "Local Administrators"
Write-Host "----------------------------------------------------------------"

# Getting Members of Local Admins
$admins | ForEach-Object {  
    $_.partcomponent –match "Win32_(.+).+Domain\=(.+)\,Name\=(.+)$" > $nul  
    $matches[2].trim('"') + "\" + $matches[3].trim('"') + " Type: " + $matches[1]
    # Finding if Local Administrator member is a Domain Group and adding it to $DomainGroups
    $String = 'Win32_Group.Domain="'+$DomainUpper+'"'
    if ($matches[0].Contains($string)){
        $DomainGroups += ($matches[3].trim('"'))
        }
    }

# Looping through all the Domain Groups in the Local Admins and getting their members
foreach ($DomainGroup in $DomainGroups){
    Write-Host "----------------------------------------------------------------"
    Write-Host "$DomainGroup"
    Write-Host "----------------------------------------------------------------"
    $Members = Get-ADGroupMember $DomainGroup -server "$DomainLower.$parentDomain"
    # If a group member is another group write it to $IsGroup so we can get them later
    foreach ($member in $members){
        If ($member.objectclass -eq "user"){
            Write-host ($member.name)" Type:"($member.objectClass)
            }
        elseif ($member.objectclass -eq "group" -And $IsGroup -notcontains $member){
            Write-Host ($member.name)" Type:"($member.objectClass)
            $IsGroup += $member
            }
        elseif($member.objectclass -eq "group" -And $IsGroup -contains $member){
            }
        }
    }
# As stated above, getting the members of nested groups
foreach ($item in $IsGroup){
    Write-Host "----------------------------------------------------------------"
    Write-Host $item.name
    Write-Host "----------------------------------------------------------------"
    $DN = $item.distinguishedName
    $objectDomain = ((($DN -replace "(.*?)DC=(.*)",'$2') -replace "DC=","") -replace ",",".")
    $members = Get-ADGroupMember $item -server $objectDomain
    foreach ($member in $members){
        write-host $member.name" Type:"$member.objectClass
        }
    }

Stop-Transcript

Limitations: This script only loops through to levels of Active Directory groups on the server. So, if you have nested groups more than that, it won’t continue deeper. I’m going to rewrite this to do full recursion in a function so you can look for that post in the future.