Powershell nested variables - powershell

Soo trying to do something like the following
$Computers = Get-Content .\~test.txt
ForEach ($Computer in $Computers) {
$computer.Attempts += 1
$computer.result = Successful
}
++ EDIT ++
The output i want is
$MyComputerName.Attempt += 1
$MyComputerName.result
So that each computer will have its own name creating a basic table within the script so that it doesn't have to keep reading some information from a text file.
where $computer is the computer name in the text file. I am trying to do this to create a faster method of checking valuables.

If I'm following you, you want to create new object for each computer:
$Computers = Get-Content .\~test.txt
ForEach ($Computer in $Computers) {
New-Object PSObject -Property #{
Name = $Computer
Attempts = 1
Result = 'Successful'
}
}

Alright eventually got this thing figured out and was as follows. Import-CSV is a great tool for creating a new object for each line.
$Computers = #(Import-CSV .\Computers.txt)
ForEach ($Computer IN $Computers) {
Write-host $Computer.Name
Write-Host $Computer.IP
Write-Host $Computer.Other
}
Computers.txt:
52123-9421,123.123.123.123,yea
HELLBOMBS-PC,123.123.123.123,yea
52123-942dv,123.123.123.123,yea
52123-942RM,123.123.123.123,yea

Related

Optimizing Powershell Script to Query Remote Server OS Version

I want optimize a simple task: pull server OS version into a neat table. However, some servers in our environment have Powershell disabled. Below you fill find my script, which works! However, it takes about 20 seconds or so per server, since it waits for the server to return the results of the invoked command before moving onto the next server in the list. I know there's a way to asynchronously pull the results from a PS command, but is this possible when I need to resort to cmd line syntax for servers that can't handle PS, as shown in the catch statement?
$referencefile = "ps_servers_to_query.csv"
$export_location = "ps_server_os_export.csv"
$Array = #()
$servers = get-content $referencefile
foreach ($server in $servers){
#attempt to query the server with Powershell.
try{
$os_version = invoke-command -ComputerName $server -ScriptBlock {Get-ComputerInfo -Property WindowsProductName} -ErrorAction stop
$os_version = $os_version.WindowsProductName
} # If server doesnt have PS installed/or is disabled, then we will resort to CMD Prompt, this takes longer however.. also we will need to convert a string to an object.
catch {
$os_version = invoke-command -ComputerName $server -ScriptBlock {systeminfo | find "OS Name:"} # this returns a string that represents the datetime of reboot
$os_version = $os_version.replace('OS Name: ', '') # Remove the leading text
$os_version = $os_version.replace(' ','') # Remove leading spaces
$os_version = $os_version.replace('Microsoft ','') # Removes Microsoft for data standardization
}
# Output each iteration of the loop into an array
$Row = "" | Select ServerName, OSVersion
$Row.ServerName = $Server
$Row.OSVersion = $os_version
$Array += $Row
}
# Export results to csv.
$Array | Export-Csv -Path $export_location -Force
Edit: Here's what I'd like to accomplish. Send the command out to all the servers (less than 30) at once, and have them all process the command at the same time rather than doing it one-by-one. I know I can do this if they all could take PowerShell commands, but since they can't I'm struggling. This script takes about 6 minutes to run in total.
Thank you in advance!
If I got it right something like this should be all you need:
$referencefile = "ps_servers_to_query.csv"
$export_location = "ps_server_os_export.csv"
$ComputerName = Get-Content -Path $referencefile
$Result =
Get-CimInstance -ClassName CIM_OperatingSystem -ComputerName $ComputerName |
Select-Object -Property Caption,PSComputerName
$Result
| Export-Csv -Path $export_location -NoTypeInformation

Need help writing ping results to separate files for up and down machines

I need to ping a list of machines and have the results split between up and down into two different .txt files.
$PingPCs = Get-Content "C:\Temp\Cache Cleanup Project\Computerlist.txt
foreach ($PC in $PingPCs) {
$up = Test-Connection $PC -Count 1 -Quiet
if($up) {
$Response = Test-Connection $PC -Count 1 | Select-Object Name
$Response ## Need help figuring out the outfile process to write to .txt ##
}
else {
$Failed = "$PC is not reachable"
$Failed ### Need help figuring out the outfile process to write to .txt ###
}
}
The two places I need help with is just writing the results to separate text files. One file named Online.txt and the other Offline.txt.
You can do something like this, instead of exporting the results on each iteration and appending to a file, it's a better idea to first perform your test and save the results in memory. Once all is complete, export the results:
$PingPCs = Get-Content "C:\Temp\Cache Cleanup Project\Computerlist.txt"
$result = foreach ($PC in $PingPCs)
{
$testConnection = Test-Connection $PC -Count 1 -Quiet
[pscustomobject]#{
ComputerName = $PC
TestConnection = $testConnection
}
}
$result.where({$_.TestConnection}) | Out-File "x:\path\to\online.txt"
$result.where({-not $_.TestConnection}) | Out-File "x:\path\to\offline.txt"
Edit
Adding nice optimization to export the results. Thanks #mklement0
$online, $offline = $result.where({$_.TestConnection},'Split')
$online | Out-File "x:\path\to\online.txt"
$offline | Out-File "x:\path\to\offline.txt"

Unable to pass variable to get wmi object query

