Powershell AD-Object DistinguishedName attributes - powershell

I am trying to check if an object returned by the Get-Adcomputer belongs to a certain group under the DistinguishedName property returned. I can't seem to find anything online and looking for some pointers. This attribute contains several different things and I am interested in the OU value in particular.
$computer = Get-AdComputer "$computerName"
$computer.DistinguishedName
This returns several attributes linked to OU and CN, but I am just interested if one of the OU attributes matches 'Server'.

here is something you could try if you're just looking for a true/false check
$computer = Get-AdComputer "$computerName"
$dn = $computer.DistinguishedName
$dn.Split(',') -match '^ou=.*server'
or you could use this to get the whole path
# split the distinguishedname into an array of CNs, OUs, etc.
$split = $dn.Split(',')
# setup match variable to signal that we found an object that matches what we are looking for.
$match = $false
# for each item in the array
($split | % {
# only check this if we have not yet found a match
# if this line is an OU whose name contains the word 'server'
if (!$match -and $_ -match '^ou=.*server') {
# set match to true because we found what we were looking for
$match = $true
}
# if we have found a match, output this and all the rest of the objects to the pipeline
if ($match) {
$_
}
# now we have an array starting at the OU we were looking for
# this join will put the pieces back together into a string so it looks right
}) -join ','
note: this will likely not work if any of your objects have a comma in the name.

Related

Powershell to list free domain computers

I'm using PowerShell to get a lit of all our domain computers, and then look for ones that are skipped, so we can easily get free computer names within this name range.
For example:
Get-ADComputer -Filter {Name -like "PC016*"} | Sort-Object | select Name
This gets me all our PCs starting with "PC016", and it works. From here I want to list all the skipped names.
For example, if I have this output:
PC016225
PC016226
PC016228
PC016229
I want Powershell to list the skipped item (PC016227).
How can I do this?
Remove the prefix from the names so you're left with the numerical suffix (eg. 225), then sort the resulting values to easily locate the lowest and highest values, and then simply output any number in between that isn't in the original list:
# Define computer name prefix
$prefix = "PC016"
# Create regex pattern for the prefix + a pattern to match only computer names ending with numbers
$prefixPattern = "^$([regex]::Escape($prefix))"
$namePattern = "${prefixPattern}\d+$"
# Query the directory for computer objects with the given naming prefix,
# filter the result so we only store computers with a numerical suffix
$computerNames = Get-ADComputer -Filter "Name -like '${prefix}*'" |Where-Object Name -match $namePattern |ForEach-Object Name
# Extract the numerical suffix and convert to a numeric type
$values = #($computerNames -replace $prefixPattern) -as [int[]]
# Sort the values
$values = $values |Sort-Object
# Now generate the range of values from the smallest to the biggest,
# filter out any values that are already found in the existing list
$skipped = $values[0]..$values[-1] |Where-Object { $_ -notin $values }
# ... and finally attach the prefix again and output the new possible names
$skipped |ForEach-Object { -join $prefix,$_ }

Powershell - How to remove trailing spaces from a list of groups a user is in

