Formatting MemberOf string in Get-AdUser - powershell

I am running a simple script to list all of my domain users, the last time they logged on, whether or not their accounts are enabled, and what groups they are a member of.
I have two problems. One, the lastLogon value is not a date/time format. Two, more importantly, the MemberOf column lists everything about the group object, when all I really want is the CN value. Moreover, for users in several groups, some of the information is truncated.
How do I fix this?
This is the script I am running:
Get-AdUser -Filter * -Properties Name,Description,Enabled,lastLogon,MemberOf | FT Name,Description,Enabled,lastLogon,MemberOf -autosize | format-list | out-string -width 4096 | Out-File C:\test.txt

So, first I would suggest outputting the info as a CSV rather than try to get it out into a text file. Trying to get all the accounts on one line is very impractical. For instance my user object is in 133 different groups in my environment.
Second, I would give up on making this a one liner and use an actual script to build custom objects that you can the export to CSV. Here is some top of the head code to get you going in the right direction. I'll make it actual functional tested code later if I have a chance.
$results = #()
$users = Get-ADUser -Filter * -Properties Name,Description,Enabled,lastLogon,MemberOf
foreach($user in $users){
$lastlogon = [datetime]::FromFileTime($user.lastlogon)
foreach($group in $user.MemberOf){
$temp = New-Object PSCustomObject -Property #{'Name' = $user.Name;
'Description' = $user.Description;
'Enabled' = $user.Enabled;
'LastLogon' = $lastlogon;
'Group' = '';
}
$temp.Group = $group.Split(',')[0]
$results += $temp
}
}
$results | Export-CSV C:\Temp\SomeFile.csv -NoTypeInformation
Tested it and that code worked for me.

Related

Powershell Script to pull all AD users and last time password was changed and date of change

