Generate a Group Membership Report in HTML Format Using PowerShell

<# .SYNOPSIS Generates a report in HTML format of all a user's group memberships. .DESCRIPTION Generates a report in HTML format of all a user's group memberships. The groups are separated into categories based on the group's prefix. Groups with no prefix are Role Groups. Groups with the ACL_ prefix are access control groups. Groups with the GPO_ prefix control deployment of Group Policy. Groups with the APP_ prefix control deployment of an application. Groups with the VDI_ prefix control access to a specific virtual desktop pool. .NOTES Author : Brian Reich <breich@reich-consulting.net .LINK http://wp.me/p2A8jT-1np #> # Imports the Get-ADPrincipalGroupMembershipRecursive # See https://www.reich-consulting.net/2013/12/05/retrieving-recursive-group-memberships-powershell/ Import-Module GroupManagement\Get-ADPrincipalGroupMembershipRecursive # Ask the user to enter a username $user = Get-ADUser -Identity (Read-Host "Username") # Get the user's group memberships $groups = Get-ADPrincipalGroupMembershipRecursive -dsn $user.DistinguishedName # Split groups based on prefix $acls = $groups | Where-Object { $_.Name -like "ACL_*" } $gpos = $groups | Where-Object { $_.Name -like "GPO_*" } $apps = $groups | Where-Object { $_.Name -like "APP_*" } $vdis = $groups | Where-Object { $_.Name -like "VDI_*" } # Role groups are the groups that didn't fit the other criteria $roles = $groups | Where-Object { $_ -notin $acls -and $_ -notin $gpos -and $_ -notin $apps -and $_ -notin $vdis } <# .SYNOPSIS Returns an HTML fragment listing all of the user's Role Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's Role Groups. Roles Groups are displays with the name of the group and the group's description. .PARAMETER $roles An array of Role Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-RoleHtml( $roles ) { $html = "<h2>Roles</h2>" $html += "<p>Role groups represent the user's job position and " $html += "responsibilities within the organization.</p>" $html += $roles | Select-Object -Property Name,Description | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's ACL Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's ACL Groups. ACL Groups are named ACL_Resource Name_Permission. The name of the group is displayed alongside the Resource it controls access to and the permission it provides. .PARAMETER $acls An array of ACL Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-AclHtml( $acls ) { $html = "<h2>Access Control Groups</h2>" $html += "<p>Access Control Groups (groups whose name begins with 'ACL_') are " $html += "groups that control access rights to resources.</p>" $html += $acls | Select-Object -Property ` Name, @{Name="Resource"; Expression = {$_.Name.split("_")[1]}}, @{Name="Permission"; Expression = {$_.Name.split("_")[2]}} | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's GPO Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's GPO Groups. GPO Groups are displays with the name of the group and the name of the GPO it controls. .PARAMETER $roles An array of GPO groups. .RETURNS Returns a string containing the HTML fragment. #> function Get-GpoHtml( $gpos ) { $html = "<h2>Group Policy Groups</h2>" $html += "<p>Group Policy Groups (those that start with 'GPO_') are groups " $html += "that control Group Policy assignments.</p>" $html += $gpos | Select-Object -Property ` Name, @{Name="Policy Name"; Expression = {$_.Name.split("_")[1]}} | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's App Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's App Groups. App Groups are displays with the name of the group alongside the Application that it controls access to. .PARAMETER $roles An array of App Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-AppHtml( $apps ) { $html = "<h2>Application Groups</h2>" $html += "<p>Application Groups (those that start with 'APP_') are groups " $html += "that control deployment of specific applications. Applications may " $html += "be deployed by Group Policy, ThinApp, or other means.</p>" $html += $apps | Select-Object -Property ` Name, @{Name="Application"; Expression = {$_.Name.split("_")[1]}} | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } <# .SYNOPSIS Returns an HTML fragment listing all of the user's VDI Groups. .DESCRIPTION Returns an HTML fragment listing all of the user's VDI Groups. VDI Groups are displays with the name of the group alongside the name of the VDI pool they control access to. .PARAMETER $roles An array of VDI Groups .RETURNS Returns a string containing the HTML fragment. #> function Get-VdiHtml( $vdis ) { $html = "<h2>VDI Groups</h2>" $html += "<p>VDI Groups (those that start with 'VDI_') are groups that " $html += "that assign users to virtual desktop pools.</p>" $html += $vdis | Select-Object -Property Name,Description | Sort-Object -Property Name | ConvertTo-Html -Fragment return $html } # Get the account name, which we'll use to name the HTML File. $username = $user.SamAccountName $file = $HOME + $username + "_group_memberships.html" #The following lines generate HTML. We're generating some pretty simple markup #which contains a stylesheet to make the whole thing look decent on-screen. $html = '<html>' $html += "<head><title>RBAC Report for $username</title>" $html += "<style>" $html += " body { width: 800px; margin: 1em auto; font-family: 'Proxima Nova Regular', 'Helvetica Neue', Calibri, 'Droid Sans', Helvetica, Arial, sans-serif; font-size: 16px; font-style: normal; font-weight: normal; } h1, h2, h3, h4, h5, h6, p { border-radius: 5px; padding: 10px 20px; } h1 { background: #333; color: #f0f0f0; } h2 { background: #666; color: #f0f0f0; } p { background: #eee; color: #333 } table th { text-align: left; background: eee }" $html += "</style>" $html += "</head>" $html += '<body>'; $html += "<h1>RBAC Report for $username</h1>" $html += "<p>This report provides a list of all $username's group memberships," $html += "including all of those inherited from other groups. Groups are " $html += "organized based on their prefix. Groups with no prefix are assumed to" $html += "be Role Groups.</p>" $html += Get-RoleHtml $roles $html += Get-AclHtml $acls $html += Get-GpoHtml $gpos $html += Get-AppHtml $apps $html += Get-VdiHtml $vdis # Output the HTML to the file $HOME\$username_group_memberships.html $html | Out-File "$file" # Open the HTML file Invoke-Expression "$file"

