Get-ADGroupMember processing for non domain members - powershell

We have a trusted domain that we administer and users are given acceess to a file server on our domain through groups on the trusted domain added to the local domain group which controls permissions e.g.
For the path "\server\folder\folder" the permissions might look like
MyDomain\FolderXReadWrite
Then within that group a group from the trusted domain would be included
TrustedDomain\FolderXReadWrite
I'd like to write a script to "reverse engineer" this so that I can derive both the local and trusted domain groups that have access from a given folder path and list them - I've got the following:
$path = '\\server\path\path'
$Permissions = (Get-Item -Path $path -ErrorAction Stop | Get-Acl -ErrorAction Stop).Access | Where-Object { $_.IdentityReference -like "MyDomain\*" } | ForEach-Object {
$TrustedDomGroup = Get-ADGroupMember ($_.IdentityReference.Value -split '\\')[1] -Verbose | Where-Object { ($_.DistinguishedName -split 'DC=', 2)[-1] -like "*TrustedDomain*" -and ($_.objectClass -eq 'group') }
If ($TrustedDomGroup) {
[pscustomobject]#{Name=("TrustedDomain\" + $TrustedDomGroup.SamAccountName);Rights=("Member of " + $_.IdentityReference);Domain='PHS'}
}
[pscustomobject]#{Name=$_.IdentityReference;Rights=$_.FileSystemRights;Domain='NSS'}
}
$Permissions | Sort-Object Name -Unique
This produces the output I require, however, the domain group in the above example has LOTS of users therefore processing "Get-ADGroupMember" takes a long time. Does anyone have suggestions for a faster method?

you can try to retrieve all group members with this command:
(Get-ADGroup $ADGroup -Properties members).members
I don't know if it's faster in your environment then Get-AdGroupMember.
A measurement via Measure-Command may give you more information:
Measure-Command, Microsoft Docs

Ok, so the best method to improve the lookup speed seems to be using LDAP so I put together a small function to get Foreign Security Principal group names from a local AD group:
Function List-FSPGroupMembers {
# Use LDAP search to find and resolve Foreign Security Principals from an AD group - faster than native cmdlet
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,Position=1)][string]$GroupName="",
[Parameter(Mandatory=$true,Position=2)][string]$Domain
)
[regex]$SIDmatch = "S-\d-(?:\d+-){1,14}\d+"
Function fGetADGroupObjectFromName([System.String]$sGroupName,[System.String]$sLDAPSearchRoot) {
$oADRoot = New-Object System.DirectoryServices.DirectoryEntry($sLDAPSearchRoot)
$sSearchStr ="(&(objectCategory=group)(name="+$sGroupName+"))"
$oSearch=New-Object directoryservices.DirectorySearcher($oADRoot,$sSearchStr)
$oFindResult=$oSearch.FindAll()
If($oFindResult.Count -eq 1) {
Return($oFindResult)
}
Else{
Return($false)
}
}
$sSearchRoot="LDAP://" + $Domain + ":3268"
If($oSearchResult=fGetADGroupObjectFromName $groupname $sSearchRoot) {
$oGroup=New-Object System.DirectoryServices.DirectoryEntry($oSearchResult.Path)
$oGroup.Member | Where-Object {($_.ToString()) -match $SIDmatch } | ForEach-Object {
$SID = (Select-String -Pattern $SIDmatch -InputObject $_.ToString()).Matches.Value
Try {
(New-Object System.Security.Principal.SecurityIdentifier($SID)).Translate([System.Security.Principal.NTAccount]).Value
}
Catch {
$_
}
}
}
Else {
Write-Warning ("Group "+$groupname+" not found at "+$domain)
}
}

Related

How come my condition determining if a user is in a group always returns as false?

