Use PowerShell to generate token bloat report

Today you will find out how to create token bloat report and send it to specified email address. If you are working in large scale environment you may find this useful.

Token bloat

There is often a situation when some user is complaining that he is unable to access some corporate applications. After short investigation you can find that one of the reason for that might be large number of group membership.

This script can be modified and added to task scheduler to run it once per day – and it will help you to catch those failure attempts and generate the following email report:

token bloat
token bloat

What is Token Bloat?
Token Bloat occurs when a single user is a member of too many groups in Active Directory. The default number for maximum SIDs your Active Directory access token can contain is 1024.

In previous article I described how to get total number of group membership ( Link ). This time we can use it and add results to table:

# Function for gathering user total group count  
Function Get-TotalGroupsNumber{
    [CmdletBinding()]
                
    # Parameters used in this function
    Param
    (
        [Parameter(Position=0, Mandatory = $True, HelpMessage="Provide user", ValueFromPipeline = $true)] 
        $Username
    ) 

    $dn = (Get-ADUser -Identity $username -Properties DistinguishedName ).DistinguishedName  
    $ldap = ("LDAP://" + "$dn ")
    $LDAPUser = [ADSI]"$ldap"
    $LDAPuser.psbase.refreshCache(@("TokenGroups"))
    $secirc = new-object System.Security.Principal.IdentityReferenceCollection

    ForEach($sidByte in $Ldapuser.TokenGroups)
    {
        $secirc.Add((new-object System.Security.Principal.SecurityIdentifier $sidByte,0))
    }

    $nest = ($secirc.Translate([System.Security.Principal.NTAccount])).count
    return $nest
}
# END of TotalGroups function

In this case script will gather events 6035 from ADFS servers:

Event ID: 6035
Description:
During a logon attempt, the user’s security context accumulated too many security IDs. This is a very unusual situation. Remove the user from some global or local groups to reduce the number of security IDs to incorporate into the security context.

To find them locally on the server from last 10 days (Get-Date).AddDays(-10) we can run the following command:

Get-WinEvent -FilterHashTable @{ LogName = "System"; StartTime = (Get-Date).AddDays(-10); ID = 6035 } | 
Select-Object Message,TimeCreated

For each user apart from total group membership number we can add some other properties like city, email, mobile phone etc:


$User = Get-ADUser -Identity $UserSID.trim() -properties name, city

$col1 = New-Object system.Data.DataColumn 'Server name',([string]) 
$col2 = New-Object system.Data.DataColumn 'Token Bloat issue reported time',([string])
$col3 = New-Object system.Data.DataColumn 'User ID',([string])
$col4 = New-Object system.Data.DataColumn 'Location',([string])
$col5 = New-Object system.Data.DataColumn 'Current Security Groups Count',([string])

Additionally you can find Log function in script which saves the progress information to log file or display it in host.

# Log function
Function Log{
    param
    (
        [Parameter(Mandatory=$true, HelpMessage="Log line")] $Text
    )
    
    Process{
                $line = ($Text)
                If($Log -eq $true)
                {
                    $stream = New-Object System.IO.StreamWriter($logpath, $true)
                    $stream.Write($line)
                    $stream.WriteLine()
                    $stream.Dispose()
                }
                Else
                {
                    Write-Host $line
                }
            }
} 
# END of log function

At the end you have to specify email address from whom and where this report needs to be send. Remember to run it from server where smtp is available:

    Try
    {
        # Setup email parameters
        $Date = (Get-Date).ToString('MMMM-dd')
        $mailTo = ( (Get-ADUser -Identity $ENV:Username -Properties mail).mail ) 
        $subject = "Token Bloat Report - " + $Date
        $priority = "Normal"
        $smtpServer = "smtp.powershellbros.com"
        $emailFrom = "Reports@PowerShellBros.com"
        $emailTo = $mailTo
        $port = 25 

        Send-MailMessage -To $emailTo -Subject $subject -BodyAsHtml $body -SmtpServer $smtpServer -port $port -From $emailFrom -Priority $priority

        Log( ” Sending email to $emailto ” )
    }
    Catch
    {
        Log( $_.Exception.Message )
        Continue
    } 

Below you can see the final script which you can save to .ps1 file and add it to task scheduler. Default scan is for last 10 days but you can modify this easily.

Final script:

# Function for gathering user total group count  
Function Get-TotalGroupsNumber{
    [CmdletBinding()]
                
    # Parameters used in this function
    Param
    (
        [Parameter(Position=0, Mandatory = $True, HelpMessage="Provide user", ValueFromPipeline = $true)] 
        $Username
    ) 

    $dn = (Get-ADUser -Identity $username -Properties DistinguishedName ).DistinguishedName  
    $ldap = ("LDAP://" + "$dn ")
    $LDAPUser = [ADSI]"$ldap"
    $LDAPuser.psbase.refreshCache(@("TokenGroups"))
    $secirc = new-object System.Security.Principal.IdentityReferenceCollection

    ForEach($sidByte in $Ldapuser.TokenGroups)
    {
        $secirc.Add((new-object System.Security.Principal.SecurityIdentifier $sidByte,0))
    }

    $nest = ($secirc.Translate([System.Security.Principal.NTAccount])).count
    return $nest
}
# END of TotalGroups function


# Log function
Function Log{
    param
    (
        [Parameter(Mandatory=$true, HelpMessage="Log line")] $Text
    )
    
    Process{
                $line = ($Text)
                If($Log -eq $true)
                {
                    $stream = New-Object System.IO.StreamWriter($logpath, $true)
                    $stream.Write($line)
                    $stream.WriteLine()
                    $stream.Dispose()
                }
                Else
                {
                    Write-Host $line
                }
            }
} 
# END of log function