Get All of a User’s Group Memberships with PowerShell

<# .SYNOPSIS Gets all the Active Directory groups that have a specified user, computer, group, or service account. .DESCRIPTION Gets all the Active Directory groups that have a specified user, computer, group, or service account. Unlike the built-in Get-ADPrincipalGroupMembership cmtlet, the function I've provided below will perform a recursive search that will return all of the groups that the account is a member of through membership inheritance. This function required the Active Directory module and thus must be run on a domain controller or workstation with Remote Server Administration Tools. .PARAMETER dsn The distinguished name (dsn) of the user, computer, group, or service account. .PARAMETER groups An array of ADObject instances for each group in which the user, computer, group, or service account is a member. This parameter can be ignored and in fact should never be specified by the caller. The groups parameter is used internally to track groups that have already been added to the list during recursive function calls. .NOTES Author : Brian Reich <breich@reich-consulting.net .LINK http://www.reich-consulting.net #> function Get-ADPrincipalGroupMembershipRecursive( ) { Param( [string] $dsn, [array]$groups = @() ) # Get an ADObject for the account and retrieve memberOf attribute. $obj = Get-ADObject $dsn -Properties memberOf # Iterate through each of the groups in the memberOf attribute. foreach( $groupDsn in $obj.memberOf ) { # Get an ADObject for the current group. $tmpGrp = Get-ADObject $groupDsn -Properties memberOf # Check if the group is already in $groups. if( ($groups | where { $_.DistinguishedName -eq $groupDsn }).Count -eq 0 ) { $groups += $tmpGrp # Go a little deeper by searching this group for more groups. $groups = Get-ADPrincipalGroupMembershipRecursive $groupDsn $groups } } return $groups } # Simple Example of how to use the function $username = Read-Host -Prompt "Enter a username" $groups = Get-ADPrincipalGroupMembershipRecursive (Get-ADUser $username).DistinguishedName $groups | Sort-Object -Property name | Format-Table

Create an Unattended Deployment of Visual C++ 2010 Runtime

