How to select multiple subobjects of an object in powershell - powershell

The goal is to select all users from a list of groups which contains the subojects "Users" with X users. This user list should be written later as batch to an SQL Server. The goal ist to have a table with the user properties and the groupname + id.
To make the selection and mapping faster, i have tried to work with the select command of powershell. But there is a problem when more than one user is in the group
$GroupUsers = #()
foreach ($group in $groups) {
$GroupUsers = New-Object -TypeName PSObject -Property #{
UserLoginName = $group.Users.LoginName
UserTitle = $group.Users.Title
UserEmail = $group.Users.Email
GroupName = $group.Title
} | Select UserLoginName, UserTitle, UserEmail, GroupName
$GroupUsers+= $GroupUsers
}
The output which is generated looks like the following:
UserId : 33
UserLoginName : WalterX.test.com
UserTitle : Walter X
UserEmail : x#mail.de
GroupName : Group1
GroupId : 1
UserId : {1, 2, 3, 4...}
UserLoginName : {User1.test.com, User2.test.com,User3.test.com ...}
UserTitle : {User1,User2,User3...}
UserEmail : {User1#mail.com,User2#mail.com...}
GroupName : Group2
GroupId : 2
As you can see the first user is exported correctly. The second entry in the array has on the UserLoginName, Title and EMail property multiple users in it...

Don't use +=it's ineffective as it rebuilds the complete array on each addition
Assign the output of the foreach to a variable instead, using a [PSCustomObject]
nest another foreach to iterate the users of the group.
$GroupUsers = foreach($group in $groups) {
foreach($user in $group.Users){
[PSCustomObject]#{
UserLoginName = $User.LoginName
UserTitle = $User.Title
UserEmail = $User.Email
GroupName = $group.Title
}
}
}

Related

My AD users are not updating using powershell and csv

I have read many articles and I've been able to come up with a code that works for what i want to do: I want to be able to update my AD USERS and if a the attribute is blank on my csv it should be removed on the users attribute
$Users = Import-CSV "C:\Users\tolulope\Desktop\Tolu AD\ADusers2.csv"
foreach($User in $Users )
{
Write-host "$user"
Get-ADUser -Filter "DisplayName -like '$user'"|Set-ADUser -Replace #{
givenName = "$User.givenName";
sn = "$User.sn";
c = "$User.c";
cn = "$User.cn";
company = "$User.company";
co = "$User.co";
countryCode = "$User.countryCode" ;
department = "$User.department";
displayName = "$User.displayName";
mobile = "$User.mobile";
name = "$User.name";
userPrincipalName = "$User.userPrincipalName"
telephoneNumber = "$User.telephoneNumber";
streetAddress = "$User.streetAddress";
st = "$User.st";
postalCode = "$User.postalCode";
physicalDeliveryOfficeName = "$User.physicalDeliveryOfficeName";
manager = "$User.manager";
employeeID = "$User.employeeID";
title = "$User.title";
l = "$User.l";
homePhone = "$User.homePhone";
mail = "$User.mail";
}
}
Read-Host -Prompt "Press Enter to exit"

How to pass an array through Add-UnifiedGroupLinks -Identity?

I would like to add a user to several Office365 groups in one go.
My code is as follows:
$OfficeGroups = #("Egg_Group", "Chicken Group", "Soup Group")
foreach ($Group in $OfficeGroups)
{
$AddParams = #{
Identity = $Group
LinkType = 'Members'
Links = $Username
}
Add-UnifiedGroupLinks #AddParams
}
I am unable to pass an array of groups through the -Identity parameter. Is there an alternative cmdlt I could use? I have tried
Add-DistributionGroupMember
but it still doesn't work. Is this an impossible task?
When you work with an array of group names it should be sufficient to just loop through them:
$OfficeGroups = #('Egg_Group', 'Chicken_Group', 'Soup Group')
foreach ($Group in $OfficeGroups) {
$AddParams = #{
Identity = $Group
LinkType = 'Members'
Links = $Username
}
Add-UnifiedGroupLinks #AddParams
}
Note: It's always better to use splatting i.e. a hashtable to feed parameters to your function if you want to make it look more readable.
Can you try looping through your array like this?
$OfficeGroups = #("Egg_Group", "Chicken Group", "Soup Group")
$OfficeGroups | ForEach-Object {Add-UnifiedGroupLinks -Identity $_ -LinkType 'Members' -Links $Username}