# Parameters
$Log = $false
$logPath = "D:\Token Bloat\" + $(Get-Date -Format "yyyyMMdd_HHmmss") + '-TokenBloatReport.log'
$ServerArray = Get-Content "D:\Temp\input.txt"
$Results = @()
  
# Creating table
$tabName = “Token Bloat”
#$Cred = $Host.ui.PromptForCredential("PowerShellBros","Please use Domain and your admin account to login (ie. Domain\pawel.janowicz)","Domain\$ENV:Username","") 
 
#Create Table object
$table = New-Object system.Data.DataTable “$tabName”
 
#Define Columns
$col1 = New-Object system.Data.DataColumn 'Server name',([string]) 
$col2 = New-Object system.Data.DataColumn 'Token Bloat issue reported time',([string])
$col3 = New-Object system.Data.DataColumn 'User ID',([string])
$col4 = New-Object system.Data.DataColumn 'Location',([string])
$col5 = New-Object system.Data.DataColumn 'Current Security Groups Count',([string])
 
#Add the Columns
$table.columns.add($col1)
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)
$table.columns.add($col5)

    # Looping each server  
    ForEach ( $Server in $ServerArray ) 
    {
        Log( ” Checking $Server ” )

        Try
        {
            $Events = Invoke-Command -ComputerName $Server -ScriptBlock { Get-WinEvent -FilterHashTable @{ LogName = ”System”; StartTime = (Get-Date).AddDays(-10); ID = 6035 } | Select Message,TimeCreated } -ErrorAction Stop
        }
        Catch
        {
            Log( $_.Exception.Message )
            Continue
        }

        If($Events)
        {
            ForEach($Item in $Events)
            {
                $Time = $Item.timecreated
                [string]$UserSID = (($Item.message -split '\n')[1]).substring(14)

                Try
                {
                    $User = Get-ADUser -Identity $UserSID.trim() -properties name, city
                }
                catch
                {
                    Log( $_.Exception.Message )
                    Continue
                }
                 
                If($User)
                {
                    $Object = New-Object PSObject -Property ([ordered]@{ 
   
                        ”Server name”           = $Server
                        TimeGenerated           = $Time
                        UserName                = $User.name
                        Location                = $User.city
                        TotalGroups             = ""

                    })
    
                    $Results += $Object
                }
            }
        }
    }
  
    If(!$Results)
    {       
        Log( ” No events found ” )
    }      
    Else
    {      
        $Results = $Results | Sort-Object -Unique UserName

        ForEach($Object in $Results)
        {
            $TotalGroup = (Get-TotalGroupsNumber -Username $Object.Username)

            $row = $table.NewRow()
            $row.”Server name” = $Object.”Server name”
            $row.”Token Bloat issue reported time” = $Object.timegenerated
            $row.”User ID” = $Object.Username
            $row.”Location” = $Object.Location
            $row.”Current Security Groups Count” =  $TotalGroup 

            $table.Rows.Add($row)
        }
    }

#Creating head style 
$Head = @"
<style>
  body {
    font-family: "Arial";
    font-size: 9pt;
    }
  th, td, tr { 
    border: 1px solid #e57300;
    border-collapse: collapse;
    padding: 7px;
    text-align: center;
    }
  th {
    font-size: 12;
    text-align: center;
    background-color: #003366;
    color: #ffffff;
    }
  td {
    color: #000000;
    
    }
  .even { background-color: #ffffff; }
  .odd { background-color: #bfbfbf; }
  h6 { font-size: 11pt; 
       font-color: black;
       font-weight: bold;
       }

 text { font-size: 9pt;
        font-color: black;
        }
 }
</style>
"@

		$pre = @"
					<div class="text">
                    Hi Team,<br/>
					Please find the report for <strong>Token Bloat</strong> events.<br/>
				
                    <br/>
                    </div>
"@
 
		$post = @"
					<div class="text">
                    <br/>
                    <strong>Kind Regards</strong><br>PowerShellBros<br/>
                    <br/></div>
"@
 
 
[string]$body = [PSCustomObject]$table | Select-Object -Property ”Server name”,”Token Bloat issue reported time”,”User ID”,”Location”,”Current Security Groups Count” | Sort-Object -Property ”Current Security Groups Count” -Descending  | ConvertTo-HTML -head $head -PreContent $pre -PostContent $post  
 
# Send the report email
If($Results)
{
    Try
    {
        # Setup email parameters
        $Date = (Get-Date).ToString('MMMM-dd')
        $mailTo = ((Get-ADUser -Identity $ENV:Username -Properties mail).mail ) 
        $subject = ”Token Bloat Report - ” + $Date
        $priority = ”Normal”
        $smtpServer = ”smtp.powershellbros.com”
        $emailFrom = ”Reports@PowerShellBros.com”
        $emailTo = $mailTo
        $port = 25 

        Send-MailMessage -To $emailTo -Subject $subject -BodyAsHtml $body -SmtpServer $smtpServer -port $port -From $emailFrom -Priority $priority

        Log( ” Sending email to $emailto ” )
    }
    Catch
    {
        Log( $_.Exception.Message )
        Continue
    }   
}
Else
{
   Log( ” Script ends - no logs found on ADFS servers ” )
}
Pause

I hope that this was informative for you and see you in next articles 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *