Best Practice - If match return result - powershell

Use Case
The below script is to be placed in a scheduled task to notify me if my public IP Address changes
The IF match condition is not the correct behaviour
What would be the best way to match if the IP Address changed and output that to host?
Code
$ip = Invoke-RestMethod http://ipinfo.io/json | select -ExpandProperty ip
$date = (get-date).date
$value = "{0} - {1}" -f ($date),($ip)
Add-Content -Value $value -Path "C:\users\Sumeet\Documents\WindowsPowerShell\ip.txt"
$file = Get-Content -Path "C:\users\Sumeet\Documents\WindowsPowerShell\ip.txt"
if ($file | Select-String -Pattern $ip) {
clear-host
write-host "Match found at $_ your public IP interface has changed"
}
File Output
Date - IP
9/06/2018 12:00:00 AM - 121.211.177.20
9/06/2018 12:00:00 AM - 121.211.177.20
Output

I think something similar would be more helpful, as #LotPings suggested. You should update the file only if there is a change and follow up with notification as needed.
Also note, that change of IP can occur at anytime, but your scheduled task will let you know only whenever it is scheduled to run.
$ip = Invoke-RestMethod http://ipinfo.io/json | select -ExpandProperty ip
$date = (get-date).date
$value = "{0} - {1}" -f ($date),($ip)
$file = Get-Content -Path "E:\Code\powershell\myPS\2018\Jun\checkPublicIP\ip.txt"
if ($file | Select-String -Pattern $ip) {
Write-Host "IP is not changed"
}
else {
#Adding current IP to the file
Add-Content -Value $value -Path "E:\Code\powershell\myPS\2018\Jun\checkPublicIP\ip.txt"
#Add more code as needed for notification / email / alert.
}

Handling of data is IMO done easier when using csv files as columns/properties are assigned on import.
Because of my locale which doesn't support the date separator / or tt for AM/PM I had to use a CultureInfo object.
## Q:\Test\2018\06\09\SO_50771712.ps1
$File = "$Env:USERPROFILE\Documents\WindowsPowerShell\ip.csv"
$CIUS = New-Object System.Globalization.CultureInfo("en-US")
$Actual = [PSCustomObject]#{
Date = (get-date).ToString("d/M/yyyy hh:mm:ss tt",$CIUS)
IP = (Invoke-RestMethod http://ipinfo.io/json).ip
}
if (!(Test-Path $File)){'"Date","IP"'|Set-Content $File}
$Last=Import-Csv $File|Sort-Object {[DateTime]$_.Date}|Select-Object -Last 1
If ($Last.ip -ne $Actual.ip){
Write-Host ("Last ip : {0} from: {1}" -f $LAst.IP,$Last.date)
Write-Host ("New ip : {0} from: {1}" -f $Actual.IP,$Actual.date)
Export-Csv $File -InputObject $Actual -Append -NoTypeInformation
}
Sample output:
> Q:\Test\2018\06\09\SO_50771712.ps1
Last ip : 92.123.13.83 from: 9/6/2018 09:19:21 PM
New ip : 92.123.13.84 from: 9/6/2018 09:34:59 PM
> gc $file
"Date","IP"
"9/6/2018 08:51:00 PM","92.123.13.82"
"9/6/2018 09:19:21 PM","92.123.13.83"
"9/6/2018 09:34:59 PM","92.123.13.84"

Related

Formatting in PowerShell

