Importing CSV and using Get-Hotfix to export to CSV - powershell

So, I'm having some difficulty with some code I'm trying to use to get hotfixes from a lot of computers (which are listed in a CSV file under the column "IP Address") and export that result to a csv. They each require a local computer account to log in (in the same CSV under the column "CPU Name"). I don't really care if it's one csv for the whole thing or a csv for each result. Here's the code so far:
$ipaddress = [What do I put here?]
$cpuname = [What do I put here?]
$OutputFile = 'MyFolder\Computer.csv'
$Username = '$cpuname\MyUsername' [Is this ok?]
$Password = 'MyPassword'
$pass = ConvertTo-SecureString -AsPlainText $Password -Force
$SecureString = $pass
$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$SecureString
$Computers = Import-CSV "C:\MyFolder\Computers.csv"
ForEach ($ipaddress in $Computers) {
}
try
{
Get-HotFix -Credential $MySecureCreds -ipaddress $IPAddress | Select-Object PSComputerName,HotFixID,Description,InstalledBy,InstalledOn | export-csv $OutputFile
}
catch
{
Write-Warning "System Not reachable:$ipaddress"
}
Am I close?

The IP Address and the Computer Name are coming from the CSV, so you don't need to statically define them.
Regarding the username, a couple things:
You need to use double quotes to expand.
Also you'll need to read the 'CPU Name' property one way to do this
would be using a subexpression inside of the double quoted larger expression e.g. "$($variable.property)more text". Or you could concatenate the string $variable.property + 'more text'
Since CPU Name has a space in the column name you'll need to enclose that in quotes.
Since the User name comes from the Computer name which comes from the
CSV, defining the User name variable needs to be in the foreach loop.
The security practices in this script are questionable, but outside the scope of the question. e.g. Saving passwords in scripts, especially a local account with administrative privileges to a large number of networked machines that all have that same password...
$InputFile = 'C:\MyFolder\Input.csv'
$OutputFile = 'C:\MyFolder\Output.csv'
$Password = Read-Host | ConvertTo-SecureString -AsPlainText -Force
$Computers = Import-CSV $InputFile
$HotfixOutput = foreach ($Computer in $Computers) {
$Username = "$($Computer.'CPU Name')\MyUsername"
$MySecureCreds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Username,$Password
try {
Get-HotFix -Credential $MySecureCreds -ipaddress $Computer.'IP Address' | Select-Object PSComputerName, HotFixID, Description, InstalledBy, InstalledOn
}
catch {
Write-Warning "System Not reachable: $($Computer.'IP Address')"
}
}
$HotfixOutput | Export-Csv $OutputFile -NoTypeInformation
Remove-Variable Password
Remove-Variable MySecureCreds

Related

Get email Address from CSV file line and send email to each of them separately