I've copied/created a script to get all the members of a group that a user is part of, including nested groups. However, my output isn't quite the way I want it.
It goes in one of two ways. Either it outputs as one big string, which looks nice, but has trailing spaces on each line so I cannot simply copy and paste it into AD. Or if I change the Out-String to use -stream, it comes out as a garbled mess, but may allow me to trim the spaces.
I currently have the output going into a TextBox in a simple GUI.
Function Get-ADUserNestedGroups {
Param
(
[string]$DistinguishedName,
[array]$Groups = #()
)
#Get the AD object, and get group membership.
$ADObject = Get-ADObject -Filter "DistinguishedName -eq '$DistinguishedName'" -Properties memberOf, DistinguishedName;
#If object exists.
If($ADObject)
{
#Enummurate through each of the groups.
Foreach($GroupDistinguishedName in $ADObject.memberOf)
{
#Get member of groups from the enummerated group.
$CurrentGroup = Get-ADObject -Filter "DistinguishedName -eq '$GroupDistinguishedName'" -Properties memberOf, DistinguishedName;
#Check if the group is already in the array.
If(($Groups | Where-Object {$_.DistinguishedName -eq $GroupDistinguishedName}).Count -eq 0)
{
#Add group to array.
$Groups += $CurrentGroup;
#Get recursive groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups;
}
}
}
#Return groups.
Return $Groups;
}
Function Display-UserGroups {
#Get all groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $userSAM).DistinguishedName;
$ResultsTextBox.Text = $Groups | Select-Object Name| Sort-Object name | Out-String
The output with the first way looks like:
Group Name1(Eight Spaces Here)
Group Name2(Eight Spaces Here)
The output with the second way looks like:
Group Name1GroupName2GroupName3
Thanks for your help!
You need to trim your output, which can easily be done with String.Trim method. However, it can only be applied against strings. $Groups will be an array of ADObject types. You will need to return the Name values of those objects and apply the Trim() method to the values.
($Groups | Select -Expand Name | Sort).Trim() -join "`r`n"
You can use $Groups += $CurrentGroup.trimEnd() to add the value with the trailing spaces trimmed.
A few other methods you might want to research
.trim() # Trim leading and trailing
.trimEnd() # Trim trailing
.trimStart() # Trim leading
The padding is being caused by PowerShell's formatting system. Select-Object is returning single property (Name) objects. PowerShell outputs that as a table, which may have some padding. Out-String is keeping that padding which is why it was reflecting in your final output...
The name property of the group is already a string. There's no need to use Out-String if you unroll the Name property from your $Groups variable/collection.
With some other adjustments for readability & testing. Below will emit a single string with each group on a new line. That should be paste-able into AD, by which I think you meant Active Directory User & Computers.
Function Get-ADUserNestedGroups
{
Param
(
[string]$DistinguishedName,
[array]$Groups = #()
)
# Get the AD object, and get group membership.
$ADObject = Get-ADObject $DistinguishedName -Properties memberOf, DistinguishedName
# If object exists.
If( $ADObject )
{
# Enummurate through each of the groups.
Foreach( $GroupDistinguishedName in $ADObject.memberOf )
{
# Get member of groups from the enummerated group.
$CurrentGroup = Get-ADObject $GroupDistinguishedName -Properties memberOf, DistinguishedName
# Check if the group is already in the array.
If( $Groups.DistinguishedName -notcontains $GroupDistinguishedName )
{
# Add group to array.
$Groups += $CurrentGroup
# Get recursive groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName $GroupDistinguishedName -Groups $Groups
}
}
}
# Return groups.
Return $Groups
}
$userSAM = 'UserName'
# Get all groups.
$Groups = Get-ADUserNestedGroups -DistinguishedName (Get-ADUser -Identity $userSAM).DistinguishedName
($Groups.Name | Sort-Object) -join [System.Environment]::NewLine
Among the secondary changes you'll see I removed the -Filter parameter in a few places. The DistinguishedName can be used as the -Identity parameter so no need to filter.
Also, where you were checking if the $Groups collection already had the current group I used the -notContains operator instead. That should be faster then repeatedly iterating the collection with |Where{...} The code is also shorter and more readable.
Note: if we reverse the operands we could use -notin which may look
nicer still
An aside; You should avoid appending arrays with +=. Doing so can causes performance problems because it creates a new array any copies the contents over. The best way to deal with this is to allow PowerShell to accumulate the results for you. If you must do the append directly, look into using Array Lists. There's a lot information on this, just google it.

PowerShell Compare AD Groups but show only these what I need

I have a PowerShell script that is generating from a file.txt a list of users and groups where they belong to.
The next step that I need to achieve is to confirm if the group from file Groups.txt is assigned to users.
To do this I used logical operator -contains so the code is looking like that:
$UserList = Get-Content ("C:\users.txt")
$GroupList = Get-Content ("C:\Groups.txt")
$result = #()
foreach ($UserList in $UserList){
$data = New-Object PSObject
$Group = (Get-ADPrincipalGroupMembership -Identity $UserList | foreach {$_.SamAccountName}) -contains $GroupList
$data = get-aduser $Userlist -properties samaccountname,givenname,surname | select samaccountname,givenname,surname, #{name="Groups";expression={$Group}}
$result += $data
}
$result
The code is working when I have only one group in file Groups.txt. If I have two or more it is applying only the last one with value True
The resolution what I expecting is
When the user has one or multiple groups from the file Group.txt script should mention that group name and nothing else.
To be more precise I need something like that as results:
samaccountname givenname surname Groups
-------------- --------- ------- ------
User FirstName Surname False (or anything)
User1 Firstname Surname Group1, Group2, Group3
Many thanks for any help in this matter.
You can do the following:
$UserList = Get-Content "C:\users.txt"
$GroupList = Get-Content "C:\Groups.txt"
$result = foreach ($User in $UserList) {
$Groups = Get-ADPrincipalGroupMembership -Identity $User |
Where SamAccountName -in $GroupList
Get-ADUser -Identity $User -Properties GivenName,Surname |
Select-Object SamAccountName,GivenName,Surname,#{name="Groups";expression={$Groups.SamAccountName}}
}
$result
When using a foreach loop, the proper syntax is foreach ($<item> in $<collection>) { statements }. $<item> is just a variable that you can reference within the statements, and it should be a variable that has not been assigned up until that point. See About_Foreach.
If your foreach statements produce output as in this case, they can simply be collected by assigning a variable to the foreach loop. This will result in a more efficient array assignment. Using += to effectively expand an array, just results in creating a new array on each loop iteration that is bigger than the previous. It is not efficient and is unnecessary in cases like these.
Regarding collection comparison, -contains is used when comparing a left-hand side (LHS) single item with a right-hand side (RHS) collection. A sample syntax would be $<collection> -contains $<single_item>. -in is used when comparing a LHS single item with a RHS collection. A sample syntax would be $<single_item> -in $<collection>. See About Comparison Operators.
Since you ultimately wanted to gather groups from a command output based on a certain condition, that is a prime candidate for Where or Where-Object. It's pseudo code usage is out of these 20 items, show me the ones that meet a certain condition. See Where-Object.

PowerShell comparison of partial string confusion

I have a set of partial job titles in an array that I'm trying to compare to someone's full title in Active Directory (AD). -like and -match, as well as all the others, aren't working.
[string[]]$Titles =#('genetic','medic','nurs','optome','perfusion','pharm','phys')
($titles -like $user.Title) - nope
($user.title -contains $titles) - nope
($Titles.Contains($user.title)) - nope
I need a user title, "Physician", to match up with "phys". Why is this not working?
To do what you (seem to) want, iterate ("loop") over each title in $Titles and compare the $User.Title property to each individual partial title, and then see if any of them returned $true:
foreach($User in Get-ADUser -Filter * -Properties Title){
$TitleFound = #($Titles |ForEach-Object {$User.Title -like "$_*"} ) -contains $true
if($TitleFound){
Do-Stuff
}
}
That being said, you may want to use the partial strings to build an LDAP Filter string, that way the Domain Controller can take care of the filtering instead of returning all the users.
I LDAP search filter syntax, your query would look like this:
(|(title=genetic*)(title=medic*)(title=nurs*)(title=optome*)(title=perfusion*)(title=pharm*)(title=phys*))
You could generate that from your string array, with ForEach-Object:
# The | prefix means "or", as in "if any of these statements are true", return the object
$FilterTemplate = '(|{0})'
# This will be the comparison statements inside the filter
$FilterCriteria = $Titles |Foreach-Object { '(title={0}*)' -f $_ }
# Now put it all together
$LDAPFilter = $FilterTemplate -f -join($FilterCriteria)
# And retrieve the users:
$MedicalStaff = Get-ADUser -LDAPFilter $LDAPFilter -Properties Title

Powershell - User creation script avoid duplicate username

I would like to do an Active Directory search using PowerShell to discover if the username I want to create is already in use,. If it is already in use I want the script to add the following number at the and of the user name.
Import-Module ActiveDirectory
$family= Mclaren
$first= Tony
#This part of the script will use the first 5 letters of $family and the first 2 letters of $first and join them together to give the $username of 7 letters
$username = $family.Substring(0, [math]::Min(5, $family.Length)) + $first.Substring(0, [math]::Min(2, $first.Length))
The user name will look like "mclarto" base on that (username
take the 5 first letters of the family name plus 2 charof the firstname)
a seach is done in AD.
If there is no result, "mclarto" will be taken as $username without
any number at the end.
If the search find other users with the same username, the
username should take the following number, in this case it would be
"mclarto1".
If "mclarto1" already exist then "mclarto2" should be use and so on.
I think this will get you close, it uses the ActiveDirectory module.
Import-Module ActiveDirectory
$family = "Mclaren*"
# Get users matching the search criteria
$MatchingUsers = Get-ADUser -Filter 'UserPrincipalName -like $family'
if ($MatchingUsers)
{
# Get an array of usernames by splitting on the # symbol
$MatchingUsers = $MatchingUsers | Select -expandProperty UserPrincipalName | %{($_ -split "#")[0]}
# loop around each user extracting just the numeric part
$userNumbers = #()
$MatchingUsers | % {
if ($_ -match '\d+')
{
$userNumbers += $matches[0]
}
}
# Find the maximum number
$maxUserNumber = ($userNumbers | Measure-Object -max).Maximum
# Store the result adding one along the way (probably worth double checking it doesn't exist)
$suggestedUserName = $family$($maxUserNumber+1)
}
else
{
# no matches so just use the name
$suggestedUserName = $family
}
# Display the results
Write-Host $suggestedUserName