I need to create a CSV that contains all possible emails addresses that an Active Directory user has. The CSV must be in the following format (very rigid API for where their going to be imported to):
Username | EmailAddress1 | EmailAddress2 | EmailAddressN
My script so far looks like this:
$Group = "GroupNAme
$usersObj = #()
$countMax = 0
$GetAdGroup = Get-AdGroup -Identity $Group -Properties *
[array]$members = $GetAdGroup.Members
ForEach ($member in $members) {
$currentUser = get-aduser -Identity $member `
-Properties EmailAddress, ProxyAddresses |
Where {$_.Enabled -eq "True"}
$countTemp = ($currentUser.ProxyAddresses).count
if ($countTemp -gt $countMax){ $countMax = $countTemp}
foreach ($mailAdd in $currentUser.ProxyAddresses) {
$usersOBJ += [pscustomobject]#{
'Username' = $currentUser.SamAccountName;`
'ProxyAddresses' = $mailAdd.SubString(5)
}
}
$usersOBJ | Export-CSV -NoTypeInformation C:\Export.csv
Now my existing Object spits out the Users as follows:
UserName | Emailaddress1
Username | Emailaddress2
Username | EmailsaddressN
I can't seem to make the leap into working out how to create a better object. I can get the max number of ProxyAddresses that occur but I'm not sure how to figure that into building my object and then populating the values of the $currentUser.ProxyAddresses into those columns.
I've read about Hash tables but they don't seem to fit my requirements, usually taking the form:
Username1 | Username2
Emailaddress1 | Emailaddress1
Emailaddress2 | Emailaddress2
Can anyone please point me in the right direction?
Cheers!
You just need to separate the values you're adding a bit. Add the username to a single object before the loop, then add additional properties on an as-needed basis. Finally, add the object to your array to be exported.
Try this:
...
$x = New-Object System.Object
$x | Add-Member –type NoteProperty –Name UserName –Value $currentUser.SamAccountName
$i=0
foreach ($mailAdd in $currentUser.ProxyAddresses) {
$i++
$x | Add-Member –type NoteProperty –Name "Emailaddress$i" –Value $mailAdd.SubString(5)
}
$usersOBJ += $x
Assuming you add all the users to $usersOBJ before exporting the csv, the columns should work perfectly for any number of proxy addresses.
Related
Hi Stackoverflow community, this is my first time posting a question.
I'm also new to powershell but I was able to put together a small script based on other scripts I found and it kind of works.
My goal is to query all mailboxes and output a column with the user and one with the entry for their trustedsendersanddomains.
I would like it to repeat the user when the user has multiple entries and display none in case they don't have any. It would look something like this:
csv
This is what I have. (for testing purposes I'm setting 3 actual users but the commented part works)
$Users = ("user1", "user2", "user3")
#$Users = Get-Mailbox | select -ExpandProperty Alias
$objfull = #()
Foreach ($User in $Users)
{
$Junk = Get-MailboxJunkEmailConfiguration $User
$Emails = Get-MailboxJunkEmailConfiguration $User | select -ExpandProperty TrustedSendersAndDomains
$obj = New-Object System.Object
Foreach ($Email in $Emails)
{
$obj | Add-Member NoteProperty User $User
$obj | Add-Member NoteProperty Trusted_Senders $Junk.TrustedSendersAndDomains
$objfull += $obj
}
}
$objfull | Export-Csv C:\Users\Leo\export.csv -NoTypeInformation
While it will throw error for users without entries this works in repeating the user, but the trusted sender output is the whole array and it's space separated, not comma.
This other one I was testing with will write to terminal the way I want the csv to be.
$Users = ("user1", "user2", "user3")
#$Users = Get-Mailbox | select -ExpandProperty Alias
$objfull = #()
Foreach ($User in $Users)
{
$Junk = Get-MailboxJunkEmailConfiguration $User
$Emails = Get-MailboxJunkEmailConfiguration $User | select -ExpandProperty TrustedSendersAndDomains
$obj = New-Object System.Object
Foreach ($Email in $Emails)
{
Write-Host $Email, $User
}
}
#$objfull | Export-Csv C:\Users\Leo\export.csv -NoTypeInformation
I appreciate the help in advance!
I am trying to get a powershell script to export all users in an OU and sub OUs which I can do fine, but when I try to get the user's OU, I get nothing for the OU. I have looked all over online and found a few scripts that pull just the user's OU, but they are a little slow and I can't seem to get them to pull groups or is for pulling from one group instead of listing all users and their groups.
I am trying to export this list and sort by OU so that I can ensure each student is in the proper groups. We have had a few students that were in extra groups and I want a quick and easy look to find those students.
#Student
$Report = #()
#Collect all users
$Users = Get-ADUser -Filter * -SearchBase 'OU=Student,DC=domain,DC=com' -Properties distinguishedname, Name, GivenName, SurName, SamAccountName, UserPrincipalName, MemberOf, Enabled -ResultSetSize $Null
# Use ForEach loop, as we need group membership for every account that is collected.
# MemberOf property of User object has the list of groups and is available in DN format.
Foreach($User in $users){
$UserGroupCollection = $User.MemberOf
#This Array will hold Group Names to which the user belongs.
$UserGroupMembership = #()
#To get the Group Names from DN format we will again use Foreach loop to query every DN and retrieve the Name property of Group.
Foreach($UserGroup in $UserGroupCollection){
$GroupDetails = Get-ADGroup -Identity $UserGroup
#Here we will add each group Name to UserGroupMembership array
$UserGroupMembership += $GroupDetails.Name
}
#As the UserGroupMembership is array we need to join element with ',' as the seperator
$Groups = $UserGroupMembership -join ','
#Creating custom objects
$Out = New-Object PSObject
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value #{Name="DistinguishedName";Expression={$_.distinguishedname | ForEach-Object {$_ -replace '^.+?(?<!\\),',''}}}
$Out | Add-Member -MemberType noteproperty -Name Name -Value $User.Name
$Out | Add-Member -MemberType noteproperty -Name UserName -Value $User.SamAccountName
$Out | Add-Member -MemberType noteproperty -Name Status -Value $User.Enabled
$Out | Add-Member -MemberType noteproperty -Name Groups -Value $Groups
$Report += $Out
}
#Output to screen as well as csv file.
$Report | Sort-Object DistinguishedName | FT -AutoSize
$Report | Sort-Object DistinguishedName | Export-Csv -Path $env:temp\students.csv -NoTypeInformation
There you go, I added some comments to help you understand the thought process.
This should be a lot faster than what you were doing.
The problem while adding your OUs was here:
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value #{Name="DistinguishedName";Expression={$_.distinguishedname | ForEach-Object {$_ -replace '^.+?(?<!\\),',''}}}
Which should've been:
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value ($user.distinguishedname -replace '^.+?(?<!\\),','')
#Student
$Report = [system.collections.generic.list[pscustomobject]]::new()
# Using Collection.Generic.List instead of System.Array for efficiency
#Collect all users
$Users = Get-ADUser -Filter * -SearchBase 'OU=Student,DC=domain,DC=com' -Properties MemberOf
# -> Use -SearchScope Subtree if you want to go all the way down in OU recursion starting from the 'OU=Student'
# -> distinguishedname, Name, GivenName, SurName, SamAccountName, UserPrincipalName and Enabled are Default Properties
# of Get-ADUser, no need to call them.
# -> -ResultSetSize $Null is default for Get-ADUSer, no need to call it
# Use ForEach loop, as we need group membership for every account that is collected.
# MemberOf property of User object has the list of groups and is available in DN format.
Foreach($User in $users)
{
#This Array will hold Group Names to which the user belongs.
$UserGroupMembership = [system.collections.generic.list[string]]::new()
#To get the Group Names from DN format we will again use Foreach loop to query every DN and retrieve the Name property of Group.
Foreach($UserGroup in $User.MemberOf)
{
# $GroupDetails = Get-ADGroup -Identity $UserGroup
# -> Instead of this, we can do some string manipulation
# which will be a lot faster and give you the same results.
$UserGroupMembership.Add($UserGroup.Split(',OU=')[0].replace('CN=',''))
}
#As the UserGroupMembership is array we need to join element with ',' as the seperator
$Groups = $UserGroupMembership -join ','
#Creating custom objects
<#
$Out = New-Object PSObject
$Out | Add-Member -MemberType noteproperty -Name DistinguishedName -Value #{Name="DistinguishedName";Expression={$_.distinguishedname | ForEach-Object {$_ -replace '^.+?(?<!\\),',''}}}
$Out | Add-Member -MemberType noteproperty -Name Name -Value $User.Name
$Out | Add-Member -MemberType noteproperty -Name UserName -Value $User.SamAccountName
$Out | Add-Member -MemberType noteproperty -Name Status -Value $User.Enabled
$Out | Add-Member -MemberType noteproperty -Name Groups -Value $Groups
$Report += $Out
-> Again, Add-Member is highly inefficient compared to casting PSCustomObject
-> += is evil ( •̀ᴗ•́ )و ̑̑
#>
$Report.Add(
[pscustomobject]#{
OrganizationalUnit = ($user.DistinguishedName -replace '^.+?(?<!\\),','')
Name = $user.Name
UserName = $user.sAMAccountName
Status = $user.Enabled
Membership = $Groups
})
}
#Output to screen as well as csv file.
$Report | Sort-Object OrganizationalUnit | FT -AutoSize
$Report | Sort-Object OrganizationalUnit | Export-Csv -Path $env:temp\students.csv -NoTypeInformation
I don't know how many users you have but every time you += on an array the entire array plus the new element is copied to a completely new array. This is a bad practice and gets exponentially worse with every item added the array. You can avoid this by building the arrays as a loop result or by using dotnet list object with an efficient add() method.
You also look up the same group names repeatedly. I don't know the numbers but it's probably a lot better to put all your groups in a hashtable once and then look them up.
Your question is unclear, but if you want a list of users and their groups, you are going the long way around. You mention the ou but AFAICS there is no org unit used in the code. Do you want the AD ou property or a part of the DN? You don't seem to be using either.
Note that the DN is a string and sorting by DN will just give an alpha string sort which is not helpful. Are your students in separate org units under OU=students ? This is not clear. If so, use the AD canonicalName to sort the list.
No need to include default properties in -property. Splatting is nice.
You should improve your question by indicating what your AD structure looks like and what you think your output should look like.
Also, format your code for readability.
You want something along these lines:
# group hashtable, for efficient name lookup
$groupName = #{}
$ignoredGroups = #( 'AllStudents','AllUsers', 'etc' ) # don't clutter list with these groups
Get-AdGroup -filter '*' | # any restrictions? searchbase, etc
ForEach-Object {
if ( $ignoredGroups -notcontains $_.Name ) {
$groupName[ $_.distinguishedName ] = $_.Name
}
}
# ADsplat, for readability
$AD_Splat = #{
Filter = '*'
SearchBase = 'OU=Student,DC=domain,DC=com'
Properties = 'MemberOf,CanonicalName,sn,givenName'.split(',') # split to array
ResultSetSize = $Null # !? also, there are system limits to size
}
$results = Get-ADUser #ad_splat |
ForEach-Object {
$DN = $_.distinguishedName # do you need this at all?
$CName = $_.canonicalName # for sorting by AD org unit
$XName = $_.sn + ', ' + $_.givenName
if ( $_.Enabled ) { $Enabled = 'Y'} else { $Enabled = '.' }
$groups = (
$_.memberOf |
ForEach-Object { $GroupName[ $_ ] } | # lookup name
where-Object { $_ } | # ignore nulls (when group not in hashtable)
sort-object # consistent ordering between users
) -join ';' # don't use comma, csv conflict
# leave custom object in pipe! This builds the array efficiently.
New-Object PSObject -Property #{
DistinguishedName = $dn
Name = $_.name
XName = $XName
Login = $_.SamAccountName
CName = $CName
Groups = $Groups
}
} | Sort-Object CName # sort the objects by canonical name
$results | format-table
$results | Export-Csv -Path 'c:\temp\usersgroups.csv' -NoTypeInformation
I am currently trying to do an Out-GridView to get a simple overview about our group policy objects. To do so, I am using the Get-GPO cmdlet, like so:
Get-GPO -all |
Select-Object -Property DisplayName, Description |
Sort-Object -Property DisplayName |
Out-GridView
In our company we use the first line of the description field to store the name of the admin who created the policy, and all following lines hold a short description.
I would want to be able to grab the first line of the the Description field with the column header Responsability and all other lines of the field in a separate column. So assuming my current code would give me a table like this:
DisplayName | Description
-------------------------
GPO1 | Username
| stuff
| stuff
I would want it to look like this:
DisplayName | Responsability | Description
------------------------------------------
GPO1 | Username | stuff
| | stuff
How can I achieve this?
As #Matt suggested, you can use a calculated property.
Then since Description is a string, rather than an array of strings, you will need to split the line at the line breaks. This can be done by using -split and since it's information from a GPO we can assume Windows line endings `r`n (Otherwise you could use [environment]::newline)
The first property, use array element [0] will be the first line. For the second property, we'll need to save the array in a variable. Then we can use the length of that variable to get first element through the last.
Get-GPO -all |
Select-Object -Property DisplayName, #{
Name = "Responsibility"
Expression = {($_.Description -split "`r`n")[0]}
}, #{
Name = "Description"
Expression = {
$linearray = ($_.Description -split "`r`n")
$linearray[1..($linearray.length - 1)] | Out-String
}
} |
Sort-Object -Property DisplayName |
Out-GridView
Alternatively, you could create a new object rather than using the calculated property.
Get-GPO -all |
ForEach-Object {
$linearray = ($_.Description -split "`r`n")
[pscustomobject]#{
"DisplayName" = $_.DisplayName
"Responsibility"= $linearray[0]
"Description" = $linearray[1..($linearray.length - 1)] | Out-String
}
} |
Sort-Object -Property DisplayName |
Out-GridView
The first thing to understand is what Get-GPO is returning: an array of objects, each of which has a set of properties.
What is displayed in your table is a series of rows (one per object), with the columns being the values of the properties for that object.
Therefore if you want a new column, you need a new property.
There are two ways you can do this: create a calculated property with Select-Object or add a property to the objects via Add-Member.
Calculated
You may provide a hashtable as a property to Select-Object, and the hashtable must have two keys:
Name (the name of the property)
Expression (a scriptblock that will be executed to determine the value, where $_ refers to the object itself)
Get-GPO -all |
Select-Object -Property DisplayName, Description, #{
Name = 'Responsibility'
Expression = {
($_.Description -split '\r?\n')[0] # First line
}
} |
Sort-Object -Property DisplayName |
Out-GridView
New Member
You can use a ScriptProperty that will execute a scriptblock each time the property is called on the object. Use $this to refer to the object in this context.
Get-GPO -all |
Add-Member -MemberType ScriptProperty -Name Responsibility -Value {
($this.Description -split '\r?\n')[0] # First line
} -Force -PassThru |
Select-Object -Property DisplayName, Responsibility, Description |
Sort-Object -Property DisplayName |
Out-GridView
I would probably use something like this:
Get-GPO -All | ForEach-Object {
$info = $_.Description
$pos = $info.IndexOf([Environment]::NewLine)
if ( $pos -gt 0 ) {
$responsibility = $info.Substring(0,$pos)
$description = $info.Substring($pos + [Environment]::NewLine.Length)
}
else {
$responsibility = ""
$description = $info
}
[PSCustomObject] #{
"DisplayName" = $_.DisplayName
"Responsibility" = $responsibility
"Description" = $description
}
}
This way you can preserve formatting.
I have the below working script that checks if a large list of users in a CSV file are a member of an AD group and writes the results to results.csv.
Not sure how to convert the script so I can change $group = "InfraLite" to $group = DC .\List_Of_AD_Groups.CSV.
So the script doesn't just return matches for one AD group but so it returns matches for the 80 AD groups contained in the List_of_AD_groups.csv also. Writing a YES/NO for each AD group in a new column in the CSV (or if that's not possible creating a seperate .csv file for each group with results would do also.
I could do this manually by changing the value of $group and export file name, and re-running the script 80 times but must be a quick was with PS to do this?
e.g. results.csv:
NAME AD_GROUP1 AD_GROUP2 AD_GROUP80 etc etc.
user1 yes no yes
user2 no no yes
user3 no yes no
echo "UserName`InfraLite" >> results.csv
$users = GC .\user_list.csv
$group = "InfraLite"
$members = Get-ADGroupMember -Identity $group -Recursive |
Select -ExpandProperty SAMAccountName
foreach ($user in $users) {
if ($members -contains $user) {
echo "$user $group`tYes" >> results.csv
} else {
echo "$user`tNo" >> results.csv
}
}
I played with this for a while, and I think I found a way to get you exactly what you were after.
I think Ansgar was on the right path, but I couldn't quite get it to do what you were after. He mentioned that he didn't access to an AD environment at the time of writing.
Here is what I came up with:
$UserArray = Get-Content 'C:\Temp\Users.txt'
$GroupArray = Get-Content 'C:\Temp\Groups.txt'
$OutputFile = 'C:\Temp\Something.csv'
# Setting up a hashtable for later use
$UserHash = New-Object -TypeName System.Collections.Hashtable
# Outer loop to add users and membership to UserHash
$UserArray | ForEach-Object{
$UserInfo = Get-ADUser $_ -Properties MemberOf
# Strips the LPAP syntax to just the SAMAccountName of the group
$Memberships = $UserInfo.MemberOf | ForEach-Object{
($_.Split(',')[0]).replace('CN=','')
}
#Adding the User=Membership pair to the Hash
$UserHash.Add($_,$Memberships)
}
# Outer loop to create an object per user
$Results = $UserArray | ForEach-Object{
# First create a simple object
$User = New-Object -TypeName PSCustomObject -Property #{
Name = $_
}
# Dynamically add members to the object, based on the $GroupArray
$GroupArray | ForEach-Object {
#Checking $UserHash to see if group shows up in user's membership list
$UserIsMember = $UserHash.($User.Name) -contains $_
#Adding property to object, and value
$User | Add-Member -MemberType NoteProperty -Name $_ -Value $UserIsMember
}
#Returning the object to the variable
Return $User
}
#Convert the objects to a CSV, then output them
$Results | ConvertTo-CSV -NoTypeInformation | Out-File $OutputFile
Hopefully that all makes sense. I commented as much of it as I could. It would be very simple to convert to using ADSI if you didn't have RSAT installed on whatever machine you're running this on. If you need that let me know, and I'll make some quick modifications.
I've also tossed a slightly modified version of this in a Gist for later reference.
The trivial solution to your problem would be to wrap your existing code in another loop and create an output file for each group:
$groups = Get-Content 'C:\groups.txt'
foreach ($group in $groups) {
$members = Get-ADGroupMember ...
...
}
A more elegant approach would be to create a group mapping template, clone it for each user, and fill the copy with the user's group memberships. Something like this should work:
$template = #{}
Get-Content 'C:\groups.txt' | ForEach-Object {
$template[$_] = $false
}
$groups = #{}
Get-ADGroup -Filter * | ForEach-Object {
$groups[$_.DistinguishedName] = $_.Name
}
Get-ADUser -Filter * -Properties MemberOf | ForEach-Object {
$groupmap = $template.Clone()
$_.MemberOf |
ForEach-Object { $groups[$_] } |
Where-Object { $groupmap.ContainsKey($_) } |
ForEach-Object { $groupmap[$_] = $true }
New-Object -Type PSObject -Property $groupmap
} | Export-Csv 'C:\user_group_mapping.csv' -NoType
I currently use this script to obtain the SID of a user from AD. Not that each time I need an SID, I have to open the script and type the persons username in, which when I have 100's to do can be frustrating. The current script is as follows:
$name = "username"
(New-Object System.Security.Principal.NTAccount($name)).Translate([System.Security.Principal.SecurityIdentifier]).Value
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Is there a way that I can use the same script but put the AD usernames in a textfile and pull them into powershell and have the output come out like so I get the username and the SID. Ideally into CSV format?
Cheers,
Assuming you have a list of usernames, each on its own row in userlist.txt, the process is not too complicated.
# Array for name&sid tuples
$users = #()
cat C:\temp\userlist.txt | % {
# Syntax sugar
$spSid = [Security.Principal.SecurityIdentifier]
# Custom object for name, sid tuple
$user = new-object psobject
$user | Add-Member -MemberType noteproperty -name Name -value $null
$user | Add-Member -MemberType noteproperty -name Sid -value $null
$user.Name = $_
$user.Sid = (New-Object Security.Principal.NTAccount($_)).Translate($spSid).Value
# Add user data tuple to array
$users += $user
}
# Export array contents into a CSV file
$users | Select-Object -Property Name,Sid | Export-Csv -NoTypeInformation C:\temp\sidlist.txt