How to receive OU values separately in different columns via powershell?

The powershell query I use to get the list of all the workstations registered in AD is on below:
Get-ADComputer -Filter {OperatingSystem -NotLike "*server*"} -Property * | Select-Object Name,SID,DistinguishedName,whenCreated,LastLogonDate | Export-CSV allworkstations2.csv -NoTypeInformation -Encoding UTF8
An example of "DistinguishedName" is like:
CN=500-AV,OU=Workstations,OU=SecondOU,OU=ThirdOU,OU=FourthOU,DC=myDC1,DC=myDC2
CN=600-AV,OU=FirstOU,OU=SecondOU,OU=ThirdOU,OU=FourthOU,OU=FifthOU,DC=myDC1,DC=myDC2
Please note that the number of OU values for each workstation can differ, can't say it is always 4. Edit: Maximum number of OU a workstation can have is 5.
I need all the OU values separately, as different columns (OU1, OU2, ... etc). In Excel, I was using this formula below to receive all the OU values in separate columns:
=TRIM(MID(SUBSTITUTE(MID($B2,FIND(",OU=",$B2)+4,FIND(",DC=",$B2)-FIND(",OU=",$B2)-4),",OU=",REPT(" ",999)),(COLUMN(A:A)-1)*999+1,999))
What I want is to get the OU values in different columns directly from the powershell. I couldn't find out the correct syntax to update my query accordingly. Any help would be appreciated.
Here is the version info:
Edit2: Example of expected output:
WorkstationName SID OU1 OU2 OU3 OU4 OU5 createdDate LastLogin
500-AV X Workstations SecondOU ThirdOU FourthOU null 1/1/2018 6/1/2018
600-AV X FirstOU SecondOU ThirdOU FourthOU FifthOU 1/1/2018 6/1/2018
now that you clarified things a tad, i think this does what you want. [grin]
what it does ...
creates two user objects to work with
delete this when you are ready to work with your data set. [grin]
iterates thru the user list
splits the DistinguishedName to get the OUs
makes the $OuList variable into an array even if there is only one OU
builds a custom object with the anticipated max number of OUs
you will need to determine that ahead of time.
sends that object out to the $Results collection
displays that collection
at that point, you have a collection that will gracefully export to a CSV file. [grin]
here's the code ...
$UserList = #(
[PSCustomObject]#{
ComputerName = '111-AV'
SID = '1-22-333'
DistinguishedName = 'CN=111-AV,OU=SolitaryOU,DC=myDC1,DC=myDC2'
DateCreated = '2011-11-11'
LastLogon = '2019-08-11'
}
[PSCustomObject]#{
ComputerName = '500-AV'
SID = '1234-5678-90'
DistinguishedName = 'CN=500-AV,OU=Workstations,OU=SecondOU,OU=ThirdOU,OU=FourthOU,DC=myDC1,DC=myDC2'
DateCreated = '2001-01-01'
LastLogon = '2019-08-08'
}
[PSCustomObject]#{
ComputerName = '666-AV'
SID = '777-888-999'
DistinguishedName = 'CN=666-AV,OU=Servers,OU=SrvOu2,OU=SrvOu3,OU=SrvOu4,OU=SrvOu5,DC=myDC1,DC=myDC2'
DateCreated = '1999-12-31'
LastLogon = '2019-08-20'
}
)
$Results = foreach ($UL_Item in $UserList)
{
[array]$OuList = #($UL_Item.DistinguishedName.Split(',')).
Where({$_ -match 'OU='}).
ForEach({$_.Split('=')[-1]}).
Trim()
[PSCustomObject]#{
ComputerName = $UL_Item.ComputerName
SID = $UL_Item.SID
OU_1 = $OuList[0]
OU_2 = $OuList[1]
OU_3 = $OuList[2]
OU_4 = $OuList[3]
OU_5 = $OuList[4]
DateCreated = $UL_Item.DateCreated
LastLogon = $UL_Item.LastLogon
}
}
$Results
output to screen ...
ComputerName : 111-AV
SID : 1-22-333
OU_1 : SolitaryOU
OU_2 :
OU_3 :
OU_4 :
OU_5 :
DateCreated : 2011-11-11
LastLogon : 2019-08-11
ComputerName : 500-AV
SID : 1234-5678-90
OU_1 : Workstations
OU_2 : SecondOU
OU_3 : ThirdOU
OU_4 : FourthOU
OU_5 :
DateCreated : 2001-01-01
LastLogon : 2019-08-08
ComputerName : 666-AV
SID : 777-888-999
OU_1 : Servers
OU_2 : SrvOu2
OU_3 : SrvOu3
OU_4 : SrvOu4
OU_5 : SrvOu5
DateCreated : 1999-12-31
LastLogon : 2019-08-20

Return a selectable list in Powershell

I'm writing a powershell script that will search active directory and give me info on users like this:
$fname = Read-Host 'Enter first name'
$lname = Read-Host 'Enter Last Name'
$search = [adsisearcher]"(&(ObjectCategory=Person)(ObjectClass=User)(givenName=$fname)(sn=$lname))"
$users = $search.FindAll()
foreach($user in $users) {
$displayname = $user.Properties['displayname']
"$displayname"
}
That will return a list of users who have the same first and last names:
User1
User2
User3
User4
I'd then like to be able to select which user I want to show further info on, something like this:
Read-Host 'Enter user number'
#input 1 - 4
#returns exchange server name for the selected user:
$msExchHomeServerName = $user.Properties['msExchHomeServerName']
"Exchange server: $msExchHomeServerName"
I can't figure out how to return a selectable list that lets me get further details on that user.
This works for me. I'm more familiar with PSCustomObjects, but you could do this as a hashtable instead, I'm sure (although I think hashtables get funny if you try and use an integer as a key).
And I'm sorry, I'm too lazy to refactor this with ADSI, but hopefully the logic is clear.
If you wanted to, you could return the user properties you might need with your initial LDAP query and add them to the [PSCustomObject] you're creating for each user. Then just pull the properties out of $usertable rather than doing another AD query. (See the second example.)
However, I'm really adamant that if it's more than a few properties for more than a few users, don't try and grab everything at once. I get really sick of lazy LDAP filters with -properties * when you only want one property. In my environment, if I get all the properties on just my account, it's 74 KB. That starts to add up quickly when dragging stuff out of LDAP.
# sort user list by desired attribute below e.g. samaccountname, lastname, etc
$users = get-aduser -filter 'name -like "macdo*"' | sort samaccountname
# create an array to store the user results
$usertable = #()
$no = 0
$users | foreach {
$no++
$o = [PSCustomObject]#{
No = $no
Name = $_.samaccountname
}
$usertable += $o
}
$usertable |sort no
# this is what the table will look like on screen
No Name
-- ----
1 AKMacDonald
2 AMacDonald
3 BMacDonald
4 BMacdonnell
$myint = Read-Host 'Enter user number'
> Enter user number: 29
# Select desired user properties in Get-ADUser below
$ADuser = $usertable | where {$_.no -eq $myin} |select -ExpandProperty Name |
Get-Aduser -Properties msExchHomeServerName
$ADuser
#AD user output
DistinguishedName : CN=BMacDonald,OU=Accounts,DC=example,DC=com
Enabled : False
GivenName : Bruce
Name : BMacDonald
...
If you're grabbing a few extra attributes with your initial AD query, you can store them in the $usertable for quick retrieval.
# grab msExchHomeServerName in this query
$users = get-aduser -filter 'name -like "macdo*"' -properties msExchHomeServerName |
sort samaccountname
# create an array to store the user results
$usertable = #()
$no = 0
$users | foreach {
$no++
$o = [PSCustomObject]#{
No = $no
Name = $_.samaccountname
Exchsrv= $_.msExchHomeServerName
}
$usertable += $o
}
# do a "select" below if you don't want to display everything in $usertable
$usertable |sort no
# $usertable contents
No Name Exchsrv
-- ---- ----
1 AKMacDonald Exch1
2 AMacDonald Exch1
3 BMacDonald Exch2
4 BMacdonnell Exch3
$myint = Read-Host 'Enter user number'
> Enter user number: 29
# Select desired user properties from $usertable
$usertable | where {$_.no -eq $myint} | Select Name,Exchsrv
# output
Name Exchsrv
---- ----
AKMacDonald Exch1
You have to specify the properties that you'd like to load for each user. [adsisearcher] is a type accelerator for DirectorySearcher, so try this:
$filter = "(&(ObjectCategory=Person)(ObjectClass=User)(givenName=$fname)(sn=$lname))"
$propsToLoad = #('displayname', 'msExchHomeServerName')
$search = [System.DirectoryServices.DirectorySearcher]::new($filter, $propsToLoad)
If you want to avoid user selection error, you can use the PromptForChoice method :
$fname = (Read-Host 'Enter first name').Trim()
$lname = (Read-Host 'Enter Last Name').Trim()
$filter = "(&(samAccountType=805306368)(givenName=$fname)(sn=$lname))"
$propsToLoad = #('displayname','samaccountname', 'mail', 'l', 'department', 'msExchHomeServerName')
$search = [System.DirectoryServices.DirectorySearcher]::new($filter, $propsToLoad)
$results=$search.FindAll()
$results[$Host.UI.PromptForChoice( "Choose the namesake you want to see",`
"Type the corresponding SamAccountName", ($results | ForEach-Object `
{ New-Object System.Management.Automation.Host.ChoiceDescription( `
$_.Properties.samaccountname, $_.Properties.department) }), 0)].Properties.Values
This shows you this once you have typed givenName and surName :
Enter first name: Justine
Enter Last Name: Noix
Choose the namesake you want to see
Type the corresponding SamAccountName
[] JNoix [] JuNoix [?] Aide (la valeur par défaut est « JNoix ») : junoix
JuNoix
Justine Noix
LDAP://CN=Justine Noix,OU=R&D,OU=Tests,DC=someDomain,DC=adds
Justine.Noix.RD#someDomain.adds
R&D
SomeCity
mailserver.someDomain.adds
if you type a wrong samAccountName, it re-asks again.
if you don't care about any help or have no department setted in AD, simply replace the last line by :
$results[$Host.UI.PromptForChoice( "Choose the namesake you want to see",`
"Type the corresponding SamAccountName", ($results | ForEach-Object `
{ New-Object System.Management.Automation.Host.ChoiceDescription( `
$_.Properties.samaccountname, "") }), 0)].Properties.Values

