I am trying to compare the values of two variables but the contents of those two strings are in different orders
Example:
$Var1 = "item1"
$Var1 += "item2"
$Var2 = "item2"
$Var2 = "item1"
How can I compare those two variables to see if they both are equal?
===== UPDATED WITH EXAMPLE =====
EXAMPLE: Get objects and sort them.
$Computers = (Get-Content "$PWD\Computers.txt").GetEnumerator() | Sort-Object {"$_"}
EXAMPLE: Add the results and sort them.
$Successful += $Computer
$Successful = $Successful.GetEnumerator() | Sort-Object {"$_"}
EXAMPLE SCRIPT: Used the examples above to create the following script. The example allowed me to check the results, instead of count, but by content allowing me to get more accurate comparison. Before I was using "Successful.count -eq Computers.count" which wouldn't check if a computer was inputted twice.
$Computers = (Get-Content "$PWD\Computers.txt").GetEnumerator() | Sort-Object {"$_"}
$HotFixes = Get-Content "$PWD\HotFixes.csv"
CLS
While (!$Successful -OR $Successful -ne $Computers) {
foreach ($Computer in $Computers) {
$MissingCount = 0
IF (!$Successful -NotLike "*$Computer*") {
Write-Host "$Computer`: Connecting"
If (Test-Connection -ComputerName $Computer -Count 1 -quiet) {
Write-Host "$Computer`: Connected"
[string]$Comparison = get-hotfix -ComputerName $Computer | Select -expand HotFixID
ForEach ($HotFix in $HotFixes) {
IF ($Comparison -NotLike "*$HotFix*") {
$Results += "$Computer,$HotFix"
$MissingCount++
}
}
Write-Host "$Computer`: $MissingCount Patches Needed"
$Successful += $Computer
$Successful = $Successful.GetEnumerator() | Sort-Object {"$_"}
} ELSE {
Write-Host "$Computer`: Unable to connect"
}
} ELSE {
Write-Host "$Computer already completed"
}
Write-Host "$Computer`: Complete"
Write-Host
}
}
$Results
If you want to find if the content is equal, regardless of characters position, you could break the string to its characters, sort the result and then use the Compare-Object cmdlet. No result means the variables are equal:
$v1 = $Var1.GetEnumerator() | Sort-Object {"$_"}
$v2 = $Var2.GetEnumerator() | Sort-Object {"$_"}
compare $v1 $v2
Related
I was able to find a piece of code that could ping all systems at once, better than any other job examples I've come across. This thing can take an entire file full of hosts, line by line, and ping them all literally at the same time. But how can I add the ones that are up to my $online array? I tried adding in the true block but it didn't work. Im simply trying to stick $online += $pc somewhere. Any help would be appreciated. Thanks.
$online = #()
$pc = Get-Content C:\servers.txt
$pc | ForEach-Object { Test-Connection -ComputerName $_ -Count 1 -AsJob } | Get-Job | Receive-Job -Wait | Select-Object #{Name='ComputerName';Expression={$_.Address}},#{Name='Reachable';Expression={if ($_.StatusCode -eq 0) { $true } else { $false }}} | ft -AutoSize
You can store the result of your jobs and then filter by Reachable. I've also simplified your code a bit and added -AutoRemove which I consider important to dispose your jobs when done.
$result = Get-Content C:\servers.txt | ForEach-Object {
Test-Connection -ComputerName $_ -Count 1 -AsJob
} | Receive-Job -Wait -AutoRemoveJob | ForEach-Object {
[pscustomobject]#{
ComputerName = $_.Address
Reachable = $_.StatusCode -eq 0
}
}
$online = $result | Where-Object Reachable
# if you want just the `ComputerName` values, you can do
$online = $result | Where-Object Reachable | ForEach-Object ComputerName
# or easier, using member-access enumeration and `.Where` method
$online = $result.Where{ $_.Reachable }.ComputerName
If you're interested in grouping the results between Reachable and Not Reachable during enumeration, the way to do it is with a hash table having 2 List<T> values.
$result = #{
Online = [System.Collections.Generic.List[object]]::new()
Offline = [System.Collections.Generic.List[object]]::new()
}
Get-Content C:\servers.txt | ForEach-Object {
Test-Connection -ComputerName $_ -Count 1 -AsJob
} | Receive-Job -Wait -AutoRemoveJob | ForEach-Object {
$obj = [pscustomobject]#{
ComputerName = $_.Address
Reachable = $_.StatusCode -eq 0
}
if($obj.Reachable) {
return $result['Online'].Add($obj)
}
$result['Offline'].Add($obj)
}
$result.Online.ComputerName # => has all reachable records
I believe the issue here is the pipe ft -autosize.
Try to pipe after the if/else statement as per below:
| ForEach-Object {
if ($_.Reachable -eq $true) {
$online += $_.ComputerName
}
}
Then if you want to view the results you can always do:
$online | ft -AutoSize
I'd also suggest a better formatting as all one line isn't easy to read. Try something like this:
$online = #()
$pc = Get-Content C:\servers.txt
$pc | ForEach-Object {
Test-Connection -ComputerName $_ -Count 1 -AsJob
} | Get-Job | Receive-Job -Wait |
Select-Object #{Name='ComputerName';Expression={$_.Address}},#{Name='Reachable';Expression={
if ($_.StatusCode -eq 0) {
$true
} else {
$false
}
}} | ForEach-Object {
if ($_.Reachable -eq $true) {
$online += $_.ComputerName
}
}
$online | ft -AutoSize
I have this script that I need to use to retrieve the data of a particular user "ADTuser" from a list of servers the script works well, but the output file with my user add also other users' detail that is not needed for my final output how can I filter it to only the user that I need.
get-content C:\servers.txt | foreach-object {
$Comp = $_
if (test-connection -computername $Comp -count 1 -quiet) {
([ADSI]"WinNT://$comp").Children | ?{$_.SchemaClassName -eq 'user' } | %{
$groups = $_.Groups() | %{$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
$_ | Select #{n='Computername';e={$comp}},
#{n='UserName';e={$_.Name}},
#{n='Memberof';e={$groups -join ';'}},
#{n='status'; e={if($groups -like "*Administrators*"){$true} else{$false}}}
}
} Else {Write-Warning "Server '$Comp' is Unreachable hence Could not fetch data"}
} | Out-File -FilePath C:\users.txt
This should be an easier way of doing what you're looking for, Get-CimInstance and Get-CimAssociatedInstance have been around since PowerShell 3:
Get-Content C:\servers.txt | ForEach-Object {
$computer = $_
try {
$query = Get-CimInstance Win32_UserAccount -Filter "Name='ADTuser'" -ComputerName $_ -ErrorAction Stop
foreach($object in $query) {
$membership = Get-CimAssociatedInstance -InputObject $object -ResultClassName Win32_Group -ComputerName $_
[pscustomobject]#{
Computername = $_
UserName = $object.Name
Memberof = $membership.Name -join ';'
Status = $membership.Name -contains 'Administrators'
}
}
}
catch {
Write-Warning "Server '$computer' is Unreachable hence Could not fetch data"
}
} | Export-Csv C:\users.csv -NoTypeInformation
If that doesn't work for you, your code would require a simple modification on your first filtering statement:
Where-Object { $_.SchemaClassName -eq 'user' -and $_.Name.Value -eq 'ADTuser' }
It's important to note that Test-Connection -ComputerName $_ -Count 1 -Quiet is not a relevant test for this script, this command is testing for ICMP response and adsi over WinNT requires RPC connectivity as well SMB.
Putting it all together with minor improvements the script would look like this:
Get-Content C:\servers.txt | ForEach-Object {
if (-not (Test-Connection -ComputerName $_ -Count 1 -Quiet)) {
Write-Warning "Server '$_' is Unreachable hence Could not fetch data"
return
}
$computer = $_
([adsi]"WinNT://$_").Children.ForEach{
if($_.SchemaClassName -ne 'user' -and $_.Name.Value -ne 'ADTuser') {
return
}
$groups = $_.Groups().ForEach([adsi]).Name
[pscustomobject]#{
Computername = $computer
UserName = $_.Name.Value
Memberof = $groups -join ';'
Status = $groups -contains 'Administrators'
}
}
} | Export-Csv C:\users.csv -NoTypeInformation
I need to get the list of the server last Windows Update patch from multiple different OU and then export it as in CSV file with the below column and its sample result I gather manually running Get-HotFix locally on each server:
ServerName, Last Time Update Installed, KB Number, KB Update Name, InstalledBy
PRODSQL01-VM, 31/12/2018 02:46:55, KB4462930, Cumulative Update, NT AUTHORITY\SYSTEM
PRODSQL02-VM, 18/12/2018 12:00:00 AM, KB4471324, Security Update, DOMAIN\SVC_SCCM
PRODDC01-VM, 16/1/2019 02:16:31, KB4343669, Cumulative Update, DOMAIN\SVC_SCCM
PRODDC02-VM, 13/1/2018 03:00:00 AM, KB4457146, Security Update, DOMAIN\Admin-Staff1
This is the modified script for multiple OU processing, but somehow the result is still a blank CSV file:
$CsvFile = 'C:\Result.csv'
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install'
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$OUList = #(
"OU=TEST Servers,OU=MyDomain Testing,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=MyDomain Sydney,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=New Company,DC=MyDomain,DC=com"
)
$OUList | ForEach-Object {
$OU = $_
$Computers = Get-ADComputer -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU |
Select-Object -ExpandProperty DNSHostName |
ForEach-Object {
If (Test-Connection $_ -Count 1 -Quiet) {
$_
}
Else {
Write-Host "Cannot reach $($_)" -ForegroundColor Red
}
}
ForEach ($computer in $Computers) {
Try {
$remoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype, $computer)
$regKey = $remoteBase.OpenSubKey($key)
$keyValue = $regkey.GetValue('LastSuccessTime')
Write-Host ''
Write-Host "$($computer): last time updates were installed was $($keyValue)"
}
Catch {
$ | Write-Error
}
Finally {
If ($regKey) {$regKey.Close()}
}
}
} | Export-Csv -Path $Csvfile -NoTypeInformation
As others have mentioned, you're doing write-host rather than adding anything to your CSV file.
Note that I haven't tested any of the below code - it's just a bit of rearranging.
$CsvFile = 'C:\Result.csv'
$Results = #() #object to hold the output
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install'
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$OUList = #(
"OU=TEST Servers,OU=MyDomain Testing,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=MyDomain Sydney,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=New Company,DC=MyDomain,DC=com"
)
ForEach ($OU in $OUList) {
Get-ADComputer -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU |
Select-Object -ExpandProperty DNSHostName |
ForEach-Object {
If (Test-Connection $_ -Count 1 -Quiet) {
Try {
$remoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype, $_)
$regKey = $remoteBase.OpenSubKey($key)
$keyValue = $regkey.GetValue('LastSuccessTime')
# Add result to Results array. Not Result
$results += [pscustomobject]#{
Computer = $_
LastSuccessTime = $KeyValue
}
}
Catch {
$ | Write-Error
}
Finally {
If ($regKey) {$regKey.Close()}
}
}
Else {
Write-Host "Cannot reach $($_)" -ForegroundColor Red
}
}
}
#export result object to CSV
$Results | Export-Csv -Path $Csvfile -NoTypeInformation
I'm sure there is a simple solution, but I'm stuck. The output in the members column is like this
{domain\Domain Admins, domain\joerod...
How can I show the
$member
value on each line?
Function Get-AdminGroups{
foreach($i in (Get-Content C:\Users\joerod\Desktop\remove_users.txt)){
#test if machine is on the network
if (-not (Test-Connection -computername $i -count 1 -Quiet -ErrorAction SilentlyContinue)) {
Write-Warning "$i is Unavalible"
"`r"
}
else {
(invoke-command {
$members = net localgroup administrators |
? {$_ -AND $_ -notmatch "command completed successfully"} |
select -skip 4
New-Object PSObject -Property #{
Computername = $env:COMPUTERNAME
Users=$members
}
} -computer $i -HideComputerName |
Select * -ExcludeProperty RunspaceID )
}
}
}
Get-AdminGroups |ft
Iterate through $members and make an object for each one. This creates an empty array, loops through the computers in your text file, and in that loop it pulls a list of the local administrators, and for each one it creates a custom object just like you are doing, and it adds it to that array.
$Results = #()
foreach($i in (GC C:\Users\joerod\Desktop\remove_users.txt)){
#test if machine is on the network
if (!(Test-Connection -computername $i -count 1 -Quiet -ErrorAction SilentlyContinue)) {
Write-Warning "$i is Unavalible`r"
Continue
}
invoke-command {
$members = net localgroup administrators |?{$_ -AND $_ -notmatch "command completed successfully"} | select -skip 4
ForEach($member in $members){
$Results += New-Object PSObject -Property #{
Computername = $env:COMPUTERNAME
Users=$member
}
}
} -computer $i -HideComputerName # | Select * -ExcludeProperty RunspaceID
}
$Results | FT
Get-content -path z:\path\name.txt |
foreach {
(get-hotfix -Computername $_ |
Sort-object IUnstalledon)[-1]
}
I would like to count and place the count in front of the (get-hotfix in the output)
1 computer-name update ncncncncn cncncncncncn date time
2 computer name.....
Maybe something like this?
#Count variable
$i = 0
Get-content -path z:\path\name.txt |
foreach {
$hotfix = (get-hotfix -Computername $_ | Sort-object IUnstalledon)[-1]
#Create your output string "Count ComputerName Hotfix"
Write-Output "$i $_ $hotfix"
$i++
}