$cwd = Split-Path $script:MyInvocation.MyCommand.Path $location = "$cwd\Visual C++ Runtime\2010" # Create Folder Structure Write-Debug "Creating Folder Structure..." # Check if path exists if( ( Test-Path "$location" -PathType Container ) -ne $true ) { # It doesn't exist, so create it. $created = New-Item "$location" -ItemType Directory # If creation failed, we can't continue if( $created -eq $false ) { Write-Debug "Cannot create folder $location." return $created } } Set-Location "$location" # Download files $source_x86 = "http://download.microsoft.com/download/5/B/C/5BC5DBB3-652D-4DCE-B14A-475AB85EEF6E/vcredist_x86.exe" $dest_x86 = "$location\vcredist_x86.exe" $source_x64 = "http://download.microsoft.com/download/3/2/2/3224B87F-CFA0-4E70-BDA3-3DE650EFEBA5/vcredist_x64.exe" $dest_x64 = "$location\vcredist_x64.exe" Write-Debug "Downloading Redistributable Packages..." try { $wc = New-Object System.Net.WebClient # If x86 package hasn't been downloaded, do it now. if( (Test-Path $dest_x86 -PathType Leaf ) -eq $false ) { Write-Debug "Downloadloading x86 package..." $wc.DownloadFile($source_x86, $dest_x86) } else { Write-Debug "x86 already downloaded, skipping..." } # It x64 package isn't already downloaded, do it now. if( (Test-Path $dest_x64 -PathType Leaf ) -eq $false ) { Write-Debug "Downloading x64 package..." $wc.DownloadFile($source_x64, $dest_x64) } else { Write-Debug "x64 package already downloaded, skipping..." } } catch [Exception] { # Bad things happened while downloading. Write-Debug $_.Exception.Message return $_.Exception.Message } Write-Debug "Extracting x86 package..." # Extract the x86 package to it's own directory. $ext_x86 = "$location\x86" if( (Test-Path $ext_x86 -PathType Container ) -eq $false ) { Write-Debug "Extracting x86 package to $ext_x86..." $ext_x86_ec = (Start-Process -FilePath "$dest_x86" -ArgumentList " /extract:`"$ext_x86`" /q" -Wait -Passthru).ExitCode if( $ext_x86_ec -ne 0 ) { Write-Debug "Failed to extract x86 package. Error code $ext_x86_ec" return $ext_x86_ec } } else { Write-Debug "$ext_x86 already exists, skipping x86 extraction." } # Extract the x64 package to it's own directory. $ext_x64 = "$location\x64" if( (Test-Path $ext_x64 -PathType Container ) -eq $false ) { Write-Debug "Extracting x64 package to $ext_x64..." $ext_x64_ec = (Start-Process -FilePath "$dest_x64" -ArgumentList " /extract:`"$ext_x64`" /q" -Wait -Passthru).ExitCode if( $ext_x64_ec -ne 0 ) { Write-Debug "Failed to extract x64 package. Error code $ext_x64_ec" return $ext_x64_ec } } else { Write-Debug "$ext_x64 already exists, skipping x64 extraction." } Write-Debug "Creating unattended install script..." $uanttend_script = "$location\unattend.ps1" if( (Test-Path $script -PathType Leaf) -ne $true ) { # Create File New-Item $uanttend_script -ItemType file | Out-Null Write-Output "`$cwd = Split-Path `$script:MyInvocation.MyCommand.Path;`r`nStart-Process -FilePath `"`$cwd\x86\Setup.exe`" -ArgumentList `"/passive`" -Wait -Passthru;`r`nStart-Process -FilePath `"`$cwd\x64\Setup.exe`" -ArgumentList `"/passive`" -Wait -Passthru;" | Out-File $uanttend_script } else { Write-Debug "Unattended install script $uanttend_script already exists. Skipping." }

Wait for Process to Complete Using PowerShell

# Execute the program. Change the -FilePath argument as needed. $script:exitCode = (Start-Process -FilePath `"setup.exe`" -Passthru -Wait).ExitCode; # Wait for process to complete. Use the Get-Process CmdLet to determine the name # of the process you need to wait for, then change the name of that process as # needed below. $running = $true; while( $running -eq $true ) { $running = ( ( Get-Process | where ProcessName -eq "setup").Length -gt 0); Start-Sleep -s 5 }

Deleting User Profiles with PowerShell

<# Deletes user profiles for all local users. @author Brian Reich <breich@reich-consulting.net> @copyright Copyright (C) 2013 Reich Web Consulting @version 1.0 #> # Get a list of all user profiles $users = Get-WmiObject Win32_UserProfile; # If you have any profiles you want to explicitly ignore, add them here. $skip = @( ); Write-Host "Cleaning up user profiles..." -ForegroundColor Magenta foreach( $user in $users ) { # Normalize profile name. $userPath = (Split-Path $user.LocalPath -Leaf).ToLower() # If the profile name was found in the skip list, don't process it. if( $skip -contains $userPath ) { Write-Host "Skipping $userPath from ignore list."; continue; } # You can't delete the profile of the currently logged-in user, so skip it. if( $userPath -eq $env:username ) { Write-Host "Skipping $userPath because it belongs to the current user."; continue; } # If the profile belongs to a "special" account (network/system), skip it. if( $user.Special -eq $true ) { Write-Host "Skipping $userPath because it is a special system account."; continue; } # If we got this far it's safe to delete. Write-Host "Deleting profile for $userPath..."; $user.Delete(); }