I have a csv file, i want to send email separately to each user, currently from my script i am getting emails to all users addresses those are in my csv file "user" column. Here is my CSV file Data.
#TYPE System.Management.Automation.PSCustomObject
"time","remip","user","msg"
"11:12:15","124.29.228.164","abc#xyz.com.pk","SSL tunnel shutdown"
"11:12:43","124.29.228.164","efg#gmail.com","SSL tunnel established"
"11:13:25","124.29.228.164","abc#xyz.com.pk","SSL tunnel established"
"11:14:05","202.63.194.8","efg#gmail.com","SSL tunnel established"
This is my powershell code
$Subject = " Alerts $(get-date -Format "dddd MM-dd-yyyy HH:mm")"
$Server = "qs42.xyz.com"
$From = "logalerts#abc.com"
$To = $ID = Import-Csv myfile.txt | Select-Object -ExpandProperty user -Unique
$PWD = ConvertTo-SecureString "test123" -AsPlainText -force
$Cred = New-Object System.Management.Automation.PSCredential("logalerts#abc.com" , $pwd)
$path = "myfile.txt"
$file = (Import-Csv $path) | Select-Object -Unique -Property Time , remip , user , Msg
Send-MailMessage -From $from -to $To -Port 587 -SmtpServer $server -Subject $Subject -Credential $cred -UseSsl -Body ($file | Out-String)
Iam able to get emails of this data on both address abc#xyz.com, efg#gmail.com which i dont want, i know emails are receiving because of my variable setting but my requirement is to get emails of each user data on the same user email address.
>>time remip user msg
---- ----- ---- ---
11:12:15 124.29.228.164 abc#xyz.com SSL tunnel shutdown
11:12:59 124.29.228.164 efg#gmail.com SSL tunnel shutdown
11:13:25 124.29.228.164 abc#xyz.com SSL tunnel established
11:14:05 202.63.194.8 efg#gmail.com SSL tunnel established
I don't know how to do this any help please.
There were a couple of things wrong with your code:
$PWD is an automatic variable and means the current working directory. You must not use that name as self-defined variable
You use Import-Csv multiple times where once is enough
The way you try to get a value in the $To variable is wrong
you need Group-Object because some users may have more than one message
I'd recommend using Splatting the parameters to the Send-MailMessage cmdlet
Try:
$password = ConvertTo-SecureString "test123" -AsPlainText -force
$cred = New-Object System.Management.Automation.PSCredential("logalerts#abc.com" , $password)
# import the data from the csv, group on field 'user' (which is the emailaddress to send to)
Import-Csv -Path 'D:\Test\myfile.txt' | Group-Object user | ForEach-Object {
# build a Hashtable to neatly send the parameters to Send-MailMessage cmdlet
# this is called Splatting.
$mailParams = #{
From = 'logalerts#abc.com'
To = $_.Name # the Name of the group is from the user field in the csv
Port = 587
SmtpServer = 'qs42.xyz.com'
Subject = 'Alerts {0:dddd MM-dd-yyyy HH:mm}' -f (Get-Date)
Credential = $cred
UseSsl = $true
Body = $_.Group | Format-Table -AutoSize | Out-String
}
Send-MailMessage #mailParams
}
This is very similar to the answer given by #theo, but it doesn't use the Group-Object cmdlet. I've had problems with memory consumption when the data set is very large because it stores structures (PSObject or PSCustomObjects) within objects and the objects tend to be pretty large.
$PassWord = ConvertTo-SecureString "test123" -AsPlainText -force
$Cred = New-Object System.Management.Automation.PSCredential("logalerts#abc.com"
, $PassWord)
$path = "myfile.txt"
# Get all alerts
$Alerts = Import-Csv $path | Select-Object -Unique -Property Time , remip , user
, Msg
# get individual recipient addresses from the log file
$UniqueUsers = $Alerts |
Select-Object -ExpandProperty user -Unique
ForEach ($User in $UniqueUsers){
$UniqueAlerts = $Alerts |
Where-Object {$_.user -eq $User}
$params = #{
From = 'logalerts#abc.com'
To = $User
Subject = 'Alerts {0:dddd MM-dd-yyyy HH:mm}' -f (Get-Date)
Body = $UniqueAlerts | Out-String # This probably won't align
the
columns properly
# if a mono-spaced font isn't
used by the e-mail client.
# you might consider sending the
message in HTMP format
SmtpServer = 'qs42.xyz.com'
Port = 587
UseSsl = $true
Credential = $cred
}
Send-MailMessage #params
}
As noted in comments, the body of the message will probably be hard to read if it's in plain-text. A HTML message with the data within a table will produces a much easier to read message.

Powershell variable script

