# encoding: ascii
# api: powershell
# title: Get-NestedGroups v2
# description: Rewrite of http://poshcode.org/3460.
# version: 0.1
# type: function
# author: Jules_74
# license: CC0
# x-poshcode-id: 3568
# x-derived-from-id: 3569
# x-archived: 2016-06-01T12:46:47
# x-published: 2013-08-11T15:02:00
#
# Accepts pipeline input. Function returns a multidimensional array when used with pipeline input.
# Input must be as either type String or an Active Directory User object.
# Returns Active Directory Group objects of the groups the user is a member of.
#
Function Global:Get-NestedGroups {
	<#
		.SYNOPSIS
			Enumerate all AD group memberships of an account (including nested membership).
		.DESCRIPTION
			This script will return the AD group objects for each group the user is a member of.
		.PARAMETER UserName
			The username whose group memberships to find.
		.EXAMPLE
			Get-NestedGroups johndoe | Out-GridView
			Get-NestedGroups johndoe, janedoe | % { $_ | Out-GridView }
			Get-ADUser -Filter "cn -like 'john*'" | % { Get-NestedGroups $_ | Sort-Object Name | Out-GridView -Title "Groupmembership for $($_)" }
			"johndoe","janedoe" | % { Get-NestedGroups $_ | Sort-Object Name | Export-CSV Groupmembership-$($_.Name).csv -Delimiter ";" }
			"johndoe","janedoe" | Get-NestedGroups | % { Sort-Object Name | Out-GridView }
		.NOTES
			ScriptName : Get-NestedGroups
			Created By : Gilbert van Griensven
			Date Coded : 06/17/2012
			Updated    : 08/11/2012

			The script iterates through all nested groups and skips circular nested groups.
		.LINK
	#>
	[CmdletBinding(SupportsShouldProcess=$True)]
	Param (
		[Parameter(Mandatory=$True,ValueFromPipeline=$True,HelpMessage="Please enter a username")] $UserName
	)
	Begin {
		$PipelineInput = -not $PSBoundParameters.ContainsKey("UserName")
		Write-Verbose "Looking for ActiveDirectory module"
		$Script:ADModuleUnload = $False
		If (!(Get-Module ActiveDirectory)) {
			Write-Verbose "ActiveDirectory module not loaded - checking availability"
			If (Get-Module -ListAvailable | ? {$_.Name -eq "ActiveDirectory"}) {
				Write-Verbose "ActiveDirectory module is available - loading module"
				Import-Module ActiveDirectory
			} Else {
				Write-Verbose "ActiveDirectory Module is not available"
				$Script:ADModuleUnload = $True
			}
		} Else {
			Write-Verbose "ActiveDirectory Module is already loaded"
			$Script:ADModuleUnload = $True
		}
		Function GetNestedGroups {
			Get-ADGroup $_ -Properties MemberOf | Select-Object -ExpandProperty MemberOf | % {
				If (!(($Script:GroupMembership | Select-Object -ExpandProperty DistinguishedName) -contains (Get-ADGroup $_).DistinguishedName)) {
					$Script:GroupMembership += (Get-ADGroup $_)
					GetNestedGroups $_
				}
			}
		}
		Function GetDirectGroups {
			$InputType = $_.GetType().Name
			If (($InputType -ne "ADUser") -and ($InputType -ne "String")) {
				Write-Error "Invalid input type `'$($_.GetType().FullName)`'" -Category InvalidType -TargetObject $_
				Break
			}
			If ($InputType -eq "String") {
				Try {
					Write-Verbose "Querying Active Directory for user `'$($_)`'"
					$UserObject = Get-ADUser $_
				}
				Catch {
					Write-Verbose "$_"
					Write-Error $_ -Category ObjectNotFound -TargetObject $_
					Break
				}
			} Else { $UserObject = $_ }
			$Script:GroupMembership = @()
			$Script:GroupMembership += (Get-ADGroup "Domain Users")
			Get-ADUser $UserObject -Properties MemberOf | Select-Object -ExpandProperty MemberOf | % {
				$Script:GroupMembership += (Get-ADGroup $_)
			}
			$Script:GroupMembership | ForEach-Object {GetNestedGroups $_}
		}
	}
	Process {
		If ($PipelineInput) {
			GetDirectGroups $_
			, $Script:GroupMembership
		} Else {
			$UserName | ForEach-Object {
				GetDirectGroups $_
				$Script:GroupMembership
			}
		}
	}
	End {
		If (!($Script:ADModuleUnload)) {
			Write-Verbose "Removing module ActiveDirectory"
			Remove-Module ActiveDirectory -ErrorAction SilentlyContinue
		}
	}
}
