Tuesday 11 March 2014

SharePoint 2007 \ 2010 - PowerShell script to get SharePoint audit information



This script outputs audit events as an array of objects, which can the be exported to csv using the | export-csv function:

.\get-spauditReport.ps1 -url <SiteCollection> -daysback 10 | export-csv <csv file>


It should work on SharePoint 2007 and 2010 - I haven't tested on 2013, but I think it should work.

The usage is in the comments at the start. Note that if there is heavy usage on the site collection, be mindful of the number of days the report should include, as memory may be an issue.


Script: get-spauditReport.ps1

# Script to output SharePoint audit information - can be exported to csv.
# Parameters:
# -url : URL of site collection
# -daysback : Number of days back from today that the audit information is to be collected from
# -startdate : As opposed to -daysback, use this as a start date for the audit report
# -enddate : Used with -startdate for the end date of the report
# -previousmonth : [switch] Previous full month for the report to use. Good if you want to run as a scheduled task every month
# -excludeusers : Can be an array of users that you don't want to appear on the report - i.e. monitoring accounts etc
# -excludeurls : excluded urls from the report - some system urls excluded by default, but will be overridden if used
# -includeRawevent : [switch] Included the raw event data from the SharePoitn audit
# -filterevent : Only returns events stated in the param
# -principle : If stated, only events for this account will be return.


param ($url, $daysback, $startdate, $enddate, [switch]$previousmonth, $excludeusers = ("SHAREPOINT\system"), $excludeurls = ("/Style Library/portal","/_catalogs","/_themes") , [switch]$includeRawEvent, $filterEvent, $principal)


