Powershell pipeline access to ADComputer object "Info" - powershell

I've been trying to work with the following query to get AD info for my servers, but "Info" just comes back as blob of stuff. To me it looks like I should be able extract specific parts, but I've had little success. I've even tried to use regex to get what I need out, but I'm terrible with regex. Any help with how to extract the SN,MAC and IP from "Info" would be much appreciated.
Get-ADComputer -Filter * -Property * -SearchBase $server | select -Property name,info

You could try using '-split':
$comp = Get-ADComputer -Filter * -Property * -SearchBase $server
$info = $comp.info
$SerialNumber = ($info -split 'SN=')[1] #replace with 'VMWare-' if desired
$SerialNumber = ($SerialNumber -split ';')[0]
This works with serial number assuming the output is formatted exactly as you have displayed it in the comments. It does, however, bring in that 'VMware' string included. Use the code in my comment if you would like just the #s.
You can copy that -split code out for the remaining fields you would like to extract.

Related

Goal: Provide a csv listing of users with Display Names that do not match their usernames

Trying to make a list of users who's names were changed after account creation for various reason (i.e. marriage, etc...)Came across a few ideas, but nothing panned out after I got stumped with Get-ADuser -Filter -Searchbase. Basic idea was to match the users first/last name with the right format for a username ($n = $.firstname.substring(0,1) + $.surname) against the current SamAccountUser name. Then that failed so just tried to simplify with matching the last names and getting a list from there.
The code below has no output (meaning that when ran the line is blank). The goal is to provide a listing of users by csv, however I wasn't able to get an output so I haven't gotten that far.
I feel like I'm missing something obvious so any help would be appreciated.
Get-ADUser -Filter * -SearchBase "CN=sample,OU=samples ,DC=sampler,DC=sampling" -Properties SamAccountName, surname | Where-Object {($_.SamAccountName.substring(1)) -ne $_.surname}
You could try it with Compare-Object, that should give you a list of differences (in both ways). If you pipe it to Out-GridView, you can filter it and copy/paste it to Excel.
We use "GivenName"."Surname" as SamAccountName, this has to be edited to your needs.
$users = Get-ADUser -Filter * -SearchBase "OU=Users,DC=contoso,DC=com" -Properties SamAccountName,Surname,GivenName
foreach($user in $users)
{
$compare += Compare-Object -DifferenceObject $user.SamAccountName -ReferenceObject ($user.GivenName + "." + $user.Surname)
}
$compare | Out-GridView

Get samaccountname from display name - but with extras

First time poster but site has helped me so much in the past.
We are an MSP and regularly get requests from clients to pull various details off a list of users they send us. Unfortunately though their lists rarely (if ever) contain any unique identifiers for AD such as samAccountName or even e-mail.
So typically I only get their first and last names, job titles etc. and use a slight variation on the below to try and get the required samAccountNames to work in batch modify scripts.
Get Samaccountname from display name into .csv
The problem comes (and caused a big headache recently) when I try to put that output back into a table to line up with the displaynames. As if the script can't find the displayname it just moves onto the next one in the list and puts the samAccountName directly below the last one it found. making it out of line with the displayname column I've put it beside.
My question is is there something I can add to the below script that when an error occurs it simply inputs null or similar into the samAccountName output csv so I could spot that easily in an excel sheet.
Similarly some users have multiple accounts like an admin and non-admin account with the same display name but different samAccountName so it pulls both of them, which is less of a problem but also if there was any way to have the script let me know when that happens? That would be super useful for future.
Import-Module activedirectory
$displayname = #()
$names = get-content "c:\temp\users.csv"
foreach ($name in $names) {
$displaynamedetails = Get-ADUser -filter { DisplayName -eq $name } -server "domain.local"| Select name,samAccountName
$displayname += $displaynamedetails
}
$displayname | Export-Csv "C:\temp\Samaccountname.csv"
So the problem lies in that you rely on Get-ADUser to provide you with user objects and when it doesn't you have gaps in your output. You instead need to create an object for every name/line in your "csv" regardless of whether Get-ADUser finds anything.
Get-Content 'c:\temp\users.csv' |
ForEach-Object {
$name = $_
$adUser = Get-ADUser -Filter "DisplayName -eq '$name'" -Server 'domain.local'
# Create object for every user in users.csv even if Get-ADUser returns nothing
[PSCustomObject]#{
DisplayName = $name # this will be populated with name from the csv file
SamAccountName = $adUser.SamAccountName # this will be empty if $adUser is empty
}
} | Export-Csv 'C:\temp\Samaccountname.csv'