I am getting the content of a JSON file into the variable $variable and filtering the $variable on a condition as in the code below and holding the filtered data in $logs. The output of echo $logs is as below.
0.842093077,
0.792955,
0.8910225,
0.8724875,
0.852885333,
0.774708,
0.987243333,
0.87078,
0.9565,
0.839393333
My question is how do i convert each of these decimal values to a percentage like 84.20 %, 79.20% and so on, so that when the mail goes out with the Percentage Utilisation field as a percentage value and not a decimal value?
DESIRED SAMPLE OUTPUT
Workspace Name : A NAME
Workspace Allowance : 100
Workspace Usage : 79.2955
Workspace Size Free : 20.7
**Percentage Utilization : 79.29**
Predicted Usage : 86.05585477
PRESENT OUTPUT
Workspace Name : A NAME
Workspace Allowance : 100
Workspace Usage : 79.2955
Workspace Size Free : 20.7
*Percentage Utilization : 0.792955*
Predicted Usage : 86.05585477
# Convert the CSV file into its JSON equivalent
import-csv "Workspacesize.csv" | ConvertTo-Json | Add-Content -Path "output.json" -Force -ErrorAction Stop
# Import email settings from config file
[xml]$ConfigFile = Get-Content "Settings.xml"
#Write-Output $ConfigFile
$emailTo = #(abc#gmail.com, xyz#gmail.com)
# Create email content
$smtpsettings = #{
From = $ConfigFile.Settings.EmailSettings.MailFrom
Subject = "TEST EMAIL"
SmtpServer = $ConfigFile.Settings.EmailSettings.SMTPServer
}
[String]$messagebody = ""
$variable=Get-Content -raw "output.json" | ConvertFrom-Json
$logs=$variable | Where-Object { [double]$_.'Percentage Utilization' -gt 0.75}
echo $logs
}
foreach ($log in $logs )
{
$messagebody = $messagebody + $log + "`r`n"
}
$messagebody = $logs | Out-String
$messagebody = '<pre>' + $messagebody + '</pre>'
#-------------------------------------------------
# Script
#-------------------------------------------------
try
{
Send-MailMessage #smtpsettings -To $emailTo -Body $messagebody -BodyAsHtml -Encoding utf8 -verbose -ErrorAction Stop
}
catch
{
Write-Warning $_.Exception.Message
}
# MOVE THE JSON & CSV FILE TO AN ARCHIVED LOCATION
Move-Item -Path $Jsonfile -Destination C:\Users\Siddhartha.S.Das2\Desktop -Force
Move-Item -Path $Csvfile -Destination C:\Users\Siddhartha.S.Das2\Desktop -Force
#-------------------------------------------------
# The End
#-------------------------------------------------
As commented, you can simplify the code by using the data from the CSV file and only if you really also need that in JSON format, convert that data and save it at the end of the script.
$CsvFile = 'C:\Somewhere\Workspacesize.csv'
$data = Import-Csv -Path $CsvFile
$logs = $data | Where-Object { [double]$_.'Percentage Utilization' -gt 0.75 }
# update the 'Percentage Utilization' property on each item
foreach ($item in $logs) {
$item.'Percentage Utilization' = [Math]::Round(([double]$item.'Percentage Utilization' * 100), 2)
}
# format the mesage body as monospaced formatted list output
$messagebody = '<pre>{0}</pre>' -f ($logs | Format-List | Out-String)
# Import email settings from config file
[xml]$ConfigFile = Get-Content "Settings.xml"
# BTW. it is better to use:
# $ConfigFile = ([System.XML.XMLDocument]::new()).Load('X:\Path\To\Settings.xml')
# because that way you'll get the encoding right.
# You need to load it using the absolute Full path and filename
# Create email splat hashtable
$smtpsettings = #{
To = 'abc#gmail.com', 'xyz#gmail.com'
From = $ConfigFile.Settings.EmailSettings.MailFrom
Subject = "TEST EMAIL"
SmtpServer = $ConfigFile.Settings.EmailSettings.SMTPServer
}
# try to send the email
try {
Send-MailMessage #smtpsettings -Body $messagebody -BodyAsHtml -Encoding utf8 -Verbose -ErrorAction Stop
}
catch {
Write-Warning $_.Exception.Message
}
# move the csv file to an archived location
Move-Item -Path $Csvfile -Destination 'C:\Users\Siddhartha.S.Das2\Desktop' -Force
# if you really also need a json file from the data retrieved from the Csv, convert and save it here:
$data | ConvertTo-Json | Set-Content -Path 'C:\Users\Siddhartha.S.Das2\Desktop\output.json'
Quick example with format specifier p for percentage, see https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings. The inputs are [double] type.
0.842093077,
0.792955,
0.8910225,
0.8724875,
0.852885333,
0.774708,
0.987243333,
0.87078,
0.9565,
0.839393333 | % tostring p
84.21%
79.30%
89.10%
87.25%
85.29%
77.47%
98.72%
87.08%
95.65%
83.94%

form the log file as a table

