Im trying to filter ADComputer by Name.
Our naming convention is a follows <CuntryCode>-<Location>-<DeviceType And Number> we have diferent locations in both USA and MX, we also have serval type of devices
Example:
<Device Type>
Servers = S
Desktops = D
Laptops = L
Tablet = T
Routers = R
Switches = U
example of actual naming:
MX-BCN-D002 or US-TAM-L001
Im creating a Script that will look at a remote PC file system, and check if user has a local .PST file. I only want devices that are Type: Desktops and Laptops, but cant seem to create a condition to filter all other devce type
Partial Script:
$Enabled_PC_list =#()
$Enabled_Online_PC_List =#()
# $Enabled_Offline_PC_list =#()
$data = #()
$PCs = Get-ADComputer -filter "Enabled -eq '$true'" -SearchBase "DC=some,DC=domain" -properties name | Select-Object -ExpandProperty name
$Enabled_PC_list += $PCs
foreach($device in $Enabled_PC_list){
Write-Output ">>> testing Device: $device <<<"
if ($device -like "*-*-D*" -or $device -like "*-*-L*" ) {
if(Test-Connection -TargetName $device -Count 1 -Quiet){
$Enabled_Online_PC_List += $device
}
}else{
Write-Output "Device $device not valid "
}
}
}
So with this line if ($device -like "*-*-D*" -or $device.name -like "*-*-L*" ) i was hoping to filter out all devices that matched what im looking for and proceed to do a Test-Connection on those devices .
Do i need to use regex on this ?
How can i use regex in powershell?
Is there a better way ?
You can do this better by letting Active Directory do the filtering for you, instead of filtering from your side. You can generate an LDAP Filter via string manipulation adding an LDAP Clause for each device type:
$filter = '(&(!userAccountControl:1.2.840.113556.1.4.803:=2)(|'
'S', 'D', 'L', 'T', 'R', 'U' | ForEach-Object {
$filter += '(name=*-*-{0}*)' -f $_
}
$filter += '))'
$onlinePCs = Get-ADComputer -LDAPFilter $filter -SearchBase "DC=some,DC=domain" | Where-Object {
Test-Connection -TargetName $_.DNSHostName -Count 1 -Quiet
}
The generated LDAP Filter would look like this (with some formatting):
(&
(!userAccountControl:1.2.840.113556.1.4.803:=2)
(|
(name=*-*-S*)
(name=*-*-D*)
(name=*-*-L*)
(name=*-*-T*)
(name=*-*-R*)
(name=*-*-U*)
)
)
And can be read as, all enabled objects (computers in this case because we're using Get-ADComputer) which Name attribute is like *-*-S* or *-*-D* or *-*-L* and so on.
Reading though your replies, it got me thinking if LDAP can do the filtering, could powershsell do it to? so i tried this and it works
the where clause did the trick Where-Object {$_.Name -like "*-*-D*" -or $_.Name -like "*-*-L*" -or $_.Name -like "*-*-T*"}
$Enabled_Online_PC_List =[System.Collections.ArrayList]#()
$Enabled_Offline_PC_list =[System.Collections.ArrayList]#()
# $data = #()
$searchBase = "OU=USA,DC=some,DC=domain"
$Enabled_PC_list = Get-ADComputer -filter "Enabled -eq '$true'" -SearchBase $searchBase -properties name | Where-Object {$_.Name -like "*-*-D*" -or $_.Name -like "*-*-L*" -or $_.Name -like "*-*-T*"} | Select-Object -ExpandProperty name
foreach($device in $Enabled_PC_list){
# Write-Output "testing now $device"
if(Test-Connection -ComputerName $device -Count 1 -Quiet){
Write-Output " Now Adding $device to Enabled_Online_PC-List"
$Enabled_Online_PC_List.Add($device)
}else{
Write-Output " Now Adding $device to Enabled_Offline_PC_list"
$Enabled_Offline_PC_list.Add($device)
}
}
Write-Output $Enabled_Online_PC_List
so script is working time is cut off dramatically, now i ask again is there a better way? for some reason LDAP does not want to stick in my head
Related
Been a while since I posted, but i've hit a road-block which an annoying issue
I have a need to scan all of the companies domains for user accounts based on full name, as is FIRST LAST
The same code works fine when running a get-aduser -identity -server domain.name, but using
Get-aduser -filter -server doesn't work inside a For loop, and I'm not sure why!
Here's the code:
$AllDomains = (Get-ADForest).domains
Function Check-ADUser {
Param(
$FullName,
$ADList
)
$ADUserArray = #()
ForEach ($SubDomain in $ADList) {
write-host "Checking for $FullName on $SubDomain ..."
$UserADDomain = Get-ADUser -Server $SubDomain -Filter {(Name -eq $Fullname)} -properties * -ErrorAction SilentlyContinue | Select #{n="DomainName"; e={($_.CanonicalName -split '/')[0]}} `
| Select-Object DomainName -ExpandProperty DomainName
} #ForEach $Domain
The results return black
Here's the code that work fine:
$AllDomains = (Get-ADForest).domains
Function Check-ADUser {
Param(
$FullName,
$ADList
)
$ADUserArray = #()
ForEach ($SubDomain in $ADList) {
write-host "Checking for $FullName on $SubDomain ..."
$UserADDomain = Get-ADUser -Server $SubDomain -Identity $userName -properties * -ErrorAction SilentlyContinue | Select #{n="DomainName"; e={($_.CanonicalName -split '/')[0]}} `
| Select-Object DomainName -ExpandProperty DomainName
} #ForEach $Domain
The function is called via a for loop against each user as such
$Users = #"
Rocky Balboa
Bruce Willis
Gene Simmons
Liz Phair
Steven Segal
"# | ForEach {$_.Split([String[]]"`r`n",[StringSplitOPtions]::None)}
$outarray = #()
ForEach ($user in $Users) {
$aa = Check-ADUser -FullName $User -ADList $AllDomains
}
The only real difference in the code within the function, is the use the -filter instead of -identity on the get-aduser cmdlet
What's odd, is that if I run the code outside of the for loop, it works! I'm thinking it's a Powershell gotcha! any help appreciated :-)
Owen
Use the filter statement like this,
If you are interested in performance, limit the properties to canonicalName instead of *.
After reading the last part of the docs, I think removing the ( ) within curly braces should resolve your issue as well.
$UserADDomain = Get-ADUser -Server $SubDomain -Filter "Name -eq '$Fullname'" -properties * -ErrorAction SilentlyContinue | Select #{n="DomainName"; e={($_.CanonicalName -split '/')[0]}}
if ($null -ne $UserADDomain) {
return $UserADDomain
}
See Microsoft docs on Filter
Excerpt:
Note: For String parameter type, PowerShell will cast the filter query to a string while processing the command. When using a string variable as a value in the filter component, make sure that it complies with the PowerShell Quoting Rules. For example, if the filter expression is double-quoted, the variable should be enclosed using single quotation marks: Get-ADUser -Filter "Name -like '$UserName'". On the contrary, if curly braces are used to enclose the filter, the variable should not be quoted at all: Get-ADUser -Filter "Name -like '$UserName'".
$computers = Get-ADComputer -Filter * -Properties * | Where-Object {$_.Name -like "LT*" -or $_.Name -like "PC*" -or $_.Name -like "MC*"} | Select name,lastlogondate
"You have [{0}] computers in domain [{1}]" -f $computers.count, (get-addomain).dnsroot
$today = Get-Date
$monthago = $today.AddDays(-30)
"Looking for systems that have not logged in since $monthago"
foreach ($computer in $computers)
{
if ($computer.lastlogondate -lt $monthago)
{"Computer [$computer] suspect"
"last logon $($computer.lastlogondate)"
""}
}
returns the following result:
Computer [#{name=lt020367; lastlogondate=10/23/2019 11:45:38}] suspect
last logon 10/23/2019 11:45:38
Can someone possibly tell me why my output is resulting in [#{ and how to resolve?
by get-adcomputer [...] | Select name,lastlogondate you are creating an object. To output properties of those objects, simply use:
"Computer [$($computer.name)] suspect"
"last logon $($computer.lastlogondate)"
By the way:
Getting all * properties is not ideal. Always filter as early as you can: -properties lastlogondate (name is always returned). Same goes for -filter "name -like 'LT*' -or name -like 'PC*' -or name -like 'MC*'".
Also be careful as lastlogondate of computer objects in AD is not synced between domain controllers.
I have a script that can have a list of AD servers (with Get-ADComputer) and the results goes to a TXT file. I don't know how to only have Online Servers only. I only need their names.
I tried to do some IF {} Else{} with the cmdlet Test-Connection -CN $Server but it doesn't work (I'm probably doing it wrong). Here is my code :
$TXTFile = "C:\Scripts\Serv.txt"
$TXTOutput = #()
Write-Host "INFO: Finding servers from AD"
$Servers = Get-ADComputer -Filter {OperatingSystem -like "*server*" -and Enabled -eq $true} | SORT Name
Write-Host "INFO:"$Servers.Count"Records found"
ForEach ($Server in $Servers) {
$ServerHash = $NULL
$ServerHash = [ordered]#{
"Servers Name" = $Server.Name
}
$TXTOutput += New-Object PSObject -Property $ServerHash
}
$TXTOutput
I want, if possible, to have all of my AD Online Servers name in a TXT file. For now I only have all of my servers (Online and Offline). It's my first post so sorry if I made it wrong !
You can use -Quiet parameter with Test-Connection cmdlet in order to get just True or False and then make a decision based on that result.
$TXTFile = "C:\Temp\Serv.txt"
$TXTOutput = #()
$servers=Get-ADComputer -Filter {OperatingSystem -like "*server*" -and Enabled -eq $true} | select -expandproperty Name
ForEach ($Server in $Servers) {
if ((Test-Connection $Server -Count 2 -Quiet) -eq $true) {
$TXTOutput += $Server
}
}
$TXTOutput | Out-File $TXTFile
You can pipe $TXTOutput to sort if you want. Keep in mind that this might take a while since you are basically pinging each server twice -Count 2.
I want to limit the results of the $Groups variable below to only return members of a specific group. This code retrieves the groups a computer belongs to. I am interested only in those that are members of any AD group that contains "SCCM".
I tried line 5 but that doesn't work. What is the correct method to do this? Thanks.
$SamAccountNames = (Get-ADComputer Computer).SamAccountName
$OSInfo = Get-WmiObject -Class Win32_OperatingSystem -ComputerName Computer
$ServerGroupMemberShipList = Get-ADPrincipalGroupMembership $SAMAccountNames | Sort SAMAccountName
$Groups = $ServerGroupMemberShipList.Name
#$Groups = $ServerGroupMemberShipList.Name | Select {$ServerGroupMemberShipList.Name -like "SCCM"}
Write-Host " "
Write-Host "Checking Software Updates Compliance on" $Computer.ToUpper() -ForegroundColor Yellow -NoNewline
Write-Host $OSInfo.Caption
$Groups
Rather than using the Select-Object cmdlet, try using the Where-Object cmdlet for filtering or ? for short. The $_ variable is the current object being processed by the cmdlet.
$Groups = $ServerGroupMemberShipList | Where-Object { $_.Name -like "SCCM" }
or
$Groups = $ServerGroupMemberShipList | ? { $_.Name -like "SCCM" }
I have a script that searches all machines in a domain and is pulling details about them and presents them in a report.
ipmo ActiveDirectory ;
$ADSearchBase = "DC=contoso,DC=chelu,DC=ro,DC=com"
write-host
write-host "Preparing your data..."
write-host
$AD_Results = Get-ADComputer -filter '(Enabled -eq $true)' -SearchScope Subtree -SearchBase $ADSearchBase -properties Description, PasswordNeverExpires, LastLogonTimeStamp, PasswordLastSet, operatingSystem, operatingSystemServicePack, whenCreated, distinguishedname, canonicalname
$count = $AD_Results.count
"Analyzing $count machines..."
# MAIN LOOP
ForEach ($Result In $AD_Results)
{
$i++
if ($i % 16 -eq 0) { $i }
$ComputerName=$result.name
$OS = $result.operatingSystem
$DESC = $result.Description
$DN = $result.distinguishedname
$PNE = $result.passwordneverexpires
if ($ComputerName.Length -ge 15)
{
$ComputerName = $ComputerName.substring(0,15)
}
## BEGIN TIME CONVERSIONS
$LLTS = 0 #AD LastLogonTimestamp
$PLS = 0 #AD PasswordLastSet
If ($result.lastLogonTimeStamp -eq $Null)
{
$T = [Int64]0
}
Else
{
$T = [DateTime]::FromFileTime([Int64]::Parse($result.lastlogontimestamp)).ToString("dd/MM/yyyy HH:mm:ss")
}
$LLTS = $T
$WCR = $result.whencreated.ToString("dd/MM/yyyy HH:mm:ss")
If (!($result.passWordLastSet -eq $Null))
{
$PLS = $result.passwordLastSet.ToString("dd/MM/yyyy HH:mm:ss")
}
## END TIME CONVERSIONS
# 1/2 is in Exceptions?
if ($DN -match "Domain Controllers") {"$computername : DOMAIN CONTROLLER -> Skipping..." ; $Skipped++ ; continue}
if ($DN -match "HVCL") {"$computername : Virtual Cluster Name -> Skipping..." ; $Skipped++ ; continue}
#2/2: isWin?
if ($result.operatingSystem -notlike '*windows*')
{
$Skipped++
continue
}
$isServer=0
if (($DN -match "Servers") -or ($result.operatingSystem -like '*server*'))
{$isServer=1}
The script is skipping some machines based on their DN (distinguishedname) as it can be seen in the
"# 1/2 is in Exceptions?" and in "#2/2: isWin?"
Meanwhile I got a request from a user to except some other (extra) machines that cannot be sorted using the initial query in the AD, which is:
$AD_Results = Get-ADComputer -filter '(Enabled -eq $true)' -SearchScope Subtree -SearchBase $ADSearchBase -properties Description, PasswordNeverExpires, LastLogonTimeStamp, PasswordLastSet, operatingSystem, operatingSystemServicePack, whenCreated, distinguishedname, canonicalname
Basically the user wants to except from the report some specific machines (machine1, machine2, machine3) which are not real computer accounts but "connection points" for clustered resources. Now, there are 2 ways to do that:
To use a script to find all these"connection points" for clustered resources. The only way to detect CNO and VCO is to look at "Service Principal name" attribute from the computer object. If you find "MSClusterVirtualServer" then the object is a CNO or a VCO
Here is what I could come up with:
$serviceType="MSClusterVirtualServer"
$spns = #{}
$filter = "(servicePrincipalName=$serviceType/*)"
$domain = New-Object System.DirectoryServices.DirectoryEntry
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = $domain
$searcher.PageSize = 1000
$searcher.Filter = $filter
$results = $searcher.FindAll()
foreach ($result in $results){
$account = $result.GetDirectoryEntry()
foreach ($spn in $account.servicePrincipalName.Value){
if($spn.contains("$serviceType/")){
$spns[$("$spn`t$($account.samAccountName)")]=1;
}
}
}
$spns.keys | sort-object
To actually create a "whitelist" or "blacklist" where to include machines by names, assuming that in the future some other users might come up with similar requests to except machines from showing up in the report, and that these last machines might not be Virtual Clusters. I prefer this method. What I did is to create an LDAP filter to find that 3 specific machines. Here it is:
(&(&(&(objectCategory=computer)(objectClass=computer)(|(cn=machine1)(cn=machine2)(cn=machine3)))))
QUESTION: Can you help me put together an IF clause that will point towards a "whitelist" in csv format that will contain the names list of the machines to be excepted from the report? The whitelist should reside on the same folder where the script is residing. Should I use the above LDAP filter? How do I do that?
Based on your $AD_Result I would try something along these lines:
ForEach ($exception In (Get-Content "exceptions.txt")) {
$AD_Result = $AD_Result | ? { $_.Name -ine $exception }
}
Why did you want your exceptions file in csv format?