Slow Get-ADUser query

Something I do not unterstand. See the following two code examples.
$LDAPResult1 = Get-ADUser -LDAPFilter "(&(objectCategory=user)(sAMAccountName=*))" -Properties #("distinguishedName","sAMAccountName","extensionAttribute13") -SearchBase "ou=test,dc=test,dc=ch"
$LDAPElements1=#{}
$LDAPResult1 |% {$LDAPElements1.Add($_.SAMAccountName, $_.extensionattribute13)}
compared with (adding a specific server to ask "-Server 'dc.test.test.ch'"):
$LDAPResult1 = Get-ADUser -LDAPFilter "(&(objectCategory=user)(sAMAccountName=*))" -Properties #("distinguishedName","sAMAccountName","extensionAttribute13") -SearchBase "ou=test,dc=test,dc=ch" -Server 'dc.test.test.ch'
$LDAPElements1=#{}
$LDAPResult1 |% {$LDAPElements1.Add($_.SAMAccountName, $_.extensionattribute13)}
The first code takes 30 seconds, the second about 5 minutes. The problem ist not the AD query. This takes around 30 seconds in both cases. But filling the result into the hash table is what is differnet. It seems as if in the second case while filling the hash sill some data is requested from the DC.
What is also interesting. When I wait for five minutes after doing the AD query in case two and then execute the filling into the hash table, then the command takes a second.
I rather would likt to define to what server the command connects in order to execute the folloing commands on the same DC, but this does not make sense if it takes that long.
Can anyone enlighten me …
Addition: We are Talking about 26'000 accounts.
I was able to replicate this. The behaviour does change when you specify the -Server parameter vs. when you don't.
I used Process Monitor to watch network activity and it definitely is talking to the DC when looping through the results returned from using the -Server parameter.
I can't explain why, but it seems like the ADUser objects returned are not populated with the properties from the search. So when they are accessed, it loads the properties from the DC. I could see this when accessing one particular element in the array:
$LDAPResults1[1000]
It displayed the properties, but I also saw network activity in Process Monitor. Whereas I do not see network activity when accessing one element from the results returned when not using the -Server parameter.
So that kind of explains what is happening, but not why. And I really don't know why.
However, I have learned that if you want performance when talking to AD, you have to scrap all the "easy" ways and do things yourself. For example, use the .NET DirectoryEntry and DirectorySearcher classes directly, which can be done in PowerShell using the "type accelerators" [adsi] and [adsisearcher]. For example, this will do the same and will perform consistently:
$dc = "dc.test.test.ch"
$searchBase = "ou=test,dc=test,dc=ch"
$searcher = [adsisearcher]::new([adsi]"LDAP://$dc/$searchBase", "(objectCategory=user)")
$searcher.PropertiesToLoad.Add("sAMAccountName") > $null
$searcher.PropertiesToLoad.Add("extensionAttribute13") > $null
$searcher.PageSize = 1000
$LDAPElements1=#{}
foreach ($result in $searcher.FindAll()) {
$LDAPElements1.Add($result.Properties["sAMAccountName"][0], $result.Properties["extensionAttribute13"][0])
}
I found the following code to be extremely slow.
$user = Get-ADUser -LDAPFilter $filter -Server "xyc" -Properties "sAMAccountName"
I was able to rewrite it as:
$directorySearcher = New-Object System.DirectoryServices.DirectorySearcher
$directorySearcher.SearchRoot = [ADSI]'LDAP://xyz'
[void]$directorySearcher.PropertiesToLoad.Add('cn')
[void]$directorySearcher.PropertiesToLoad.Add('sAMAccountName')
$directorySearcher.Filter = "(cn=abcd efg)"
$results = $directorySearcher.FindOne()
Write-Host $results.Properties["samaccountname"] -as [String]
and it was a lot faster (by an order of magnitude) than using GetAd-User (but still slow).
Export Get-ADUser results into a temporary CSV file and import it back to some objects.
Get-ADUser -LDAPFilter (....) | Export-Csv -Path "TempCSV.csv" -Encoding UTF8 -Delimiter ","
$ADUsers = Import-Csv -Path "TempCSV.csv" -Encoding UTF8 -Delimiter ","
Now you can loop the users object.
foreach ($ADUser in $ADUsers) { (....) }