I'm writing a script that requires detecting if the executing user account is a domain admin. I do this by getting the current user and checking if they are in the Domain Admins security group.
#Get current user
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name | Out-String
$CurrentUser = $CurrentUser -replace 'DOMAIN\\'
#Get list of Domain Admins members
$DomainAdmins = Get-ADGroupMember -Identity "Domain Admins" -Recursive | Select -ExpandProperty SamAccountName | Out-String
#Relevant condition
If ($DomainAdmins -like ($CurrentUser)) {
Write-Output "You're a domain admin." #example
}
Else {
Write-Output "You're not a domain admin."
}
Without fail, this script always runs the Else code when run from our domain controller using a domain administrator account.
I have also tried using -contains and .contains() with the exact same results. I've verified that the $CurrentUser value represents the current user accurately and that $DomainAdmins lists out the expected list of users.
I can also do this:
if ($DomainAdmins -contains ("USERNAME")) {Write-host "true"}
Where USERNAME is the current user typed out directly (case-correct or not) and it correctly returns true when that user is a member of the group.
Try with this:
$userSID = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
$DomainAdmins = Get-ADGroupMember -Identity "Domain Admins" -Recursive
if($DomainAdmins.SID.Contains($userSID))
{
Write-Output "You're a domain admin."
}
...
# OR
if($userSID -in $DomainAdmins.SID)
{
Write-Output "You're a domain admin."
}
...
# OR
if($DomainAdmins.SID -contains $userSID)
{
Write-Output "You're a domain admin."
}
The Out-String on Get-ADGroupMember is converting your array into a string which is why you can't use it as comparison:
PS /> #(
'one'
'two'
'three'
) -contains 'one'
True
PS /> (#(
'one'
'two'
'three'
) | Out-String) -contains 'one'
False
An alternative, instead of using Get-ADGroupMember:
$userSID = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
$domainAdminsDN = (Get-ADGroup -Identity "Domain Admins").DistinguishedName
$recursiveMembers = Get-ADUser -LDAPFilter "(memberOf:1.2.840.113556.1.4.1941:=$domainAdminsDN)"
if($recursiveMembers.SID.Contains($userSID))
{
Write-Output "You're a domain admin."
}
...
...
My preferred way to do this is by checking the MemberOf property of the user. It works a little better to keep them as AD objects when you have multiple domains or other oddities:
# check if DA
$DAdn = (Get-ADGroup 'Domain Admins').distinguishedname
If ( Get-ADUser -LDAPFilter "(&(SamAccountName=$Env:USERNAME)(MemberOf:1.2.840.113556.1.4.1941:=$DAdn))" ) {
Write-Output "You're a domain admin." #example
}
Your $DomainAdmins variable is a single string with the members instead of a list object. Using ... | Select -ExpandProperty SamAccountName is enough to get your list without Out-String.
-contains Looks for exact matches in collections, not for substrings.
You have to either remove Out-String from $DomainAdmins, to make it a list and make sure that the name matches entirely, or use .Contains(...) to look for a substring inside the $DomainAdmins string

Issue with ADSI script not pulling users from all groups

I have a list of groups that i need to pull back all users. I am aware due to the numbers it is restricted. I have tried adding page size but it does not work.
Here is the code
Get-Module ActiveDirectory - ErrorAction silentlycontinue
Function resolve-group}
param ($group)
For each ($member in $group.member){
$obj = [ADSI] ("LDAP://" + member)
if (obj.objectclass[1] -eq 'group'){resolve-group $obj}
else {
If ($obj.employeeid.length -eq 6)
{
$displayname = $obj.displayname
$employeeid = $obj.employeeid
$groupname = $group.name
$global:members +="$employeeid,$displayname,$groupname"
}
}
}
}
$global:members =#()
$group = [ADSI] "LDAP://cn=ab,ou=cd,ou=ef,DC=gh,DC=BB"
resolve-group $group
$group = [ADSI] "LDAP://cn=aa,ou=cc,ou=ef,DC=gh,DC=BB"
resolve-group $group
"ID,Name,Group" > c:\test\groups.csv
$global:members | sort-object - unique >> c:\test\groups.csv
I don't know how to amend the script to add and increase the page size?
I have only listed a couple of the groups but there are 100's of groups
Thanks

Powershell Script to query Active Directory