[void][System.reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
[void][System.reflection.Assembly]::LoadWithPartialName("System")

# Funcions

# Function: To create new object for results output
function New-AuditObj(){

    $auditobj = new-object PSObject
    $auditobj | add-member -type NoteProperty -name LoginName -Value "" 
    $auditobj | add-member -type NoteProperty -name DisplayName -Value "" 
    $auditobj | add-member -type NoteProperty -name Email -Value "" 
    $auditobj | add-member -type NoteProperty -name SPUserID -Value "" 
    $auditobj | add-member -type NoteProperty -name Event -Value "" 
    $auditobj | add-member -type NoteProperty -name Item -Value "" 
    $auditobj | add-member -type NoteProperty -name ItemType -Value "" 
    $auditobj | add-member -type NoteProperty -name Version -Value "" 
    $auditobj | add-member -type NoteProperty -name EventData -Value ""
    $auditobj | add-member -type NoteProperty -name EventDataRaw -Value ""     
    $auditobj | add-member -type NoteProperty -name AuditTime -Value "" 

    return $auditobj
}

# Function: Return Values based on Permission Level Mask
function get-PermissionLevelMask($maskno)
{
    switch ($maskno)
    {
        "0" {
            Return "EmptyMask"
        }       
        "1" {
            Return "ViewListItems"
        }       
        "2" {
            Return "AddListItems"
        }       
        "4" {
            Return "EditListItems"
        }       
        "8" {
            Return "DeleteListItems"
        }       
        "16" {
            Return "ApproveItems"
        }       
        "32" {
            Return "OpenItems"
        }       
        "64" {
            Return "ViewVersions"
        }       
        "128" {
            Return "DeleteVersions"
        }       
        "256" {
            Return "CancelCheckout"
        }       
        "512" {
            Return "ManagePersonalViews"
        }       
        "2048" {
            Return "ManageLists"
        }       
        "4096" {
            Return "ViewFormPages"
        }       
        "65536" {
            Return "Open"
        }       
        "131072" {
            Return "ViewPages"
        }       
        "262144" {
            Return "AddAndCustomizePages"
        }       
        "524288" {
            Return "ApplyThemeAndBorder"
        }       
        "1048576" {
            Return "ApplyStyleSheets"
        }       
        "2097152" {
            Return "ViewUsageData"
        }       
        "4194304" {
            Return "CreateSSCSite"
        }       
        "8388608" {
            Return "ManageSubwebs"
        }       
        "16777216" {
            Return "CreateGroups"
        }       
        "33554432" {
            Return "ManagePermissions"
        }       
        "67108864" {
            Return "BrowseDirectories"
        }       
        "134217728" {
            Return "BrowseUserInfo"
        }       
        "268435456" {
            Return "AddDelPrivateWebParts"
        }       
        "536870912" {
            Return "UpdatePersonalWebParts"
        }       
        "1073741824" {
            Return "ManageWeb"
        }       
        "68719476736" {   
            Return "UseClientIntegration "
        }
        "137438953472" {
            Return "UseRemoteAPIs"
        }       
        "274877906944" {
            Return "ManageAlerts"
        }       
        "549755813888" {
            Return "CreateAlerts"
        }       
        "1099511627776" {
            Return "EditMyUserInfo"
        }       
        "4611686018427387904" {
            Return "EnumeratePermissions"
        }       
        "9223372036854775807" {
            Return "FullMask"
        }       
   
   
   
    }

}

# Function: Return Values based on audit level mask
function get-auditMask($maskno){
    switch ($maskno)
        {
            "-1" {
                #Return "All types of events and actions."
                Return "All"
            }
            "0" {
                #Return "No events or actions."
                Return "None"
            }
            "1" {
                #Return "Check out of the object."
                Return "CheckOut"
            }
            "2" {
                #Return "Check in of the object."
                Return "CheckIn"
            }
            "4" {
                #Return "Viewing of the object by a user."
                Return "View"
            }
            "8" {
                #Return "Deletion of the object."
                Return "Delete"
            }
            "16" {
                #Return "Update of the object."
                Return "Update"
            }
            "32" {
                #Return "Change of a profile."
                Return "ProfileChange"
            }
            "64" {
                #Return "Deletion of one of the object's child objects."
                Return "ChildDelete"
            }
            "128" {
                #Return "Change of the object's schema."
                Return "SchemaChange"
            }
            "256" {
                #Return "Change in a security configuration for the object."
                Return "SecurityChange"
            }
            "512" {
                #Return "Reversal of the deletion of the object."
                Return "Undelete"
            }
            "1024" {
                #Return "Use of the object in a workflow task."
                Return "Workflow"
            }
            "2048" {
                #Return "Copying the object."
                Return "Copy"
            }
            "4096" {
                #Return "Move of the object."
                Return "Move"
            }
            "8192" {
                #Return "Search of the object."
                Return "Search"
            }
                                                                                                                                                                                              
       
       
        }
       

}

## Start a loop that will run until both $sd (Start Date) and $ed (End Date) variables are assigned values
while (-not ($sd -and $ed))
{
    ## If the -daysback parameter was specified, set $ed to current timestamp and $sd to the specified number of days back
    if ($daysback)
    {
        ## The ToShortDateString() method is used to set the time value in the DateTime object to 00:00:00 (otherwise it will match current)
        $sd = (Get-Date (Get-Date).ToShortDateString()).AddDays(-$daysback);
        $ed = Get-Date;
       
        ## Stop processing the loop as the required values have been assigned
        break;
    }
   
    if ($previousmonth)
    {
        # get previous month days
        $date = get-date
        $numdays = $date.Day
        $ed = $date.AddDays(-$numdays)
        $numdays = $ed.Day
        $sd = $ed.AddDays( -$numdays + 1)
        $sd = ((($sd.AddHours(0-$sd.hour)).AddMinutes(0-$sd.Minute)).AddSeconds(0-$sd.Second)).AddMilliseconds(0-$sd.millisecond)
        $ed = $ed.AddDays(1)
        $ed = ((($ed.AddHours(0-$ed.hour)).AddMinutes(0-$ed.Minute)).AddSeconds(0-$ed.Second)).AddMilliseconds(0-$ed.millisecond)
        ## Stop processing the loop as the required values have been assigned
        break;
    }
   
    ## If the -startdate parameter was specified, use it create a DateTime object and assign it to $sd variable
    if ($startdate)
    {
        $sd = Get-Date ($startdate);
    }
   
    ## As start date was not specified, set $sd to the earliest date possible
    else
    {
        $sd = [DateTime]::MinValue;
    }

    ## If the -enddate parameter was specified, use it create a DateTime object and assign it to $ed variable
    if ($enddate)
    {
        $ed = Get-Date ($enddate);
    }
   
    ## As end date was not specified, set $ed to today
    else
    {
        $ed = Get-Date;
    }
}

# Set type of start date and end date
$sd = Get-Date $sd
$ed = Get-Date $ed

# Get Site object
$site = New-Object Microsoft.SharePoint.SPSite($url)

# Get List of site users
$SPSiteUsers = $site.RootWeb.SiteUsers
# Get Site User Ids of excluded users (as defined in parameter)
$ExcludeUserIds = $SPSiteUsers | where-Object {$excludeusers -contains $_.LoginName} | foreach {$_.ID}
# Create gerneal query object
$query = New-Object Microsoft.SharePoint.SPAuditQuery($site)
$query.SetRangeStart($sd)
$query.SetRangeEnd($ed)

# Filter our events if defined in parametes
if ($filterEvent){
    Write-Host "  - Filtering Events:"
    $filterEvent | ForEach-Object {
        Write-Host "    - $_"
        $query.AddEventRestriction($_)
    }
}

# Get Prinicpal ID (from parameter) and set query to use it
if ($principal){
    $id = ($SPSiteUsers | Where-Object {$_.loginname -ilike "*$principal*"}).id
    if ($id){
        $query.RestrictToUser($id)
    }
    else{
        Write-Host "   - $principal does not exist on the site."
    }
}
# Get Audit Entries, excluding urls (as specified in parameters)
$auditEntries = $site.Audit.GetEntries($query) | Where-Object {$ExcludeUserIds -notcontains $_.userID } | ForEach-Object {$found = $false; if ($excludeurls){foreach ($exurl in $excludeurls){if($_.Doclocation -ilike "*$exurl*"){$found = $true}}; if ($found -eq $false){$_}}}
# reset Audit counter (for progress bar)
$auditcount = 0;

# Preloading objects from SharePoint - for efficency

# Get SecGroupCreate audit entries
$groupquery = New-Object Microsoft.SharePoint.SPAuditQuery($site)
$groupquery.AddEventRestriction("SecGroupCreate")
$SecGroupCreateEntries = $site.Audit.GetEntries($groupquery)

# Get SecRoleDefCreate and SecRoleDefModify audit entries
$rolequery = New-Object Microsoft.SharePoint.SPAuditQuery($site)
$rolequery.AddEventRestriction("SecRoleDefCreate")
$rolequery.AddEventRestriction("SecRoleDefModify")               
$SecRoleEntries = $site.Audit.GetEntries($rolequery)

# Loop through all audit entries, so we can process them
foreach ($AuditItem in $auditEntries){
    # Reset Principal variable
    $principal = $null
    # Increment counter
    $auditcount ++;
    # Progress bar
    if ($auditEntries.Count -gt 0){
        Write-Progress -activity "Processing Audit Logs for $url" -status "Processed: $auditCount of $($auditEntries.Count)" -PercentComplete (($auditcount / $auditEntries.Count)  * 100)
    }
    # Create new Audit output object
    $AuditObj = New-AuditObj
   
    # Get User Info
    try{
        $user = $SPSiteUsers.GetByID($audititem.UserId)
        $auditobj.LoginName = $user.LoginName
        $auditobj.DisplayName = $user.Name
        $auditobj.Email = $user.Email
        $auditobj.SPUserID = $user.ID
    }
    catch{
        $auditobj.LoginName = "User Removed"
        $auditobj.SPUserID = $audititem.UserId
    }
   
    # Get Item Info

    $auditobj.Event = $auditItem.Event
    $auditobj.Item = $auditItem.DocLocation
    $auditobj.ItemType = $auditItem.ItemType
    $auditobj.AuditTime = $auditItem.Occurred
    $auditobj.EventDataRaw = $AuditItem.EventData   
   
    if($auditobj.Item -ilike "*agenda.doc*")
    {
        Write-Host "Here"
    }
   
    if ((($auditItem.DocLocation).EndsWith(".aspx"))){
        $auditobj.ItemType = "Page"
    }
    # Calculate version info
    if ($AuditItem.EventData -eq ""){
        $auditobj.Version = "N/A"
    }
    if ($AuditItem.EventData -ilike "*<allversions/>*"){
        $auditobj.Version = "All"
    }   
    try {
        [xml]$eventData = "<root>$($AuditItem.EventData)</root>"
        if ($eventData.root.Version.Major -ne $null -and $eventData.Version.root.Minor -ne ""){
            if ($eventData.root.Version.Major -eq "0" -and $eventData.Version.root.Minor -eq "-1")
            {
                $auditobj.Version = "N/A"
            }
            else{
                $auditobj.Version = $eventData.root.Version.Major + "." + $eventData.root.Version.Minor
            }
        }
       
    }
    catch{}

    # Reset general EventData variable
    Remove-Variable eventData
    # Switch based on event type
    switch ($auditItem.Event)
        {
            "AuditMaskChange" {
                # Get mask from EventData
                $permissionMask = ([xml]($AuditItem.EventData)).NewAuditMask
                $auditobj.EventData = "Auditing Events: "
                # Go through each mask and bit and them - evaluation passed to "Get-AuditMask" function for actual values
                8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1 | foreach {if (($permissionMask -band $_) -gt 0){$auditobj.EventData += $(get-auditMask ($permissionMask -band $_)) + " "}}

            }
            "Move" {
                [xml]$eventData = $AuditItem.EventData
                $auditobj.EventData = "Moved to: {0}" -f $EventData.NewName
            }

            "ChildMove" {
                [xml]$eventData = $AuditItem.EventData
                $auditobj.EventData = "Moved to: {0}" -f $EventData.RelatedItem.NewName
            }            
            "Delete" {
           
                if ($($AuditItem.EventData) -ilike "*<Recycle>1<Recycle>*")           
                    {$auditobj.EventData = "Sent to the recycle bin"}
                if ($($AuditItem.EventData) -ilike "*<Recycle>0<Recycle>*")           
                    {$auditobj.EventData = "Permantently Deleted"}              
                   
                }    
            "UnDelete" {
                $auditobj.EventData = "Restored from the recycle bin"
                }                         
            "ChildDelete" {
                $auditobj.EventData = "Deleted {0}" -f $($eventData.RelatedItem.Location)
            }
            "EventsDeleted" {
                [xml]$eventData = $AuditItem.EventData              
                $auditobj.EventData = "{0} audit rows deleted before {1}" -f $eventdata.DeleteEntriesInfo.Rows, $(get-date ($eventdata.DeleteEntriesInfo.EndDate)).TosTring()
            }
            "View" {
                $auditobj.EventData = ""
            }
            "Search" {
                $eventData = $AuditItem.EventData
                $query = ($eventData.split(";")[0]).replace("query=","")
                $context = ($eventData.split(";")[1]).replace("constraint=' ","").replace("'","").replace("site:","").replace('"',"")
                $auditobj.EventData = "Searched for {0} in {1}" -f $query, $context

            }
                                        
            "SecGroupMemberAdd" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                $GroupID = $eventData.root.groupid
                if ($GroupID -gt 0){
                    try {$GroupName = (($site.RootWeb.SiteGroups).GetByID($eventData.root.groupid)).Name}catch{}
                    if (!$groupName){
                        $SecGroupCreateEntries  | Where-Object {$_.EventData -ilike "*<groupid>$GroupID</groupid>*"} | Select -First 1 | foreach {[xml]$eventdata = ("<root>$($_.EventData)</root>").replace("&","&amp;"); $GroupName =  $eventData.root.title}

                    }
                    try {$principal = $SPSiteUsers.GetById($eventdata.root.user)}catch{}
                    if ($principal -ne $null){
                        $userinfo = "{0} ({1})" -f $principal.loginname, $principal.email
                    }
                    else
                    {
                        $userinfo = $eventdata.root.username
                    }
                    $auditobj.EventData = "Added {0} to Group: {1}" -f $userinfo, $GroupName
                }
            }                
           
            "SecGroupMemberDel" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                try {$principal = $SPSiteUsers.GetById($eventdata.root.user)}catch{}
                if ($principal -ne $null){
                    $userinfo = "{0} ({1})" -f $principal.loginname, $principal.email
                }
                else{
                    $userinfo = "UserID: $($eventdata.root.user)"
                }  
                try {$GroupName = (($site.RootWeb.SiteGroups).GetByID($eventData.root.groupid)).Name}catch{}
                if (!$groupName){
                    $SecGroupCreateEntries  | Where-Object {$_.EventData -ilike "*<groupid>$GroupID</groupid>*"} | Select -First 1 | foreach {[xml]$eventdata = ("<root>$($_.EventData)</root>").replace("&","&amp;"); $GroupName =  $eventData.root.title}

                    }                           
                $auditobj.EventData = "Removed {0} from Group: {1}" -f $userinfo, $GroupName
            }               

            "SecGroupCreate" {
                [xml]$eventdata = ("<root>$($AuditItem.EventData)</root>").replace("&","&amp;")
                $auditobj.EventData = "Group Created: {0}" -f $eventData.root.title
            }             

            "SecGroupDelete" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                $SecGroupCreateEntries | Where-Object {$_.EventData -ilike "*<groupid>$($eventData.root.groupid)</groupid>*"} | Select -First 1 | foreach {[xml]$eventdata = ("<root>$($_.EventData)</root>").replace("&","&amp;"); $auditobj.EventData = "Group Deleted: {0}" -f $eventData.root.title}
            }  
           
            "SecRoleBindUpdate" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                try {$principal = $SPSiteUsers.GetById($eventdata.root.principalid)}catch{}
                if ($principal){
                    $principalInfo = "{0} ({1})" -f $principal.loginname, $principal.email
                }
                else{
                    # The Principle may be a group
                    try{$principal = (($site.RootWeb.SiteGroups).GetById($eventData.root.principalid)).Name}catch{}
                     $principalInfo = "$principal"
                }

                if ($eventdata.root.roleid -eq -1){
                    $permission = "All"
                }
                elseif ($eventdata.root.roleid -eq "0")
                {
                    $permission = "None"
                }
                else{
                    $permission = ($site.RootWeb.RoleDefinitions | Where-Object {$_.id -eq $eventData.root.roleid}).name
                }
                $operation = ($eventdata.root.operation).replace("ensure ","")
                $auditobj.EventData = "'{0}' permission {1} : {2}" -f $permission, $operation, $principalInfo
            }
            "SecRoleBindInherit" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                $auditobj.EventData = "Item Url: {0}" -f $($eventdata.root.url)
            }                    
            "SecRoleBindBreakInherit" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                $auditobj.EventData = "Item Url: {0}" -f $($eventdata.root.url)
            }
            "SecRoleDefCreate" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                $permissionMask = $($eventdata.root.perm)
                $auditobj.EventData = "{0}: " -f $($eventdata.root.name)
                1,2,4,8,16,32,64,128,256,512,2048,4096,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,68719476736,137438953472,274877906944,549755813888,1099511627776,4611686018427387904,9223372036854775807`
                 | foreach {if (($permissionMask -band $_) -gt 0){$auditobj.EventData += $(get-PermissionLevelMask ($permissionMask -band $_)) + " "}}
            }            
            "SecRoleDefModify" {
                [xml]$eventdata = "<root>$($AuditItem.EventData)</root>"
                $permissionMask = $($eventdata.root.perm)
                $auditobj.EventData = "{0}: " -f $($eventdata.root.name)
                1,2,4,8,16,32,64,128,256,512,2048,4096,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,68719476736,137438953472,274877906944,549755813888,1099511627776,4611686018427387904,9223372036854775807`
                 | foreach {if (($permissionMask -band $_) -gt 0){$auditobj.EventData += $(get-PermissionLevelMask ($permissionMask -band $_)) + " "}}
            }   
            "SecRoleDefDelete" {
                [xml]$eventdata = $AuditItem.EventData
                $SecRoleEntries| Where-Object {$_.EventData -ilike "*<id>$($eventData.id)</id>*"} | Select -First 1 | foreach {[xml]$eventdata = "<root>$($_.EventData)</root>"; $auditobj.EventData = "Permission Level Deleted: {0}" -f $eventData.root.name}
            }                                                                                                                                                                                       
            default {}
        }


    if (-not $includeRawEvent){
        $auditobj | Select-Object * -exclude EventDataRaw
    }
    else{
        $auditobj
    }

}

# End Script

1 comment:

  1. Not working in SharePoint 2013. Error At Line 287 "new-object". Have tried using get-spsite too, same error.

    ReplyDelete