Find missing prefix for computer name

I am trying to write a script that will automatically find the full asset tag based on the ID of the computer.
For example:
The PC ID is: PC0001
$computerID = PC0001;
But the full asset tag that I need is: WKSPC0001
But these asset tags might have different prefixes, for example DSTPC0002, TBLPC0003 etc. But they all have common pattern: Prefix+PC+ID (the ID is unique and there is no repetition).
I am trying to figure out how to write a query for that in PowerShell but I cant figure it out. I've tried:
$current = Get-ADComputer -Filter {Name -like "*$computerId.text"} |
Select -Property Name
But I was having issues to get it to work.
What am I doing wrong?
A few observations.
You want $computerId to be a string, so $computerID = "PC0001";
The filter expression for Get-ADComputer is expected to be a string also, using curly braces here is wrong. So Get-ADComputer -Filter "..."
Powershell can expand variable values in strings, but it only does that in double-quoted strings. "Name -like '$variable'" will work, but 'Name -like "$variable"' won't.
There is no .text in your $computerId variable. It's a plain string.
Knowing that, try:
$current = Get-ADComputer -Filter "Name -like '*$computerId'"
Other notes
Don't do ... | Select Name unless you really must. Storing the computer object itself will be more versatile. You can do $current.Name when you need it anytime.
Querying the AD with a filter that begins with a wildcard is slow. Try to avoid.
If you have a fixed number of possible prefixes, an LDAP filter like the following will be much faster:
$current = Get-ADComputer -LDAPFilter "(|(cn=WKS$computerId)(cn=DST$computerId)(cn=TBL$computerId))"

Powershell, paginating output from foreach

Seems like this should be simple, but powershell is winning another battle with me. Simply wanting to output the name of all the services running on a system, and their executable path, and pipe that into something I can use to search through it like Less.
So far I have:
$services = get-WmiObject -query 'select * from win32_service'
foreach($service in $services){$service.Name $service.Pathname} | less
But thats giving me the "An empty pipe element is not allowed." that I seem to have come up alot. Anyone tell me how to fix this, either by outputting to a file and Ill go through it with vim, or pipe to page/less/etc so I can do quick scan (with my eyes not programically quite yet).
Try doing the following
$services | %{ $_.Pathname } | less
EDIT Add multiple to the path
$services | %{ "{0} {1}" -f $_.Pathname,$_.Name } | less
If you are using PowerShell 2.0, you might like this:
gwmi win32_service | select-object Name,PathName | ogv
ogv (Output-GridView) is a new cmdlet in 2.0.
get-wmiobject win32_service | select-object name, pathname | more
This is also powershell 2.0 and is the close to the comment above.
You were just trying to use a foreach when you didn't need to in this case.
Even with the foreach, you were close to getting an output you could work with.
A comma in your foreach would have generated a output like a list and you could have used the more command instead of less.
$services = get-WmiObject -query 'select * from win32_service'
foreach($service in $services){$service.Name $service.Pathname} | more
Here is another way to write this same statement.
get-WmiObject win32_service | foreach{$.Name, $.Pathname} | more
This is still not the same as my first example, but I wanted to show you how close you were.
Looks like a good reason to use foreach-object:
$services = get-WmiObject -query 'select * from win32_service'
$services|ForEach-Object {$_|Select-Object Name,Pathname}|less
Please excuse me while I oneline it:
get-WmiObject -query 'select * from win32_service' |ForEach-Object {$_|Select-Object Name,Pathname}|less
foreach-object will return an object to the pipeline based on the input object.
I'm assuming less is an alias of your own making since I don't seem to have it.