Windows PowerShell Filtering by data range

I have a powershell script to get deactivated accounts from our SSO app but would like to filter it down to only those that were deactivated more than 90 days ago.
I then have another script to take the results and deletes those users from the SSO app.
Can you tell me how to add a filter to the below script to exclude results were the StatusChanged date is greater than 90 days from current date.
$users = oktaListDeprovisionedUsers -oOrg PREV
$toexport = New-Object System.Collections.ArrayList
Foreach ($u in $users)
{
$line = #{
status = $u.status
employeeid = $u.profile.employeeNumber
firstName = $u.profile.firstName
lastName = $u.profile.lastName
email = $u.profile.email
department = $u.profile.department
supervisor = $u.profile.manager
created = $u.created
lastUpdated = $u.lastUpdated
login = $u.profile.login
title = $u.profile.title
GroupName = $u.profile.Group_Name
Organization = $u.profile.organization
Location = $u.profile.workday_location
User_type = $u.profile.userType
StatusChanged = $u.StatusChanged
}
$obj = New-Object psobject -Property $line
$_c = $toexport.Add($obj)
}
#Path for utility will have to be changed to a more generic location.
$toexport | Select-Object "login", "StatusChanged", "employeeid", "firstName","lastName", "email", "title","supervisor","department","Organization","Location", "GroupName" | >Export-Csv -Path "C:\OktaExport\user-list.csv" -NoTypeInformation
You can filter the $users object by a Where-Object
$users = $users | Where-Object{((Get-Date) - $_.StatusChanged).TotalDays -gt 90}
Add this to the 2nd line of your script.