Another week another powershell question. First off thank you all for your help I am learning a lot and I am thinking that I know the answer but can't seem to get it out of my head into the script today.
I currently have this.
$user = "user"
$pass1 = Read-Host "Enter Techops Password"
$SecurePassword = ConvertTo-SecureString $pass1 -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($user, $SecurePassword)
$querylist = (Import-Csv -Path 'C:\Users\user\Documents\20221110\VIPS\20221115_vips1.csv').ip
$today = '{0:yyyyMMdd}' -f (Get-Date)
Import-Module tm-device42
Connect-D42 -Credential $Credential
$results = foreach ($ip in ($querylist)) {
Get-vServerPrdFromPim {$_.ip}
}
$results | Write-Output
The $results | Write-output wont be staying I'm just using it whilst building the script.
The $querylist = (Import-Csv -Path 'C:\Users\user\Documents\20221110\VIPS\20221115_vips1.csv').ip returns the correct info, currently a single IP address 10.10.10.1 (example)
However I get a result of product not found on the Get-vServerPrdFromPim (this is an internal custom made module) but if I do
Get-vServerPrdFromPim 10.10.10.1 in powershell I get the answers that I would expect.
user> (Import-Csv -Path 'C:\Users\user\Documents\20221110\VIPS\20221115_vips1.csv').ip
10.75.230.74
So I am assuming it is an issue with my foreach statement.
I am just wanting to pull the IP's from a large sheet and check them against a database and then output the data.
Am I correct in thinking that I haven't correctly formatted my foreach statement? If so how do I make $ip reference the info from $querylist?
Kind regards
-------------UPDATE --------------------
I have adjusted the following code as suggested
Get-vServerPrdFromPim $ip
So now the foreach loop looks like
$results = foreach ($ip in ($querylists)) {
Get-vServerPrdFromPim $ip | select Name, ProductCode, NetAddr, InfrastructureOwner, OperatingSystemOwner
}
This seems to work fine for a file with a single IP address but when I add additional IP's to the CSV it only processes the first IP.
Will need to dig in further.
Further Update. As requested this is what the excel sheet looks like. This is just an extract.
Found the mistake in my code. Thanks to #mathias and also other research. Code ended up being
$user = "user"
$pass1 = Read-Host "Enter Techops Password"
$SecurePassword = ConvertTo-SecureString $pass1 -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($user, $SecurePassword)
$ips = (Import-Csv -Path 'C:\Users\user\Documents\20221110\VIPS\20221115_vips.csv').ip
$today = '{0:yyyyMMdd}' -f (Get-Date)
Import-Module device42
Connect-D42 -Credential $Credential
$results = foreach ($ip in $ips){
Get-vServerPrdFromPim $ip | select Name, ProductCode, NetAddr, InfrastructureOwner, OperatingSystemOwner
}
$results | Export-Csv C:\Users\user\Documents\20221110\D42_test.csv

Powershell - combining multiple for each into a single output

Good morning everyone,
I am still learning powershell and have a script that my company run's. I have adjusted it slightly and it works well. however...
Overview
Script pulls in a CSV file and then for each device on the CSV it logs in and grabs all of the VIP information and then outputs it to a CSV that is dated and titled with the device name.
Ask
I would like adjust the script so that instead of having 50+ CSV files I could have a single CSV file with an extra column that would like the device name that all the VIPS are on. How do I do that?
Current Script
$user = "user"
$pass1 = Read-Host "Enter Netscaler Password"
$Devicelist = Import-Csv'C:\Users\user\Documents\20221110\20221109_Citrix_inventory_Master1.csv'
foreach ($device in ($Devicelist | ? { $_.fqdn -match "nsr" -and $_.State -match "Primary" -and $_.Configuration_viewable_with_Techops_login -match "Yes" })) {
Write-Host "Attempting to connect to $($device.fqdn)"
$SecurePassword = ConvertTo-SecureString $pass1 -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($user, $SecurePassword)
$session = Connect-NetScaler -Hostname $device."IP address" -Credential $Credential -PassThru
$nsVIPs = Get-NSLBVirtualServer | select name, ipv46, port, curstate
$nsVIPs | Out-File C:\Users\user\Documents\20221110\VIPS\$(get-date -f yyyyMMdd)"_"$($device.fqdn)-"vips.csv"
}
Current CSV Input File format
Current output file format
What I would like to out file format to be
**
What have I tried
**
At the moment nothing except for research, I am guessing that I will need to hold of of the info in an array and then output it at the end. I just don't have a clue how to do that.
Thanks for looking and helping
Neil
You can change the code to first collect all data in one single variable $result and after the loop write all that out in a single CSV file.
To write csv, you need to use Export-Csv, not Out-File, which is intended to write simple text, not objects.
$user = "user"
$pass1 = Read-Host "Enter Netscaler Password"
$SecurePassword = ConvertTo-SecureString $pass1 -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential ($user, $SecurePassword)
$today = '{0:yyyyMMdd}' -f (Get-Date)
$Devicelist = Import-Csv -Path 'C:\Users\user\Documents\20221110\20221109_Citrix_inventory_Master1.csv' |
Where-Object { $_.fqdn -match "nsr" -and
$_.State -match "Primary" -and
$_.Configuration_viewable_with_Techops_login -match "Yes" }
$result = foreach ($device in $Devicelist) {
Write-Host "Attempting to connect to $($device.fqdn)"
$session = Connect-NetScaler -Hostname $device.'IP address' -Credential $Credential -PassThru
Get-NSLBVirtualServer |
Select-Object name, ipv46, port, curstate, #{Name = 'Device Name'; Expression = {$device.fqdn}}
}
# now write out the csv file just once
$result | Export-Csv -Path "C:\Users\user\Documents\20221110\VIPS\$($today)_vips.csv" -NoTypeInformation