I am trying to query all users in multiple OUs of the same name. Get the SamAccountName attribute and then check for a file at a specific location with that name.
Here is what I have so far:
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = Get-ADUser -Filter * -SearchBase $_.DistinguishedName |
Select SamAccountName
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$AccountName.pdf"
}
If a file is not found. I want to add the user to a group, however here is the kicker. The account has to be added to the non-compliance group for the organization that the account belongs to.
I.E an admin account found under:
OU=Admin-User-Accounts,OU=Administration,OU=ORG1,OU=ORGS,DC=domain,DC=net
would be added to the group named 'ORG1 IA - Non-Compliant Users' located under:
OU=Groups,OU=ORG1,OU=Information Assurance,OU=ORGS,DC=domain,DC=net
Well your post is a bit confusing, and no way to really validate because I have nothing setup like this.
Yet, querying for users in all OU or the enterprise is a common everyday thing.
However, an OU name, just like any other AD object name, must be unique. So, querying for the same OU name is not a thing, in a single AD forest / domain. If you meant querying every OU for the same username, then alrighty then.
By stepping thru how you are explanation for your use case, that you have laid out.
(though maybe you want to edit your post to make it's more clear, well to me anyway...)
Using pseudo code, then trying to map that out... and with no real way to determine what you mean by several things in your post/sample. So, the below is a rough first example of how I'd do approach this... again this is untested, so, I leave that homework to you.
# query all users in multiple OUs
(Get-ADOrganizationalUnit -Filter *).DistinguishedName |
ForEach{
# Collect all members of the current OU
$AccountNames = Get-ADUser -SearchBase $PSItem -Filter *
# Process each member in the current OU collection
ForEach($AccountName in $AccountNames)
{
"Processing $($AccountName.SamAccoutnName)`n"
# Initialize properties needed for processing
$UserOrg = $AccountName.DistinguishedName.split(",")[1]
$MemberCheckOU = "OU=Admin-User-Accounts,OU=Administration,OU=ORG1,OU=$UserOrg,DC=domain,DC=net"
$NonCompliantOU = "OU=Groups,OU=ORG1,OU=Information Assurance,OU=$UserOrg,DC=domain,DC=net"
# Validate user file existence for the current user
If(-Not (Test-Path -LiteralPath "\\domain.net\SYSVOL\domain.net\IA\$($AccountName.SamAccoutnName).pdf)"))
{
# if no file Process the user groupmebership modification
"Processing $($AccountName.SamAccoutnName)"
# Notify that the file was not found and processing is required
Write-Warning -Message "$($($AccountName.SamAccoutnName).pdf) not found. Process group modify actions"
# If the current user is in the MemberCheckOU, add to the NonCompliantOU
If(Get-ADPrincipalGroupMembership -Identity $($AccountName.SamAccoutnName) | Where-Object -Property DistinguishedName -Match $MemberCheckOU )
{ Add-ADGroupMember -Identity $NonCompliantOU -Members $($AccountName.SamAccoutnName) }
Else
{
# Do something else
}
}
Else
{
# Notify that the file was found and no processing required
Write-Host "$($AccountName.pdf) found. No further actions taken" -ForegroundColor Green }
}
}
It seems that one of the variables is incorrect because PowerShell is giving me the following:
Get-ADPrincipalGroupMembership : Cannot validate argument on parameter 'Identity'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command
again.
Okay, so here is what I have so far based on your post above Postanote:
# query all users in multiple OUs
(Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'") |
ForEach{
# Collect all members of the current OU
$AccountNames = Get-ADUser -SearchBase $PSItem -Filter *
# Process each member in the current OU collection
ForEach($AccountName in $AccountNames)
{
"Processing $($AccountName.SamAccoutnName)`n"
# Initialize properties needed for processing
$UserOrg = $AccountName.DistinguishedName.split(",")[1]
$MemberCheckOU = "OU=Admin-User-Accounts,OU=Administration,OU=$UserOrg,OU=ORGS,DC=domain,DC=net"
$NonCompliantOU = "OU=Groups,OU=$UserOrg,OU=Information Assurance,OU=ORGS,DC=domain,DC=net"
# Validate user file existence for the current user
If(-Not (Test-Path -LiteralPath "\\domain.net\SYSVOL\domain.net\IA\$($AccountName.SamAccoutnName).pdf)"))
{
# if no file Process the user groupmebership modification
"Processing $($AccountName.SamAccoutnName)"
# Notify that the file was not found and processing is required
Write-Warning -Message "$($($AccountName.SamAccoutnName).pdf) not found. Process group modify actions"
# If the current user is in the MemberCheckOU, add to the NonCompliantOU
If(Get-ADPrincipalGroupMembership -Identity $($AccountName.SamAccoutnName) | Where-Object -Property DistinguishedName -Match $MemberCheckOU )
{ Add-ADGroupMember -Identity "$UserOrg IA - Non-Compliant Users" -Members $($AccountName.SamAccoutnName) }
Else
{
# Do something else
}
}
Else
{
# Notify that the file was found and no processing required
Write-Host "$($AccountName.pdf) found. No further actions taken" -ForegroundColor Green }
}
}
Looking at the original script fragment:
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = Get-ADUser -Filter * -SearchBase $_.DistinguishedName |
Select SamAccountName # note 1
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$AccountName.pdf" # note 2
}
Note 1: Your going to end up with $accountname.accountname holding your value. I think your going to want to expand this instead.
Note2: Powershell may be getting confused and thinking your looking for the variable $accountname.pdf
Instead, try this...
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = $(Get-ADUser -Filter * -SearchBase $_.DistinguishedName).SamAccountName
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$($AccountName).pdf"
}
here, we save the value of just .SamAccountName for the query to the $AccountName, and by adding $($accountname) we make clear the variable we want, and that .pdf is not part of the variable name.
Now, note as well, this doesn't save the results anywhere, it will just flash them to screen.

Get admin groups with powershell

I work as system administrator in a company with 300 users.
I am looking for a PowerShell script to get all the groups, users in each group and additional single users located at the local admin group of multiple servers joined to a single domain.
This is what I have but it's for local users only.
# Get local and Groups Users List with Content
function get-localusers {
param(
[Parameter(Mandatory=$true,valuefrompipeline=$true)]
[string]$strComputer)
begin {}
Process {
$Select = "Name","Class" | %{
Invoke-Expression "#{n='$_';e={ `$_.GetType().InvokeMember('$_', 'GetProperty', `$Null, `$_, `$Null) }}"
}
If (Test-Connection $strComputer -Count 2 -Quiet){
$computer = [ADSI]("WinNT://" + $strComputer + ",computer")
$Users = $computer.psbase.children | ? {$_.psbase.SchemaClassName -eq "User"}
foreach ($User in $Users) {
$User | Select #{N="ComputerName";E={$strComputer}},#{N="User";E={$_.Name}},Class
}
}
Else {
"" | Select #{N="ComputerName";E={$strComputer}},#{N="User";E={"Not able to Ping"}},Class
}
}
end {}
}
Get-Content "c:\temp\Servers.txt" | get-localusers | Select ComputerName,User | Export-Csv "c:\temp\Local-User_$((get-date).toString('MM-dd-yyyy')).csv" -NTI
This is the output from the script above.
"ComputerName", "User"
"mbptl-ws01","mbadmin"
"mbptl-ws01","Guest"
"mbptl-ws01","sv-dtb-pr"
Please help aggregating by groups ( that show users)
Seems that this is a good start :
https://4sysops.com/archives/create-a-list-of-local-administrators-with-powershell/
looks that this will at least get you the members for several computers

Verify Domain Admins against List via AD Module for PowerShell

I have an issue of certain administrators temporarily elevating users to Domain Admins for troubleshooting. Instead of removing said administrators from the picture, I have been asked to create a script to check the Domain Admins group nightly, and remove any users that do not belong there.
I need it to verify the Domain Admins group against a givin list in txt or csv. I could delete everone from the group nightly, then readd the desired users back, but this could create issues if someone is logging in or logging off when that happens.
Can anyone help with this? So far I have managed to export a list of users within the group by piping get-adgroup and export-csv. But I'm failing in my attempt to get the list of users and compare to an already existing list.
Here's a little script I just wrote for you. First you need to make a default text file with the default members. I named my file PreviousMembers.txt
Import-Module ActiveDirectory
Get-AdGroupMember "Domain Admins" | Select Name | Out-File C:\Scripts\PreviousMembers.txt
Now, save the following lines in ADGroupMembersDiff.ps1 and run it any time to get a current list of members. You'll need to make sure the files are in the same location that you specify below and the file names need to be the same, too.
Import-Module ActiveDirectory
$pattern = ".*"
Get-AdGroupMember "Administrators" | Select Name | Out-File C:\Scripts\CurrentMembers.txt
(Get-Content C:\Scripts\CurrentMembers.txt) | ? {$_.trim() -ne "Name" -and $_.trim() -ne "----" -and $_.trim() -ne "" } | Set-Content C:\Scripts\CurrentMembers.txt
(Get-Content C:\Scripts\PreviousMembers.txt) | ? {$_.trim() -ne "Name" -and $_.trim() -ne "----" -and $_.trim() -ne "" } | Set-Content C:\Scripts\PreviousMembers.txt
$comparedLines = Compare-Object (Get-Content C:\Scripts\PreviousMembers.txt) (Get-Content C:\Scripts\CurrentMembers.txt) -IncludeEqual | Sort-Object { $_.InputObject.ReadCount }
$lineNumber = 0
$comparedLines | foreach {
if($_.SideIndicator -eq "==" -or $_.SideIndicator -eq "=>")
{
$lineNumber = $_.InputObject.ReadCount
}
if($_.InputObject -match $pattern)
{
if($_.SideIndicator -eq "==")
{
$lineOperation = "No Change"
}
ElseIf($_.SideIndicator -eq "=>")
{
$lineOperation = "New User"
}
Elseif($_.SideIndicator -eq "<=")
{
$lineOperation = "Default User"
}
$HashChanges = #{
Line = $lineNumber
Operation = $lineOperation
Users = $_.InputObject
}
New-Object psobject -Property $HashChanges | select Users, Operation
}
}
Here's what the output will look like.
Users Operation
_____ ______
John Hancock New User
Thomas Edison Default User
George Washington New User
Thomas Jefferson No Change
Good Luck,
T|CK
MJOLINOR had the perfect suggestion. Here are some details about Restricted Groups...
By creating a Restricted Group GPO and linking it to the Domain Controllers OU, I have been able to add my desired group to the newly created GPO, tell that GPO which users belong in the desired group, and now, every 16 hours my desired group (Domain Admins) will be verified against the new GPO. So if users are members that are not part of the new GPO, they will be removed, and if users have been removed that are part of the new GPO, they will be added.
Thank you! And I hope this helps someone else.
I have only ever used Restricted groups to modify the memberships of local groups on client computers. I have never used it to modify the memberships of domain groups. In fact, Microsoft states that the intention of Restricted Group is not for Domain groups.
...Restricted Groups is a client configuration means and cannot be used with Domain Groups...