Format-Table not displaying a columns data - powershell

I am using the powershell command below to read a csv file and match the computer name to the employee name where the last login date is 181 days old. For some reason the Employee_Name column in the output is only displaying {} on each row. Any idea why its not returning the employee name?
Import-Module ActiveDirectory
$Days = (Get-Date).AddDays(-181)
$Computers = #{}
Import-CSV -Path c:\PS\ComputerNames.CSV | % { $Computers[$_.Computer_Name] = $_.Employee_Name }
Get-ADComputer -Property Name,lastLogonDate -Filter {lastLogonDate -lt $Days} -Server servername -Searchbase "OU=US,DC=Domain,DC=net" | ? { $Computers.Keys -contains $_.Computer_Name } | select Name,lastLogonDate,#{n='Employee_Name';e={$Computers[$_.Computer_Name]}} | ft

When you are looking at it in the debugger have you tried piping that field to Get-Member? I suspect that it is the array that Format-Table is not expanding but the debugger is able to unwind when you mouse over it. You are defining $Computers as an array. Maybe you only care about the first element in the array and you can define 'Employee_Name' as the first element of the array instead of the full array (of maybe just one element).

Related

powershell how do I Create/format a dynamic list from a group membership to use in a for each loop

bit of a noob question.
I have the following cmd which grabs the server members of a group which I can copy into a text list. however as the group changes I need to modify the text list manually.
Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | select name
when I have that output in a text list, the following works fine.
$listpath = "C:\Scripts\servers.txt"
[System.Collections.ArrayList]$list = #(Get-content $listpath)
foreach($ComputerName in $list)
{
Get-Uptime -ComputerName $ComputerName
I want to know if it is possible to use a variable that I can use again in a for each loop. I've tried to do so, however the format of the list is not the same when is goes into a variable, thus the function (get-uptime) against the server doesn't work, anyone know what I can do to format the output so I only get the server name?
EG.
$WSUS_7PM = Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | select name
PS C:\Windows\system32> $WSUS_7PM
name
----
AXXXXX003
BXXXXX005
CXXXXX006
DXXXXX007
PS C:\Windows\system32> foreach($Name in $WSUS_7PM) {Write-Host $Name}
#{name=AXXXXX003}
#{name=BXXXXX005}
#{name=CXXXXX006}
#{name=DXXXXX007}
so when I run the same cmds as above modified with the variable instead of the text list, I get the following as the server name is obviously incorrect.
$listpath = $WSUS_7PM
[System.Collections.ArrayList]$list = #(Get-content $WSUS_7PM)
foreach($ComputerName in $list)
{
Get-Uptime -ComputerName $ComputerName
WARNING: Unable to connect to #{name=AXXXXX003}
WARNING: Unable to connect to #{name=BXXXXX005}
I hope that makes sense to someone, appreciate the help in understanding what the difference is in the object output.
Thanks
Alzoo
When you use Select-Object name you are creating a list of objects with a name property. You can either expand it ahead of time
$WSUS_7PM = Get-AdGroupMember -identity "Reboot 7pm" | Sort-Object | Select-Object -ExpandProperty name
or reference the name property later
foreach($Name in $WSUS_7PM.name) {Write-Host $Name}

Multiple rows in a grid [duplicate]

This question already has answers here:
Export hashtable to CSV with the key as the column heading
(2 answers)
Closed 4 years ago.
I'm trying to list all ad group memberships of specific users. The input would be a string of logins split with a comma 'login1,login2'.
So I go over each user and list their memberships with the username as title. Somehow it only shows the first entry. Also it shows the user groups in one row and I don't know how to change that.
Code below:
$users = $logon -split ','
$q = #()
foreach ($user in $users) {
$usernm = Get-ADUser -Filter 'samAccountName -like $user' | select Name
$useraccess = Get-ADPrincipalGroupMembership $user | Select-Object Name
$userobj = New-Object PSObject
$userobj | Add-Member Noteproperty $usernm.Name $useraccess.Name
$q += $userobj
}
Expected output would be something like:
fullnameuser1 fullnameuser2 list of users goes on...
------------- ------------- ------------------------
adgroup1 adgroup3 ...
adgroup2 adgroup4
... ...
In principle this would also mean that if i typed $q.'fullnameuser1' output would be:
fullnameuser1
-------------
adgroup1
adgroup2
...
Whenever the code is ran, it will only ever add the first user's access, also returning all groups on one row. So somehow I need to go over all the group memberships and add a row for each one.
First and foremost, PowerShell does not expand variables in single-quoted strings. Because of that Get-ADUser will never find a match unless you have a user with the literal account name $user. Also, using the -like operator without wildcards produces the same results as the -eq operator. If you're looking for an exact match use the latter. You probably also need to add nested quotes.
Get-ADUser -Filter "samAccountName -eq '${user}'"
Correction: Get-ADUser seems to resolve variables in filter strings by itself. I verified and the statement
Get-ADUser -Filter 'samAccountName -eq $user'
does indeed return the user object for $user despite the string being in single quotes.
If you want a fuzzy match it's better to use ambiguous name resolution.
Get-ADUser -LDAPFilter "(anr=${user})"
You may also want to avoid appending to an array in a loop, and adding members to custom objects after creation. Both are slow operations. Collect the loop output in a variable, and specify the object properties directly upon object creation.
$q = foreach ($user in $users) {
...
New-Object -Type PSObject -Property {
$usernm.Name = $useraccess.Name
}
}
Lastly, I'd consider using the user's name as the property name bad design. That would be okay if you were building a hashtable (which is mapping unique keys to values), but for custom objects the property names should be identical for all objects of the same variety.
New-Object -Type PSObject -Property {
Name = $usernm.Name
Group = $useraccess.Name
}
Basily query all the users and store it in $users, example:
Get-ADUser -Filter * -SearchBase "dc=domain,dc=local"
And then you can export the results as csv or a table.
To Export as CSV :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | export-CSV C:\data\ADUserGroups.csv`
To Format the result as Table in the console itslef :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | Format-Table

How to remove characters from an item in an array using PowerShell

I have an array of computer names, $computers, that are in a FQDN format. I want to trim all characters to the right of the first period and including the period.
Ex: server-01.mydomain.int = server-01
This is what I tried but it errors out.
$computers = Get-VM | Select Name
$computers = $computers.Substring(0, $computers.IndexOf('.'))
$computers
When you do |Select Name, PowerShell returns an object with a Name property, rather than only the value of each Name property of the input objects.
You could change it to Select -ExpandProperty Name and then iterate over each item in the array for the actual substring operation using a loop or ForEach-Object - although we can skip the first step completely:
$computers = Get-VM |ForEach-Object { $_.Name.Substring(0, $_.Name.Indexof('.')) }
Or another way.
$computers = Get-VM | ForEach-Object { ($_.Name -split ".")[0] }
Since you are always selecting the first string before the first "." you can just split at the dot and select the first element in the resulting array.

User Get-ADUser to list all properties and export to .csv

I'm trying to go through a list of users I have and would like to get a few properties (DisplayName, Office) to show in a table then convert the table to a .csv.
I've been working with:
$Users = gc "C:\scripts\Users.txt
foreach ($User in $Users) {
Get-ADUser -Identity $User -Properties DisplayName,Office
}
And that's fine, I can combine it with "select DisplayName,Office" but if I do an "Out-File -append" it just looks terrible. I think I should do this with an array or hash table but I've been reading up on them and don't really understand how I would automate one that can be exported. Thanks for any help you can provide.
Query all users and filter by the list from your text file:
$Users = Get-Content 'C:\scripts\Users.txt'
Get-ADUser -Filter '*' -Properties DisplayName,Office |
Where-Object { $Users -contains $_.SamAccountName } |
Select-Object DisplayName, Office |
Export-Csv 'C:\path\to\your.csv' -NoType
Get-ADUser -Filter '*' returns all AD user accounts. This stream of user objects is then piped into a Where-Object filter, which checks for each object if its SamAccountName property is contained in the user list from your input file ($Users). Only objects with a matching account name are passed forward to the next step of the pipeline. The output can be limited by selecting the relevant properties before exporting the data.
You can further optimize the code by replacing the -contains operator with hashtable lookups:
$Users = #{}
Get-Content 'C:\scripts\Users.txt' | ForEach-Object { $Users[$_] = $true }
Get-ADUser -Filter '*' -Properties DisplayName,Office |
Where-Object { $Users.ContainsKey($_.SamAccountName) } |
Select-Object DisplayName, Office |
Export-Csv 'C:\path\to\your.csv' -NoType
This can be simplified by completely skipping the where object and the $users declaration. All you need is:
Code
get-content c:\scripts\users.txt | get-aduser -properties * | select displayname, office | export-csv c:\path\to\your.csv
#AnsgarWiechers - it's not my experience that querying everything and then pruning the result is more efficient when you're doing a targeted search of known accounts. Although, yes, it is also more efficient to select just the properties you need to return.
The below examples are based on a domain in the range of 20,000 account objects.
measure-command {Get-ADUser -Filter '*' -Properties DisplayName,st }
...
Seconds : 16
Milliseconds : 208
measure-command {$userlist | get-aduser -Properties DisplayName,st}
...
Seconds : 3
Milliseconds : 496
In the second example, $userlist contains 368 account names (just strings, not pre-fetched account objects).
Note that if I include the where clause per your suggestion to prune to the actually desired results, it's even more expensive.
measure-command {Get-ADUser -Filter '*' -Properties DisplayName,st |where {$userlist -Contains $_.samaccountname } }
...
Seconds : 17
Milliseconds : 876
Indexed attributes seem to have similar performance (I tried just returning displayName).
Even if I return all user account properties in my set, it's more efficient. (Adding a select statement to the below brings it down by a half-second).
measure-command {$userlist | get-aduser -Properties *}
...
Seconds : 12
Milliseconds : 75
I can't find a good document that was written in ye olde days about AD queries to link to, but you're hitting every account in your search scope to return the properties. This discusses the basics of doing effective AD queries - scoping and filtering: https://msdn.microsoft.com/en-us/library/ms808539.aspx#efficientadapps_topic01
When your search scope is "*", you're still building a (big) list of the objects and iterating through each one. An LDAP search filter is always more efficient to build the list first (or a narrow search base, which is again building a smaller list to query).

powershell how to remove `{}#` from output. Is there a special command to do it?

I entered gwmi win32_product | select -property name | select -first 1 and output to a file. My result was #{name=Google Talk Plugin}.
How can I get rid of #{}, and name. I only want it to show Google Talk Plugin?
#{} means your exporting an object with properties. Try the -ExpandProperty parameter in Select-Object. You could also combine both select-object commands, like:
gwmi win32_product | select -expandproperty name -first 1
I ran into a problem similar with
$drive = Get-WmiObject Win32_LogicalDisk -ComputerName $servername | Select-Object DeviceID
$drive comes up as #{DeviceID=C:}, #{DeviceID=D:}, ...
Here is my brute force hack at it.
The second Trim statement was because for some reason if I put it in the first Trim it starts to Trim the letters in the Drive =D: becomes :
enter code here
$Asdrive = #() #declared before to get rid of null pointer issue, also to tell PS this is an array not a string
#Write-Host "Trimming for Get-WmiObject"
for($i=0;$i -lt $drive.length; $i++) {
[string]$sdrive = $drive[$i]
[string]$sdrive1 = $sdrive.Trim("#","{","}","D","e","v","i","c","e","I","D")
[string]$sdrive2 = $sdrive1.Trim("=")
$Asdrive += $sdrive2
}
If you're running at least Version 3, you can also use the member enumeration feature and then array slicing to take the first one, instead of using select:
(gwmi win32_product).name[0]
I add some code as I found this question with google.
Frode F. solution is the best one.
If you write out something like:
Get-ADComputer -Filter * -SearchBase $OU | Select-Object Name
you get a proper List of all Computers in an OU. You can also pipe that to a CVS/HTML file and its still nice.
| Export-CSV "mylist.csv"
But if you store it into a variable (array) every name will be wrapped in #{}.
In my case I needed computer names in a variable. Here is the solution thanks to Frodo:
$computerList = Get-ADComputer -Filter * -SearchBase $OU | Select-Object -ExpandProperty Name
Hope it helps someone.
(would add it as comment under the right solution, but I don't have enough reputation to do so)