Hope you could help me with my issue. I tried to import a list of packages to $list variable and a list of DP server to variable named $DP. I used foreach and write-host and have confirmed that all items in the variable are showing ok, but when I use the variable in get-wmi object as parameter to filter the $i.packageid it is empty/null. Please advise how to resolve this.
sample code
$list=import-csv -Path 'C:\Users\Administrator\desktop\SCCM\packagelist.csv'
$DP="SCCMDP1","SCCMDP2"
foreach ($i in $list)
{
write-host "Item is $($i)" # showing ok
write-host "package is $($i.package)" # showing ok
write-host "packageid is $($i.packageid)" # showing ok
get-wmiobject -namespace root\sms\site_v01 -class sms_distributiondpstatus -Filter {packageid like "$i.packageid"}|select name
You're calling your variables differently in the parameters than from your Write-Host calls. You can't access object properties without a subexpression in the string $().
$list = import-csv -Path 'C:\Users\Administrator\desktop\SCCM\packagelist.csv'
$DP = 'SCCMDP1','SCCMDP2'
foreach ($i in $list)
{
write-host "Item is $i" # showing ok
write-host "package is $($i.package)" # showing ok
write-host "packageid is $($i.packageid)" # showing ok
Params = #{
Namespace = 'root\sms\site_v01'
Class = 'sms_DistributionDPStatus'
Filter = "PackageId LIKE '%$($i.PackageId)%'"
}
Get-WmiObject #Params | Select-Object -Property Name
}
If you're not going to use wildcards, using the LIKE WQL comparison is unnecessary.

putting foreach loop into a csv file

im stuck on how to output my foreach loop into a csv or excel file. this script just get all computers off a local network and then test to see if that computer has a certain KBPatch. The script works just like I said I need help trying to make it output to a csv file. any tips/help is appreciated
Code Below
$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$ObjSearcher.SearchRoot = $objDomain
$objSearcher.filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach($i in $colProplist)
{
$objSearcher.PropertiesToLoad.Add($i)
}
#Finds all operating systems and computer names
$colResults = $objSearcher.FindAll()
foreach($objResult in $colResults)
{
$objComputer = $objResult.Properties;
$names = $objComputer.name
# Define Hotfix to check
$CheckKBS = #(“patch#" , "patch#")
#Query the computers for the HotFix
foreach($name in $names)
{
foreach ($CheckKB in $CheckKBS) {
$HotFixQuery = Get-HotFix -ComputerName $name | Where-Object {$_.HotFixId -eq $CheckKB} | Select-Object -First 1;
if($HotFixQuery -eq $null)
{
Write-Host “Hotfix $CheckKB is not installed on $name”;
}
else
{
Write-Host “Hotfix $CheckKB was installed on $name by ” $($HotFixQuery.InstalledBy);
}
}}
}
To do this cleanly I usually use a custom object.
Before your foreach, instantiate an empty array:
$records = #()
and make an object template:
$tmpRecord = [PSCustomObject]#{
serverName = ''
missingKB = ''
}
Inside the foreach, clone the record obj:
$record = $tmpRecord.psobject.copy()
put your data into the record:
$record.serverName = $name
$record.missingKB = $CheckKb
Put the record into the array:
$records += $record
Then after the foreach, export to csv:
$records | export-csv yourcsv.csv
The need for an object template was confusing to me when I first learned this pattern. You need this because of the combination of scope and how objects are added to arrays (by reference).
If you try to get away with declaring an object inside the loop then that object will be scoped to the lifetime of the foreach loop. You'll then add a reference to that object to your $records array. After the foreach loop completes you will have an array full of references to objects that do not exist.

PowerShell: Use Get-Content from multiple text files to send a message

There was very little on the topic of using multiple text files for PowerShell, only found stuff that would take one list and run it against the primary list. Anyway...
My question comes from a need to combine 2 sets of data, equal in the number of rows.
Server.txt & SessionID.txt. Both files are created from another Get-XASession query.
I wanted to combine these in a Send-XAMessage.
Servers.txt = "Server1","Server2","Server3",etc.
SessionIds.txt = "2","41","18",etc.
Here's the code I've tried unsuccessfully...
BTW, "ServerX", is a static connection server required for XA Remote computing.
$Server = Get-Content .\Server.txt
$SessionIds = Get-Content .\SessionIds.txt
ForEach ($s in $Servers -And $i in $SessionIds) {
Send-XASession -ComputerName ServerX -ServerName $s -SessionId $i -MessageTitle "MsgTitle" -MessageBody "MsgBody" }
For normal usability, we can switch the Stop-XASession, with Get-Service, and use the $s for -ComputerName.
And switch SessionId for -ServiceName.
That would look something like this...
ForEach ($s in $Servers -And $i in $Sevices) { Get-Service -ComputerName $s -Name $i } | FT Name,Status
The only thing that matters, is that each line on both text files is ran through simultaneously. No duplicates. Matching line 1 in Servers.txt to line 1 on SessionIds.txt and using it in each command.
Any help would be greatly appreciated.
You can do something like this:
$Server = Get-Content .\Server.txt
$SessionIds = Get-Content .\SessionIds.txt
$i=0
ForEach ($s in $Servers)
{
Send-XASession -ComputerName ServerX -ServerName $s -SessionId $SessionIds[$i++] -MessageTitle "MsgTitle" -MessageBody "MsgBody"
}
That will cycle the $SessionIds elements in synch with the $server elements. The postincrement operator on $SessionIds[$i++] will increment $i each time it goes through the loop.