Last active
April 24, 2024 08:37
-
-
Save PanosGreg/dd0144d8496ebe850c38bb609bd32c91 to your computer and use it in GitHub Desktop.
Function that finds AD Principals, namely Users, Computers or Groups
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function Get-ADPrincipal { | |
<# | |
.SYNOPSIS | |
Get users,computers or groups from Active Directory, without the need of the ActiveDirectory module. | |
The examples show the use of the .Members property, the .GetGroups() method and also | |
the .Add() and .Remove() methods from the group members list along with the .Save() method. | |
.EXAMPLE | |
Get-ADPrincipal *test* | |
Get all the users with a given name. | |
The name parameter can accept wildcards. | |
.EXAMPLE | |
(Get-AdPrincipal -SamAccountName john.doe).GetGroups() | select Name,SamAccountName | |
Show all the groups that a user is a member of. | |
.EXAMPLE | |
$users = (Get-ADPrincipal 'Domain Admins' -Type Group).Members | |
$users | sort enabled,name | select Name,SamAccountName,Enabled | |
Get all the users that are part of a given group, in this case the Domain Admins group. | |
.EXAMPLE | |
$grp = Get-ADPrincipal app-admin* -Type Group | |
$usr = Get-ADPrincipal -SamAccountName john.doe | |
if (-not $grp.Members.Contains($usr)) {$grp.Members.Add($usr)} | |
elseif ($grp.Members.Contains($usr)) {[void]$grp.Members.Remove($usr)} | |
$grp.Save() | |
Add/Remove users to/from groups. | |
You'll need to have the appropriate AD permissions to add/remove users into/from a group | |
.EXAMPLE | |
Get-ADPrincipal -Type Computer -Name lab-gmsa | |
When you want to get a gMSA (Group Managed Service Account), | |
then you need to use the Computer type. | |
.NOTES | |
About the CleanUp switch, and why I don't dispose/clean-up the objects by default. | |
If we dispose the objects, then the end-user cannot use properties or methods | |
like .Members or .GetMembers() on a group to get back the members, or .GetGroups() | |
on a user to get back the groups he belongs to, or .Add() / .Remove() on the Members of a group | |
Simply because the objects would've been disposed and you'll get an error like this: | |
Exception calling "GetMembers" with "0" argument(s): "Cannot access a disposed object." | |
This error is an example when trying to use the .GetMembers() method on a group. | |
Alternatively on the same example if you try to access the .Members property | |
you won't get an error but you'll get back nothing. | |
Another note is about gMSA type of accounts. Now these are not technically Users. | |
But still from a principal perspective, you can get them with this function using the Computer type. | |
#> | |
[CmdletBinding(DefaultParameterSetName = 'Name')] | |
[OutputType('System.DirectoryServices.AccountManagement.UserPrincipal')] # <-- when Type is User | |
[OutputType('System.DirectoryServices.AccountManagement.GroupPrincipal')] # <-- when Type is Group | |
[OutputType('System.DirectoryServices.AccountManagement.ComputerPrincipal')] # <-- when Type is Computer | |
# Note: Have to use 'type' strings for the type names, cause if we use literal [types], | |
# then the function won't work if the assembly is not loaded into the session, cause PS won't know these types | |
param ( | |
[Parameter(Mandatory,Position=0,ParameterSetName = 'Name')] | |
[string]$Name, | |
[Parameter(Mandatory,Position=0,ParameterSetName = 'SamAccountName')] | |
[string]$SamAccountName, | |
[ValidateSet('User','Group','Computer')] # <-- the Computer type can also be used for gMSA's | |
[string]$Type = 'User', | |
[string]$DomainName = $env:USERDNSDOMAIN, | |
[pscredential]$Credential, # <-- user/pass to connect to AD | |
[switch]$CleanUp | |
) | |
if ( -not ('System.DirectoryServices.AccountManagement.PrincipalContext' -as [type])) { | |
Add-Type -AssemblyName System.DirectoryServices.AccountManagement -ErrorAction Stop | |
} | |
. ([scriptblock]::Create('using namespace System.DirectoryServices.AccountManagement')) | |
try { | |
if ($PSBoundParameters.ContainsKey('Credential')) { | |
$UserName = $Credential.UserName | |
$Password = $Credential.GetNetworkCredential().Password | |
if ($UserName.IndexOf('\') -ge 1) {$UserName = $UserName.Split('\')[1]} | |
$Context = [PrincipalContext]::new([ContextType]::Domain,$DomainName,$UserName,$Password) | |
try {$IsValidUser = $Context.ValidateCredentials($UserName,$Password)} | |
catch {$IsValidUser = $false} | |
if (-not $IsValidUser) {throw "Invalid Username/Password or User $UserName is disabled"} | |
} | |
else { | |
$Context = [PrincipalContext]::new([ContextType]::Domain,$DomainName) | |
} | |
} | |
catch {throw $_} | |
if ([string]::IsNullOrWhiteSpace($Context.ConnectedServer)) { | |
$Context.Dispose() | |
throw "Could not connect to the domain $DomainName" | |
} | |
switch ($Type) { | |
'User' {$Principal = [UserPrincipal]::new($Context)} | |
'Group' {$Principal = [GroupPrincipal]::new($Context)} | |
'Computer' {$Principal = [ComputerPrincipal]::new($Context)} | |
} | |
try { | |
$SetName = $PSCmdlet.ParameterSetName | |
if ($SetName -eq 'Name') {$Principal.Name = $Name} | |
elseif ($SetName -eq 'SamAccountName') {$Principal.SamAccountName = $SamAccountName} | |
$Searcher = [PrincipalSearcher]::new() | |
$Searcher.QueryFilter = $Principal | |
$Searcher.FindAll() | |
} | |
catch {throw $_} | |
finally { | |
if ($CleanUp) { | |
# clean up | |
$Searcher.Dispose() | |
$Principal.Dispose() | |
$Context.Dispose() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment