Combining PowerShell Commands - powershell

I've written two scripts that give me the independent output that I need, but am not sure how to put them together to give me the combined output.
Returns the count of all of the active users in a particular OU
(Get-ADUser -searchbase "OU=OU, DC=domain, DC=com" -filter * |Where {$_.enabled -eq "True"}).count
Returns the OU's underneath the same above OU that have "string" in the description property.
Get-ADOrganizationalUnit -searchbase "OU=OU, DC=domain, DC=com" -filter * -Properties description | where {$_.description -eq "string"}
What I'm trying to accomplish is for the script to give me a count of all of the active users underneath the OU's that have "string" in the description property.

I think this is the easiest way to achieve your goal
$OUs = Get-ADOrganizationalUnit -searchbase "OU=OU, DC=domain, DC=com" -filter * -Properties description | where {$_.description -eq "string"}
ForEach ($OU in $OUs) {
$count = (Get-ADUser -searchbase $OU -filter * | Where {$_.enabled -eq "True"}).count
Write-Host "OU $OU has $count users"
}
the result will be
OU OU=foo,DC=domain,DC=com has 6 users
OU OU=Computers,OU=foo,DC=domain,DC=com has 0 users
OU OU=Users,OU=foo,DC=domain,DC=com has 6 users
OU OU=Groups,OU=foo,DC=domain,DC=com has 0 users

Sergio Tanaka's helpful answer works well; let me complement it with a performance improvement:
You can greatly speed up your command by filtering at the source, by passing the filter criterion as a -Filter argument instead of retrieving all objects first and then filtering them with a separate Where-Object call:
Get-ADOrganizationalUnit -SearchBase 'OU=OU, DC=domain, DC=com' `
-Filter 'Description -eq "string"' -Properties description | #`
ForEach-Object {
$count = (Get-ADUser -SearchBase $_ -Filter 'Enabled -eq $true').Count
}
Note that the -Filter-string syntax of the AD cmdlets resembles PowerShell code, but it differs in many important ways - see Get-Help about_ActiveDirectory_Filter
The general advantages of using -Filter:
On a general note, the same performance improvement can be had with cmdlets for other PowerShell data providers, such as the one for the filesystem (e.g., Get-ChildItem), if they support a -Filter parameter:
A -Filter string is applied at the data source, which means that PowerShell only receives the result of the filtering.
Since providers are are implemented in compiled code (and they have access to lower-level internals), this generally makes for much better performance; additionally, in remoting scenarios performance improves by simply having to transfer less data over the network.
Note that -Filter parameters are always strings with provider-specific syntax, so you must consult the relevant provider/cmdlet documentation.

Related

Filtering Get-ADGroups with question mark wildcard doesn't seem to work

I'm trying to get a list of AD groups that have a name that starts with "Users-####-" (# is a number 0-9).
I've tried using Get-ADGroup -Filter {name -like "Users-[0-9][0-9][0-9][0-9]-*"} and Get-ADGroup -Filter {name -like "Users-????-*"}, but got no results.
I can of course use Get-ADGroup -Filter {name -like "Users-*"}, but this will also include all the groups that have something else than four characters after Users-.
I then decided to try using Where-Object and the this code returned the expected groups
Get-ADGroup -Filter * | Where-Object {$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"}
According to Microsoft documentation about wildcards, both ways I tried should work, but they actually don't.
Anybody have an idea what I'm doing wrong or is this just a bug in how ADGroup filtering works?
According to Microsoft documentation about wildcards, both ways I tried should work, but they actually don't.
That's a reasonable assumption, but the -Filter parameter exposed by some cmdlets in the ActiveDirectory module is a deceptive construct - it's designed to look like PowerShell's native operator syntax, but "underneath the hood" the cmdlet translates the filter expression to a valid LDAP query filter:
name -like "Users-*"
# is translated to
(name=Users-*)
$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"
# is translated to
(Name=Users-[0-9][0-9][0-9][0-9]-*)
Since LDAP doesn't recognize the wildcard range construct [0-9], it ends up querying the directory store for objects where the name literally starts with Users-[0-9][0-9][0-9][0-9]- - same goes for ?.
Since * is the only wildcard accepted by LDAP, the closest you can get is:
Get-ADGroup -Filter {name -like "Users-*-*"}
And then filter the results further on the client with Where-Object (in which case we're back to PowerShell performing the comparison and we can use all the wildcards again):
Get-ADGroup -Filter {name -like "Users-*-*"} | Where-Object Name -like 'Users-[0-9][0-9][0-9][0-9]-*'
As stated in about_ActiveDirectory_Filter:
Note: PowerShell wildcards, other than "*", such as "?" are not
supported by the -Filter parameter syntax.
In this case, you can combine -LDAPFilter with Where-Object to keep your query compatible and efficient:
Get-ADGroup -LDAPFilter "(name=Users-*-*)" | Where-Object {
$_.Name -like "Users-[0-9][0-9][0-9][0-9]-*"
}
You can use -Filter in this case as pre-filter, so at least you will get only groups with names starting with Users-.
Then in a further Where-Object clause you can specify further and in this case I would use regex -match there like:
Get-ADGroup -Filter "Name -like 'Users-*'" | Where-Object { $_.Name -match '^Users-\d{4}-.*' }
P.S. -Filter should be a string, not a scriptblock
The filters in the Powershell Active Directory module have odd behaviors.
Filter or Where Clause
There are two ways to restrict the output of an AD cmdlet like
Get-ADUser. First, you can use the -LDAPFilter or -Filter parameters
to filter the output. Second, you can pipe the results to the
Where-Object cmdlet. Where possible, the first method is more
efficient for two reasons.
Filtering is done on the domain controller instead of the local
client. The domain controller is more likely to be a server class
computer optimized for queries. Filtering results in a smaller
resultset sent over the network from the domain controller to the
client. In contrast, the Where-Object cmdlet only filters on the local
client after the resultset has been sent from the remote computer. For
example, you could retrieve all users with a department that starts
with "IT" using the Where-Object cmdlet as follows:
Get-ADUser -Filter * -Properties department | Where-Object {$_.department -Like "it*"} | Select sAMAccountName, department The
resultset from the Get-ADUser statement includes all users in the
domain. A more efficient method to get the same results would use a
filter, similar to below:
Get-ADUser -Filter {department -Like "it*"} -Properties department | Select sAMAccountName, department Now only the users needed are
included in the resultset from Get-ADUser. In a test domain with 2,150
users (7 of which have "IT" departments) the first command above took
4 times as long as the second (average of 10 trials each with 16
minutes between trials). The difference could be substantial in a
domain with ten's of thousands of users.
Also, note that the statements above use the -Properties parameter to
specify only the properties needed. The default properties exposed by
the cmdlet are always included, like sAMAccountName in this case. If
you request all properties, with -Properties *, the resultset will
include many properties for each user. The resultset will be much
smaller if you only specify the extended properties needed, like
department in this case. Repeating the last command above in the test
domain with 2,150 users, but requesting all properties (with
-Properties *) required 75% more time on average to complete. The default and extended properties exposed by the Get-ADUser cmdlet are
documented in Active Directory: Get-ADUser Default and Extended
Properties.
PowerShell Filter Syntax
The PowerShell Active Directory module cmdlets support an extended
form of the PowerShell Expression Language. PowerShell documentation
indicates that PowerShell syntax filters should be enclosed in braces.
But there are many examples where single quotes or double quotes are
used instead. As you might expect, this affects how the filter is
interpreted.
Using String Attributes The following table shows some example
PowerShell syntax filters using string properties, like Department.
Some filters result in error, others do not raise an error but never
produce results. The variable $Dept is defined as previously.
Filter Result
-Filter {department -eq "IT Department"} Works
-Filter {department -eq $Dept} Works
-Filter {department -eq "$Dept"} No Results
-Filter {department -eq '$Dept'} No Results
-Filter "department -eq $Dept" Error
-Filter 'department -eq $Dept' Works
-Filter {department -eq "it*"} No Results
-Filter {department -Like "it*"} Works
-Filter "department -Like ""it*""" Works
-Filter "department -Like 'it*'" Works
-Filter 'department -Like "it*"' Works
-Filter 'department -Like ''it*''' Works
-Filter {department -ge "IT"} Works
Some of these results may not be expected.
For example, you might expect enclosing a variable in a quoted string
to work. The best policy might be to always enclose PowerShell syntax
filters in braces, and to refrain from quoting variables.
The last example using the "-ge" operator is only useful in rare
situations. The filter will result in any departments that are
lexicographically greater than or equal to "IT". For example, it will
return "Test Department", because "T" is greater than "I".

Powershell Query to show all accounts in an AD group or in an OU

I have been asked to export the details of all service accounts in our AD domain. Seems straight forward but I have found over the years there has not be a consistent way service accounts have been controlled. We have a Service Accounts OU as well as a Service Account AD security group. Some accounts are in the OU but not the group, some in the group but another random OU, some are in both.
I can query each one individually:
Group
Get-ADGroupMember GROUP.ServiceAccounts
OU
Get-ADUser -SearchBase "OU=Service Accounts,OU=Accounts,DC=Domain,DC=com" -filter *
How can I combine both into one powershell query?
To combine these into one query, which will likely be more inefficient than what you are already doing, you can do the following:
$searchBase = '*OU=Service Accounts,OU=Accounts,DC=Domain,DC=com'
$groupDN = 'CN=Group Name,OU=Groups,DC=Domain,DC=com'
Get-Aduser -Filter * |
Where-Object { $_.DistinguishedName -like $searchBase -or $_.MemberOf -contains $groupDN}
You will need to update $groupDN with the actual distinguished name of your group. If you could find a more efficient way to -Filter on DistinguishedName, you could make this faster with the -Filter parameter.
Alternative Solution:
A more efficient way would be to combine your results into an object array.
$array1 = Get-ADGroupMember $Group
$array2 = Get-ADUser -SearchBase "OU=Service Accounts,OU=Accounts,DC=Domain,DC=com" -filter *
$array1,$array2

List all OU with Users

I'm searching a way in PowerShell to list all OU with Users in it.
I tried something with Get-ADUser or Get-ADOrganizationalUnit but it doesn't really work.
One approach would be to get all of the OU's and check to see if they contain any users via -SearchBase. Filter them out with a Where-Object clause
Get-ADOrganizationalUnit -Filter * |
Where-Object {(Get-ADUser -SearchBase $_.DistinguishedName -Filter *).Count -gt 0} |
Select-Object -ExpandProperty DistinguishedName
Simply pull the OU from each user object. Then find unique values.
Get-ADUser -Filter * |
ForEach-Object {$_.DistinguishedName -replace '(^.*?)(OU=.*)','$2'} |
Sort-Object -Unique
Note: this makes the assumption that you are not storing user objects in Containers rather than OUs

get-adgroup -filter "SID -like '*-512'"

I have been wanting to figure out how to use -filter to get what I want. What I am trying to do is find the Domain Admins group by a -like statement of *-512 against the SID property using the following:
get-adgroup -filter "SID -like '*-512'"
It works if I put the actual SID
get-adgroup -filter "SID -eq 'S-1-5-21domain-512'"
I know doing it this way will work
get-adgroup -filter * | ? {$_.SID -like '*-512'}
https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
As BenH comments, you cannot partially filter on SIDs in LDAP queries, because of the way SID values are stored in the directory. The SID string you see is an SDDL representation of an underlying byte array.
I assume your motivation for attempting wildcard matching against a well-known RID is that you don't know the domain SID in advance. You can easily obtain that with the Get-ADDomain cmdlet:
$DomainSID = (Get-ADDomain).DomainSID
$DomainAdminsSid = New-Object System.Security.Principal.SecurityIdentifier ([System.Security.Principal.WellKnownSidType]::AccountDomainAdminsSid,$DomainSID)
Get-ADGroup -Filter {SID -eq $DomainAdminsSid}

Finding all members in OUs of the same name

My organization has a different OU for each site we have. Within each site is a nested out called OU=USERS.
I want to find all members in every sites nested USERS OU.
Simply using the following command does not work:
Get-ADUser -Filter * -SearchBase "OU=USERS,DC=*****,DC=*****"
Obviously, this does not return anything. I must specify a site:
Get-ADUser -Filter * -SearchBase "OU=USERS,OU=MySite,DC=*****,DC=*****"
Is it possibly to search through every site OU looking for the sub USERS ou?
You could use another cmdlet to get the OU's you are looking for.
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Users'"
$ous | ForEach-Object{
Get-ADUser -Filter * -SearchBase $_.DistinguishedName
}
Get-ADOrganizationalUnit will get all the USERS OU's for you then you can run Get-ADUser against each of those.