TenBrink Tech Technology | Life

3Jan/09

PowerShelling Audit Reports

Part of working in corporate IT is the necessity to create and demonstrate compliance with general computing controls. In my environment part of those controls consist of creating exports of the membership of certain Active Directory groups. Since I am a fan of automation I put together some PowerShell scripts to speed up this task but it also comes in handy for those one-off “Who is in such-and-such group?” requests.

First, you’ll need two requirements.

  • PowerShell 2.0 CTP (I use the convertto-csv cmdlet that does not exist in PSv1. I suppose I could write a routine to do the conversion – but why when 2.0 is coming along nicely?)
  • Quest ActiveRoles AD Cmdlets (Once again, I could write the whole thing using System.DirectoryServices, but why when the heavy lifting has been done for you?)

With that out of the way, the routine consists of three different scripts. First, the workhorse of the routine, Get-RecursiveGroupMembership.ps1:

param (
    [string] $distinguishedname,
    [bool] $addOtherTypes = $false
    )
$members = @()

$this = (Get-QADGroup $distinguishedname).member | Get-QADObject
$this | foreach {
    if ($_.type -eq 'user') {
        $members += $_
        }
    elseif ($_.type -eq 'group') {
        Write-Host "Adding sub group $_"
        $members += .\Get-RecursiveGroupMembership.ps1 $_.dn $addOtherTypes
        }
    else {
        if ($addOtherTypes -eq $true) {
            $members += $_
            }
        else {
            Write-Host "Non user/group member detected. Not added. Use -addOtherTypes flag to add."
        }
    }
}
return $members

This script is very handy for a lot of things and gives me more power for recursive membership listing than the Get-QADGroupMember cmdlet alone. The other thing that the Get-QADGroupMember cmdlet falls short on is enumerating membership when the group is in another domain – a common occurrence in my environment – hence the use of QADObject and then reading the members attribute.

You will also see the switch. By default the script returns only user objects. You can throw the second parameter (the $addOtherTypes) to true to get all object types.

Future functionality for this script could include a parameter for specifying types to return.

On to the second script, Audit-QuickGroup.ps1, which allows me to pass the distinguishedName of a group in any domain and have the results of that group membership written to a csv file of the same name. This script relies on the first.

param ([string] $name)
$csvdata = .\Get-RecursiveGroupMembership.ps1 $name | select name,type,dn,title,office,description | convertto-csv -NoTypeInformation
$filename = $name + ".csv"
[String]$reportdate = "Report Generated: " + [datetime]::Now
$f = new-item -itemtype file $filename
add-content $f "Audit Report - Active Directory Group - $name"
add-content $f $reportdate
add-content $f $csvdata

This script is pretty simple, taking the output, selecting my most commonly requested attributes, and writing it to a csv file with some header information. Future functionality may include the ability to specify an output filename (optional) and including the header or not.

Lastly, for bulk operations, I needed a way to use the filtering abilities of the Quest Get-QADGroup cmdlet to do the export of multiple groups, written to multiple files with one command. Sure, I could have used a little “Get-QADGroup blah –filter moreblah | foreach { loop previous script }”, but why do that when I can do this in my Audit-MultipleGroups.ps1 script?

# take group input
param ([string] $GroupInput)

#get groups
$GroupList = get-qadgroup $groupinput

# iterate through groups, creating output
foreach ($Group in $GroupList) {
    Write-Host $group.dn
    $GroupMembers = .\Get-RecursiveGroupMembership.ps1 $group.DN | select name,type,dn,title,office,description | convertto-csv -NoTypeInformation
    #now create file
    $filename = $Group.Name + ".csv"
    [String]$reportdate = "Report Generated: " + [datetime]::Now
    $file = New-Item -ItemType file $filename -Force
    Add-Content $file "Audit Report - Active Directory Group Membership"
    Add-Content $file $reportDate
    Add-Content $file $groupMembers
    }

With the swipe of one command, like:

PS> .\Audit-MultopleGroups.ps1 SEC_ADM_*

Provided you have a good Active Directory group naming policy this can output all of the groups matching that prefix into their own csv files with one little command.

So there you go. A very useful base script, a more customer-focused output script, and a purpose-built script all reducing the amount of work necessary to report on group membership in Active Directory. There is one last scenario to deal with in my environment, local computer group membership. The script below will take the computer name and group name as parameters and output the members into a text file. I’ve called it Audit-LocalGroupMembership.ps1.

#local group member enumeration
param (
    [string] $Server,
    [string] $GroupName
)
$MemberNames = @()
$Group= [ADSI]"WinNT://$Server/$GroupName,group"
$Members = @($Group.psbase.Invoke("Members"))
$Members | ForEach-Object {$MemberNames += $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}

#file routine
$filename = $server + " " + $GroupName + ".txt"
$f = New-Item -ItemType file $filename
[string]$reportdate = "Date: " + (Get-Date).tostring('yyyyMMdd')
Add-Content $f "Local Group Membership Report"
Add-Content $f "Server: $server"
Add-Content $f "Group : $groupname"
Add-Content $f $reportdate
Add-Content $f $membernames
return $membernames

Happy New Year and happy PowerShelling!

  • Share/Bookmark