I have the following code
function ping-test($hosts) {
$conn = [System.Collections.ArrayList]#($hosts)
[int]$hostsamount = $conn.Count
foreach($co in $conn)
{
$check = Test-Connection $co -Count 3 -ErrorAction SilentlyContinue
$zugriffzeit = $check | select ResponseTime | Measure-Object ResponseTime -Average
$avg = [system.math]::Round($zugriffzeit.Average)
$zeit = Get-Date -Format HH:mm:ss
if($check -eq $null)
{
$pcre = Write-Output $co
$pire = Write-Output 'False'
$zure = $null
}
else
{
$pcre = Write-Output $co
$pire = Write-Output 'True'
$zure = Write-Output "$avg ms"
$zure = $zure.Replace(' ','')
}
[void]$re.Add([PSCustomObject] #{PCName=$pcre; PingResult=$pire; Zugriffszeit=$zure; Zeit=$zeit} )
**$log = "Host:{0} Ping: {1} Zugriffszeit: {2} Zeit: {3}" -f $pcre, $pire, $zure, $zeit
$log >> $logpath**
[int]$recount = $re.Count
[int]$eff = $recount - $hostsamount
try {
$re.RemoveRange(0, $eff)
}
catch{
Write-Host $Error
}
}
return $re
}
I use the following code(is in that function)
$log = "Host:{0} Ping: {1} Zugriffszeit: {2} Zeit: {3}" -f $pcre, $pire, $zure, $zeit
$log >> $logpath
the Question is: I want to form a table with the Colums "Host", "Ping", "Zugriffszeit", and "Zeit".
How can I form this table and save as a .txt or .log file somewhere??
Thx for the help
Use the same data as you're outputting!
To export to csv (if you want to re-use the data programmatically later):
$re |Export-Csv $logpath -NoTypeInformation
If you want to ever format it in a nice table again, it's as easy as:
Import-Csv $logpath |Format-Table
If you simply want nicely tabulated output in your log file:
$re |Format-Table |Out-String |Out-File $logfile
#MathiasR.Jessen showed import and export to csv.
But if you are bound to use .txt or .log files (As the aspect of your question says) then use PSCustomObject and Out-File
[PSCustomObject]#{
Host = $pcre
Ping = $pire
Zugriffszeit = $zure
Zeit = $zeit
} | Out-File $logpath
Later import it like:
Get-Content $logpath

Loop Broken? Am I going about this correctly?