Reading values from CSV files and storing in local variable in PowerShell

I am stuck at one point, the requirement is I have to store the Username, Password, Hostname & hostkey in a CSV file and I have to read & store the values in local variable which can be used for establishing SFTP connection.
My CSV looks like this:
HostName,Username,Password,SshHostKeyFingerprint
abc.com,Lear,qwert,ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx...
The code which I am using to read and store the different columns values is:
Add-Type -Path "WinSCPnet.dll"
$csv = Import-Csv c:\test\output.csv
$csv | ForEach-Object
{
$Hostname = $_.hostname
$username = $_.username
$Password = $_.Password
"$Hostname - $username -$Password"
}
# Set up session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = $Hostname
UserName = $username
Password = $Password
SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
}
Nothing is getting displayed, when trying to display the values.
The script block after the ForEach-Object cmdlet has to start on the same line. Otherwise PowerShell won't connect them together.
$csv | ForEach-Object {
$HostName = $_.HostName
$Username = $_.Username
$Password = $_.Password
"$HostName - $Username - $Password"
}
Another problem, that your will face eventually, is that your code does process only the last line in the CSV file.
You most probably actually want to process the parsed out values within the ForEach-Object script block. Like this:
$csv | ForEach-Object {
$HostName = $_.HostName
$Username = $_.Username
$Password = $_.Password
"$HostName - $Username - $Password"
# Set up session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = $HostName
UserName = $Username
Password = $Password
SshHostKeyFingerprint = ...
}
# ...
}

Change a file in our network trough powershell as an administrator

I try to replace some lines in .ini files on devices which are not in our Domain. I need to execute the copy/paste-item and get/set-content cmdlet's with the local administrator account. The IP's of all the devices are in a seperate text file. Could someone tell me, how I can execute my cmdlet's as the local administrator?
$user = ".\administrator"
$pass = ConvertTo-SecureString "password" -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user,$pass
$IP_Array = (Get-Content \\iparraypath)
foreach ($IP in $IP_Array) {
mainreplace
}
function mainreplace {
$path = "\\$IP\path.."
Copy-Item $path path..
$l = (Get-Content $path)
if ($l.StartsWith('oldtext')) {
($l) -replace "oldtext.*",'newtext' | Set-Content $path
}
}
The cmdlet you're looking for is Invoke-Command. Also, you want to run -replace on the entire content of the input file, otherwise all non-matching lines would be removed from the output.
foreach ($IP in $IP_Array) {
Invoke-Command -Computer $IP -ScriptBlock {
# use local path here, b/c the scriptblock is running on the remote host
$file = 'C:\path\to\your.ini'
(Get-Content $file) -replace 'oldtext.*', 'newtext' |
Set-Content $file
} -Credential $cred
}
Copying the file is not necessary (unless you want to keep a backup copy), because putting Get-Content in parentheses (a so-called grouping expression) reads the entire content into memory before the replacement. That way you can write the modified output directly back to the source file.