I am attempting to create a script to extract all AD users from 3 different domains with their last logon date as well as the last time they changed their password and extract it to a CSV. I have the following code:
$data = #()
$domains = "example.exa.com "," example.com",” ex.example.com”
foreach($domain in $domains)
{
$data | Foreach-Object –Process { get-aduser -filter * -server $domains -properties passwordlastset,lastlogondate | select name, passwordlastset,lastlogondate }
$data | Export-csv –Path C:\passwords.csv -notypeinformation
The following code does not produce any errors, but it runs infinitely with no results. Can anyone help with what I am doing wrong?
There are quite a few things wrong with your code:
You define the three domains with leading or trailing spaces
You loop through the $domains using iterating variable $domain, but in the loop you are using the complete array $domains
$data is defined as (empty) array on top, but still you use it to loop through its elements (which aren't there) by piping to Foreach-Object
Try to avoid adding items to an array with += as it needs to reconstruct the entire array in memory on every addition
Try:
$domains = 'example.exa.com','example.com','ex.example.com'
$data = foreach($domain in $domains) {
# just output the objects here, so they will be collected for you in variable $data
Get-ADUser -Filter * -Server $domain -Properties PasswordLastSet, LastLogonDate | Select-Object Name, PasswordLastSet, LastLogonDate
}
$data | Export-csv –Path 'C:\passwords.csv' -NoTypeInformation

Export user from multiple groups

Good Morning Guys,
I write this script to export multiple users in multiple groups in a CSV file.
In addition to not exporting anything, I cannot insert a column in the CSV where the user's appatenency group is specified. Just the one with the name "vpn-*", like vpn-users
Powershell Version 4.0
$Data = $UserData = #()
$GroupName = Get-ADGroup -Filter {name -like "VPN-*"} -Properties Name | Select-Object Name
Foreach ($group in $GroupName)
{
$UserData = Get-ADGroupMember -Identity {$group}
Where objectClass -eq 'user' |
Get-ADUser -Properties Name, UserPrincipalName, description, Enabled |
Select-Object Name, UserPrincipalName, description, Enabled
}
$UserData | export-csv "c:\members2.csv"
I see a number of issues with the posted code. Some are mere preferences, but others look like they would affect the outcome.
-Properties Name isn't needed in the initial Get-ADGroup command. The Name property is returned by default.
Respectful to #Theo's comment re: -ExpandProperty, however, I prefer not to use that unnecessarily. You don't even need the first Select-Object command, just reference the property and it will unroll and return a string array. Property unrolling is supported as of PowerShell Version 3.
Your argument to the -Filter parameter should be a string. I myself have a hard time breaking the habit of wrapping those queries in script blocks {...}, however, there are some specifics of the AD cmdlets, and using a string is a best practice. Basically, the argument takes a string type, if you give it a script block it will convert it to a string and that process may not always work as desired.
It's better to store your AD properties in an array you can cite multiple times. Normally you would've only needed to add 'Description' but considering you were also using it in a later Select-Object command it makes more sense to add all the desired properties to an array.
Personally, I prefer not to use simplified syntax. I don't know what the community's opinion is on that but I rewrote using my preference.
You were also missing |' before the Where-Object command.
And, yes you would have to move the Export-Csv inside the loop to output data per loop iteration. Using the -Append parameter to avoid overwriting it on every iteration.
Untested Example:
$ADProps = 'Name', 'UserPrincipalName', 'description', 'Enabled'
$GroupName = (Get-ADGroup -Filter "name -like 'VPN-*'" ).Name
Foreach ( $Group in $GroupName )
{
$UserData =
Get-ADGroupMember -Identity $Group |
Where-Object{ $_.objectclass -eq 'user' } |
Get-ADUser -Properties $ADProps |
Select-Object ($ADProps += #{Name = 'Group'; Expression = { $Group }})
$UserData | export-csv "c:\members2.csv" -NoTypeInformation -Append
}

Powershell - Only show count of users if OU has users to count

I've tried this a few ways and have come very, very close but no cigar.
What I need help with is creating a report that shows the number of users in each OU. I know there are several ways to do this, but each method I've tried has given me varying degrees of usability. I would really appreciate some feedback on what I can do to improve my queries.
My first attempt was creating an object table, but unfortunately my knowledge of object tables isn't that great. The output was one large table of all the child OU's and their counts, but since a lot of our child OU's have the same name it wasn't any good. It may be possible to add another member on for the parent OU, but I'm not sure how to do that.
With this same attempt I also tried Out-Host and while this did break up the mass table into individual tables I was still stuck with the child OU name issue.
$OUName_ou = Get-ADOrganizationalUnit -filter * -searchbase ‘OU=Users,DC=business,DC=com’ -SearchScope 1
$OUName = #()
foreach ($ou in $OUName_ou) {
$Query = New-Object PSObject
$Query | Add-Member -membertype NoteProperty -Name OU_Name -Value “”
$Query | Add-Member -membertype NoteProperty -Name User_Count -Value “”
$Query.OU_Name = $ou.name
$users = get-aduser -Filter * -SearchBase $ou
$numb = $users | measure
$Query.User_Count = $numb.count
$OUName += $Query
}
$OUName
My next attempt was to try just for a list view, but the output is very muddled. It generates a list that shows the drill down for each OU, but since there are other items there I receive a long list with a lot of 0 counts. Because of the list view, and the number of items, the report is difficult to get relevant data from. If I could find a way to clean up the output it would work fine for my needs. Ideally there would be a way to modify this to exclude empty OU's and specific OU's (like test groups for example) then it would work well enough. I'm positive there is an if command I could use to do it, but so far I haven't found the right syntax.
$ous = Get-ADOrganizationalUnit -Filter * -SearchBase "ou=Users,ou=CMSG,dc=contoso,dc=com" | Select-Object -ExpandProperty DistinguishedName
$ous | ForEach-Object{
[psobject][ordered]#{
OU = $_
Count = (Get-ADUser -Filter * -SearchBase "$_").count
}
}
You could simply get the count value before outputting the PsCustomObject and only output that if the count is greater than 0
Something like:
$ous = Get-ADOrganizationalUnit -Filter * -SearchBase "ou=Users,ou=CMSG,dc=contoso,dc=com"
$result = $ous | ForEach-Object {
$count = (Get-ADUser -Filter * -SearchBase $_.DistinguishedName).Count
if ($count) {
[PsCustomObject]#{
OU = $_.DistinguishedName
Users = $count
}
}
}
# output as table on screen
$result | Format-Table -AutoSize
# output to CSV you can open in Excel
$result | Export-Csv -Path 'D:\Test\UserCount.csv' -UseCulture -NoTypeInformation
Use [PsCustomObject] here, because it is ordered by default

Powershell - create CSV of users with CRM licences who haven't logged in for more than 30 days

I'm trying to combine the output from two commandlets. I need to list all the users with a particular licence SKUID (CRMPLAN) who haven't logged in for over 30 days.
Is something along these lines even close?
$UserList = get-MSOLUser -All | where {$_.isLicensed -eq "TRUE" -and $_.Licenses.AccountSKUID -eq "Domain:CRMPLAN"}
$MasterList = #()
foreach ($DisplayName in $UserList) {
$MyObject = New-Object PSObject -Property #{
LastLogonTime = (Search-ADAccount -AccountInactive -TimeSpan 30.00:00:00 | where {$_.ObjectClass -eq 'user'} | FT Name,ObjectClass –A}).LastLogonTime
}
$MasterList += $MyObject
}
$MasterList | select displayname,IsLicenced,LastLogonTime - export-CSV C:\LastLogonDate.csv
Ok so right now your basically getting a list of users in $userList and then getting a list of ALL of the last logon times of users who have not logged in within the last 30 days for each of them and adding it to an array, then trying to do some selects on data that is not there since your only selecting the lastlogontime, which does not actually exist in AD. What you need to do instead is loop through them and check their lastlogondate to see if they have logged in within the last 30 days and then add their info to the array if they have not(you can also do this by collecting both arrays and then processing using group-object but that's a lot more tricky). You also have to remember that if the property you are selecting is not part of the object you are working with in the pipeline you will need to use an expression in your select to stuff it into the object
So your foreach loop would look something like this.
$masterList = #()
foreach($user in $userList){
$oldDate = (get-date).AddDays(-30)
$userObj = Get-ADUser -Identity $user.UserPrincipalName -Properties lastlogondate | Select-Object displayname,LastLogonDate,#{Name="IsLicensed"; Expression={$user.isLicensed}}
if($userObj.lastLogonDate -le $oldDate){
$masterList += $userObj
}
}
Finally make sure that when you do your export to use a pipe rather than a subtraction symbol. so your last line should look like
$masterlist | export-csv c:\tmp\lastlogondate.csv
Note that there is no need to do the select here since we have already done a select to get the data we need inside of the loop, also try to avoid placing files on the root of C:\, any machine with UAC enabled will throw permissions errors when trying to do that unless you are running as an admin, which is unneeded for this script. Hope this helps and please let me know if you have any questions.

How do I get Powershell to output Get-ADUser -Filter * as comma delimited?

I have a simple Powershell script that I want to use to grab all the users in AD and show specific properties. Here is the heart of my script:
$id = "*"
Get-ADUser -Filter {SAMAccountName -like $id} -Properties * | Select-Object -Property SAMAccountName,Name,PasswordNeverExpires,LockedOut,PasswordLastSet,LastLogOnDate,CanonicalName
The full script has an input parameter to set $id so that it can be a single ID, a list of IDs (such as "bsmith, jdoe, gmanning"), or * to get all accounts.
Here's my problem: When using, *, I need this to output as comma delimited.
Here's the catch: The output cannot be to a CSV file--or any other type of file.
The reason being is that I'm writing this script to be used on N-Enable's N-Central monitoring suite of software. You don't need to know N-Central to help with my problem, just understand that N-Central uses its own software to run Powershell scripts on clients and returns the results into a txt file that cannot be changed to a csv file (or formatted in any other way than what it has hard-coded).
What this means is that my results have to either be what would show up on the screen or in a variable (such as $results=Get-ADUser -Filter * ....). I cannot output to any type of file, which leaves out Export-CSV as an option.
I've tried other types of formatting to no avail (such as with -f). The issue seems to be with the way Powershell grabs all AD Users using the * wildcard. It seems to grab them all as one big object, so I am unable to get my output to have a comma between all the properties so that I get a comma-delimited output. Thus, when I get the results back from N-Central as a .txt file, all the data is there, but there are no commas in between the properties for me to then open the .txt file as comma-delimited in Excel (or tab-delimited for that matter).
Does anyone have a solution that will allow me to format Get-ADUser -filter * so that it is comma-delimited without using export to file?
UPDATE: Ok, I thought I was keeping things easy by not posting my full script but it seems I've done the opposite. So, below is my full script. Anyone should be able to run this to see the results:
function Get-ADUserInfo
{
[CmdletBinding()]
param(
#[Parameter(Mandatory=$True)]
[string[]]$Users= '*'
)
Begin {
$maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
$Headers="ID, Name, Password Never Expires?, Locked Out, Password Last Set, Expiry Date, Last Logon Date, OU Path"
}
Process {
foreach ($id in $Users)
{
$results=Get-ADUser -Filter {SAMAccountName -like $id} -Properties * | select -property SAMAccountName,Name, PasswordNeverExpires,LockedOut,PasswordLastSet,(#{Expression={$_.PasswordLastSet.AddDays($maxPasswordAge)}}), LastLogOnDate,CanonicalName | `
ConvertTo-CSV -NoTypeInformation
}
}
End {
$Headers
$results
}
}
Get-ADUserInfo -Users
Some notes:
When calling Get-AdUserInfo -Users, the script needs to work by entering a single ID, *, or multiple IDs separated by a comma when using the -Users parameter.
Using ConvertTo-CSV solved my biggest problem, comma separated output,thanks all.
I'd like to get rid of the headers that are auto-created as well ("SAMAccountName","Name","PasswordNeverExpires","LockedOut","PasswordLastSet","$_.PasswordLastSet.AddDays($maxPasswordAge)","LastLogOnDate","CanonicalName"). How can I do that? I've tried -skip 1 but that doesn't work with * and removes everything (including the data) if used with a single ID or IDs separated with commas. I can't get -ExpandProperty to work either. Adding format-table -hidetableheaders at the end doesn't do anything as well
So you could use something like this then
$props = "SAMAccountName","Name","PasswordNeverExpires","LockedOut","PasswordLastSet","LastLogOnDate","CanonicalName"
$results = Get-ADUser -Filter * -Properties $props | Select $props | ConvertTo-Csv -NoTypeInformation
This uses the $props array to make the actual query readable and enforce property order. Most of those properties are returned by default, like samaccountname, so it is not required to specify them but no harm done.
$results would contain a quoted comma delimited output. If the quotes bother you they should be removed with a simple -replace
$results = $results -replace '"'
Aside
As others have mentioned you are wasting time using -Properties * you should only return the properties that you need. Which is, again, why I used $props.
Update from comments
If you want to remove the header column you just need to -Skip that from the output. At the end of the code that populates results just add Select-Object
$results = Get-ADUser -Filter * -Properties $props | Select $props | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1
Also to cover your calculated property the easiest way that I have found so far is to add another Select-Object. Not the way I would want to but for now it works.
$results = Get-ADUser -Filter * -Properties $props |
Select $props | Select-Object *,#{L="pWD";Expression={$_.PasswordLastSet.AddDays($masPasswordAge)}} |
ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1
Game Changer
So you made some pretty big changes to your example by including your function. Try this on for size now.
function Get-ADUserInfo
{
[CmdletBinding()]
param(
#[Parameter(Mandatory=$True)]
[string[]]$Users= '*'
)
Begin{
$results = #()
$maxPasswordAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
$Headers= #{Label="ID";Expression={$_.SAMAccountName}},
#{Label="Name";Expression={$_.Name}},
#{Label="Password Never Expires?";Expression={$_.PasswordNeverExpires}},
#{Label="Locked Out";Expression={$_.LockedOut}},
#{Label="Password Last Set";Expression={$_.PasswordLastSet}},
#{Label="Expiry Date";Expression={$_.PasswordLastSet.AddDays($maxPasswordAge)}},
#{Label="Last Logon Date";Expression={$_.LastLogOnDate}},
#{Label="OU Path";Expression={$_.CanonicalName}}
}
Process{
foreach ($id in $Users){
$results += Get-ADUser -Filter {SAMAccountName -like $id} -Properties PasswordNeverExpires,LockedOut,PasswordLastSet,LastLogOnDate,CanonicalName | select $Headers
}
}
End{
$results | ConvertTo-CSV -NoTypeInformation
}
}
Get-ADUserInfo -Users
$Headers= contains a collection of calculate properties most of which are there the change the header. It does not need to be done this was and you could have just manually added a header line before the output of contents. This solution however is more in line with what PowerShell is capable of.
We collect all of the users in the array $results and in the End block convert that to CSV output which would not contain your custom headers.
$id = "*"
$userlist = Get-ADUser -Filter {SAMAccountName -like $id} -Properties SAMAccountName,Name,PasswordNeverExpires,LockedOut,PasswordLastSet,LastLogOnDate,CanonicalName | ConvertTo-CSV -NoTypeInformation
$userlist will be an array of users stored in CSV formatted lines
Edit: Combined -Properties * | Select [propertylist] and get rid of the pull of all properties.
Edit: included -NoTypeInformation so that the output is pure CSV.
Note: Something should be done about the filter.