Trying to set up a loop that will run test_netconnection to each line in the text file and then output the response into a different file
I have tried different ways of writing it, just not sure where my loop breaks
$file = 'Z:\servers.txt'
$addresses = Get-Content $file
$reader = New-Object IO.Streamreader $file
while ($reader.ReadLine() -ne $null) { }
foreach ($address in $addresses) {
try {
$Test = Test-NetConnection -ComputerName $addresses -Port xxxx -InformationLevel Quiet
Write-Host "$Test"
}
catch {
Out-File z:/output.txt
}
}
Not getting any output sent to my Out-File, I suspect that my loop is broken
If servers.txt contains the following:
www.google.com
www.microsoft.com
www.stackoverflow.com
...
...then you can loop through it, use Test-NetConnection to see if entries are pingable (ICMP), and export the results as a *.CSV file with the following code:
$servers = Get-Content C:\path\to\servers.txt
$servers | % {
Test-NetConnection $_ | Select ComputerName, PingSucceeded | Export-Csv C:\path\to\results.csv -NoTypeInformation -Append
}
You can extend the exported result properties by adjusting the Select portion of the code.
here's a rather overdone version of the idea. [grin] it uses Tet-Connection instead of the newer Test-NetConnection since win7ps5.1 does not have that cmdlet.
if you nave never seen it used before, the #region comments are code folding markers. that lets one fold away the parts that are not currently of interest.
#region - save default prefs
# the Verbose default is screen display OFF
$AllowVerboseOutput = $True
# the Warning default is screen display ON
$AllowWarningOutput = $True
# save the VerbosePref
$OldVPref = $VerbosePreference
if ($AllowVerboseOutput)
{
# enable screen display of Write-Verbose [it's OFF by default]
$VerbosePreference = 'Continue'
}
# save the WarningPref
$OldWPref = $WarningPreference
if (-not $AllowWarningOutput)
{
# DISABLE Write-Warning screen output [it's ON by default]
$WarningPreference = 'SilentlyContinue'
}
#endregion - save default prefs
# if you want _fewer_ reports, use a larger final time unit
# minutes, hours, whatever suits your needs [*grin*]
$TimeStamp = Get-Date -Format 'yyyy-MM-dd_HH-mm-ss'
$SystemListDir = $env:TEMP
$SystemListFile = 'ServerList.txt'
$FullSystemListFile = Join-Path -Path $SystemListDir -ChildPath $SystemListFile
$ReportDir = $env:TEMP
# if you don't want multiple report files, comment OUT this line & UN-comment the line after it
$ReportFile = -join ('ConnectionRemport', '_-_', $TimeStamp, '.csv')
#$ReportFile = -join ('ConnectionRemport', '.csv')
$FullReportFile = Join-Path -Path $ReportDir -ChildPath $ReportFile
$NoResponse = '-- No Response --'
#region - sample data import
# fake reading in a file
# in real life, use the Get-Content line below
# remove the leading "#" on the next line when you are ready to use a real file
#<#
$SystemList = #'
LocalHost
127.0.0.1
10.0.0.1
'#.Split("`n").Trim()
#>
#endregion - sample data import
# remove the leading "#" on the next line when you are ready to use a real file
#$SystemList = Get-Content -Path $FullSystemListFile
$Results = foreach ($SL_Item in $SystemList)
{
Write-Verbose "Connecting to $SL_Item ..."
if (Test-Connection -ComputerName $SL_Item -Count 1 -Quiet)
{
Write-Verbose " System $SL_Item reached successfully."
$TempObject = [PSCustomObject]#{
MachineName = $SL_Item
Status = 'Online'
# the resulting "sortable" date string is yyyy-MM-ddTHH:mm:ss
TimeStamp = (Get-Date).ToString("s")
}
}
else
{
Write-Warning " Unable to reach $SL_Item."
$TempObject = [PSCustomObject]#{
MachineName = $SL_Item
Status = $NoResponse
# the resulting "sortable" date string is yyyy-MM-ddTHH:mm:ss
TimeStamp = (Get-Date).ToString("s")
}
}
$TempObject
} # end = foreach ($SL_Item in $SystemList)
# display $Results on screen
$Results
# save $Results to CSV file
$Results |
Export-Csv -LiteralPath $FullReportFile -NoTypeInformation
#region - restore default prefs
# restore previuos VerbosePref
$VerbosePreference = $OldVPref
# restore previous WarningPref
$WarningPreference = $OldWPref
#endregion - restore default prefs
on-screen output while running ...
VERBOSE: Connecting to LocalHost ...
VERBOSE: System LocalHost reached successfully.
VERBOSE: Connecting to 127.0.0.1 ...
VERBOSE: System 127.0.0.1 reached successfully.
VERBOSE: Connecting to 10.0.0.1 ...
WARNING: Unable to reach 10.0.0.1.
final onscreen output from the $Results collection ...
MachineName Status TimeStamp
----------- ------ ---------
LocalHost Online 2019-08-02T12:16:43
127.0.0.1 Online 2019-08-02T12:16:43
10.0.0.1 -- No Response -- 2019-08-02T12:16:47
CSV file content ...
"MachineName","Status","TimeStamp"
"LocalHost","Online","2019-08-02T12:16:43"
"127.0.0.1","Online","2019-08-02T12:16:43"
"10.0.0.1","-- No Response --","2019-08-02T12:16:47"

Attachments.Add wildcard with Powershell

I have a ZIP file generated with dynamic information (Report_ PC Name-Date_User). However when I go to attach the file I'm unable to use a wildcard. There is only one ZIP file in this directory so using a wildcard will not attach any other ZIP files.
#Directory storage
$DIR = "$ENV:TEMP"
#Max number of recent screen captures
$MAX = "100"
#Captures Screen Shots from the recording
$SC = "1"
#Turn GUI mode on or off
$GUI = "0"
#Caputres the current computer name
$PCName = "$ENV:COMPUTERNAME"
#Use either the local name or domain name
#$User = "$ENV:UserDomainName"
$User = "$ENV:UserName"
#Timestamp
$Date = Get-Date -UFormat %Y-%b-%d_%H%M
#Computer Information
$MAC = ipconfig /all | Select-String Physical
$IP = ipconfig /all | Select-String IPv4
$DNS = ipconfig /all | Select-String "DNS Servers"
#Needed to add space after user input information
$EMPT = "`n"
#Quick capture of the computer information
$Info = #"
$EMPT
*** COMPUTER INFORMATION ***
$PCName
$IP
$MAC
$DNS
"#
# Used to attach to the outlook program
$File = Get-ChildItem -Path $Dir -Filter "*.zip" | Select -Last 1 -ExpandProperty Fullname
$Start_Click = {
psr.exe /start /output $DIR\$Date-$PCName-$User.zip /maxsc $MAX /sc $SC /gui $GUI
}
$Stop_Click={
psr.exe /stop
}
$Email_Click = {
$Outlook = New-Object -Com Outlook.Application
$Mail = $Outlook.CreateItem(0)
$Mail.To = "deaconf19#gmail.com"
$Mail.Subject = "Capture Report from " + $PCName + " " + $User + " " + $Date
$Mail.Body = $Problem.text + $Info
$Mail.Attachments.Add($File)
$Mail.Send()
}
I no longer get an error but the file will not attach the first time around. The second time it will attach but it does the previous .zip not the most recent. I added my entire code
As per the msdn article it shows what the source needs to be which is.
The source of the attachment. This can be a file (represented by the
full file system path with a file name) or an Outlook item that
constitutes the attachment.
Which mean that it does not accept wildcards. To get around this you should instead use Get-ChildItem to return the name of your zip.
$File = Get-ChildItem -Path $Dir -Filter "*.zip" | Select -First 1 -ExpandProperty Fullname
That should return the full path to the first zip. Since Get-ChildItem returns and object we use -ExpandProperty on the Fullname so that you just return the full path, as a string, to the file. -First 1 is not truly required if you really only have the one file. On the off-chance you do including -First 1 will make sure only one file is attached.
Update from comments
I see that you are having issues with attaching a file still. My code would still stand however you might be having an issue with your .zip file or $dir. After where $file is declared I would suggest something like this:
If (! Test-Path $file){Write-Host "$file is not a valid zip file"}
If you would prefer, since I don't know if you see your console when you are running your code, you could use a popup

Script to check files at a server for a certain time

I'd created below script to check files remaining at a particular path in server.
I've below question please help me out.
How to change file.CreationTime to 12 hours format.
How to export the entire contents to file or email.
Kindly help me in fine tuning the below script
$fullPath = "\\server\D$\fn_1"
$numdays = 0
$numhours = 0
$nummins = 1
function ShowOldFiles($path, $days, $hours, $mins)
{
$files = #(get-childitem $path -include *.* -recurse | where {($_.LastWriteTime -lt (Get-Date).AddDays(-$days).AddHours(-$hours).AddMinutes(-$mins)) -and ($_.psIsContainer -eq $false)})
if ($files -ne $NULL)
{
for ($idx = 0; $idx -lt $files.Length; $idx++)
{
$file = $files[$idx]
write-host ("File Name: " + $file.Name, ", Pending Since : " + $file.CreationTime) -Fore Red
}
}
}
ShowOldFiles $fullPath $numdays $numhours $nummins
To dump to a file, replace your whole for loop with this:
$files | select-object Name,#{name="Pending Since";Expression={$_.CreationTime}}|export-csv -notypeinfo -path c:\output.csv;
This will produce a CSV file with all of your files listed, along with their creation time. Save the formatting for your final delivery/presentation to the user (in this case, you could format the columns in Excel & then save as XLSX).
To send via email, you'll probably want to convert it to HTML.
$filesForEmail = $files | select-object Name,#{name="Pending Since";Expression={$_.CreationTime}} | convertto-HTML;
send-mailmessage -to RECIPIENT -from FROM -subject "Your file listing" -body $filesForEmail -BodyAsHTML -smtpserver smtp.yourcompany.com
You can format the CreationTime value by running it through get-date and using the -format command and the standard DateTime formatting options (http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo(VS.85).aspx). Try this line:
write-host ("File Name: " + $file.Name, ", Pending Since : " + $(get-date $file.CreationTime -format "dddd, MMMM d, yyy h:mm tt")) -Fore Red
The main bit here is replacing $file.CreationTime with $(get-date $file.CreationTime -format "dddd, MMMM d, yyy h:mm tt"). That is a fairly standard date/time format with 12 hour formatting. You could get more detailed if you wanted and define the format earlier and only put out relevant info (such as, if days = 0 exclude that from the date format).