How to exit powershell invoked from psexec - batch-file

I am trying to call a powershell script using psexec which connects to remote system
So from "Computer A" I call the batch file Aot.bat which is on "Computer B"
Aot.bat has a line which calls the powershell
powershell.exe %SystemRoot%\system32\aot.ps1 -ComputerName AotServ01 >>\Aot01\AOtShared\Yuma.txt;exit
I can see that the connection is made and powershell runs, but then it does not exit
So please advise what can be done or added
Aot.ps1 reads the Memory and CPU at the present Moment
[cmdletbinding()]
[cmdletbinding()]
param(
[parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerName = $env:computername)
begin {
Get-Module –ListAvailable | import-module
Add-PSSnapin Citrix*
Set-executionpolicy -scope currentuser -executionpolicy unrestricted
$date = Get-Date -Format yyyMMdd
$Zip = "\\na02\KCRC\IRD_KFLOWRPT\Citrix_" + $date + "\Citrix_" + $date + ".txt"
$time = Get-Date -format F
}
process
{
foreach($Computer in $Computername)
{
$free = ( Get-WMIObject Win32_OperatingSystem -ComputerName $computer ).FreePhysicalMemory
$phys = ( Get-WMIObject Win32_OperatingSystem -ComputerName $computer ).TotalVisibleMemorySize
$Percentage = (($phys-$free)/$Phys) * 100
$proc =get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 2
$cpu=($proc.readings -split ":")[-1]
write-host "`r`n*********************************************************************************"
write-host "`r`nComputer: " $computer
write-host "`r`n_________________________________________________________________________________"
write-host "`r`nPercentage Memory user Used: " $Percentage
write-host "`r`n"
write-host "`r`nPercentage CPU Used: " $cpu
write-host "`r`n_________________________________________________________________________________"
write-host "`r`n"
write-host $time
}
}
end
{
write-host "`r`n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
write-host "`r`n END OF FILE "
write-host "`r`n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
}
===============================================================================

Related

Duplicate SQL results from Powershell CSV export

I am trying to output SQL results to a .csv file using Powershell separated by its respective column.
The script I wrote works, but it will duplicate the same result three times in the csv. Even if I have only 1 result from the Select statement from the table, it will output it three times in the .csv file.
I tried using pscustomobject as well. But it throws me an error and does not output anything.
Clear-Variable Results
Clear-Variable Report
[string] $query = "Select Name, Value From options with(nolock) where Name IN('ExportFolder','ImportFolder','GlobalExportFolder'); Select ##ROWCOUNT AS AffectedRows"
[string[]] $servers = #('sqlinstance=mytestdb')
foreach($server in $servers)
{
$instance = ($server -split '=')[0]
$db = ($server -split '=')[1]
Try{
$Results = Invoke-Sqlcmd -ServerInstance $instance -Database $db -Query $query
$ExportFolder = ($Results.ItemArray[1])
$GlobalExportFolder = ($Results.ItemArray[3])
$ImportFolder = ($Results.ItemArray[5])
$Array = '$ExportFolder','$GlobalExportFolder','ImportFolder'
$mail = $Array | Select-Object #{n="SQLServer";e={$instance}},#{n="DBName";e={$db}}, #{n="ExportFolder";e={$ExportFolder}}, #{n="GlobalExportFolder";e={$GlobalExportFolder}}, #{n="ImportFolder";e={$ImportFolder}}
$mail | Export-Csv -Path "C:\Users\localadmin\Documents\Logs\HostNameCheck.csv" -NoTypeInformation -Append -Verbose
$Report += $Results
$Report | Select Name, Value | Export-Csv -Path "C:\Users\localadmin\Documents\Logs\SFTPHostnameModificationCheck.csv" -NoTypeInformation -Append -Verbose
Clear-Variable Results
Clear-Variable Report
}
Catch {
Write-Host ("Error: Data retrieval failed against instance $instance for $db" + " - " + (Get-Date)) -ForegroundColor Red
Write-Output ("Error: Data retrieval failed against $instance on $db" + " - " + (Get-Date)) | Out-File -FilePath $PathFailedLogs -Append
}
}
$attachment = Get-ChildItem -Path "C:\Users\localadmin\Documents\Logs" -Include *.csv -Recurse -Force
Send-MailMessage -From "test#test.com" -To "localadmin#nonprod.com" -Subject "SFTPHostnameCheck" -SmtpServer "localrelay#local.com" -Attachments $attachment
Using PSCustomObject
$obj = New-Object [PSCustomObject] -Property #{
'SQLServer' = $instance
'DBName' = $db
'ExportFolder' = "$ExportFolder
'GlobalExportFolder' = $GlobalExportFolder
'ImportFolder' = $ImportFolder"
}
$list += $obj
$list | Export-Csv -Path "C:\Users\localadmin\Documents\Logs\HostNamecheck.csv" -NoTypeInformation -Append -Verbose

Why the powershell script is not invoke inside my powershell program

Hi i am quite frustrated with powershell. i want to use my powershell script to call another powershell script inside it.
My powershell only do the first part which is download files and does not invoke the following powershell script...
Can not think the reason behind it.. Could anyone help me with that? Thanks!
The Program looks like :
$invocation = (Get-Variable MyInvocation).Value
$directorypath = Split-Path $invocation.MyCommand.Path
Set-Location $directorypath
if ($StepNumber -ne 2) {
$MyName = $MyInvocation.InvocationName
$LogFileName = ".\Logs\" + [System.IO.Path]::GetFileNameWithoutExtension($MyName) + [String]::Format("_{0:yyyy-MM-dd_HH-mm-ss}.Log", (Get-Date))
if (!(Test-Path ".\Logs")) {
$X = New-Item -path . -name Logs -ItemType directory
}
&PowerShell.exe -noProfile -File NSCCMPIDDownload.ps1 2 *>`&1 | Tee $LogFileName
exit
}
## ## ---- STEP2 ------------DownLoad the file from Website
$ErrorActionPreference = "Continue"
Write-Host Running NSCC-MPID-Download at $(Get-Date)
Write-Host ""
## Download the NSCC-MPID-Directory.xls from website
$datestr=$(Get-Date).ToString("yyyy-MM-dd")
$url="http://www.dtcc.com/~/media/Files/Downloads/client-center/NSCC/NSCC-MPID-Directory.xls"
$destination = $directorypath +"/NSCC-MPID-Directory"+"_"+$datestr+".xls"
## if destination does not exsist, creat a new one.
if (!(Test-Path $destination) ) {
New-Item $destination -type file -force
}
$client = new-object System.Net.WebClient
$client.DownloadFile( $url, $destination)
Write-Host Running NSCCMPIDInsertion.ps1
$cmd = "$directorypath\NSCCMPIDInsertion.ps1"
Invoke-Expression "$cmd"
Write-Host Running NSCCMPIDAfterParse.ps1
$cmd = "$directorypath\NSCCMPIDAfterParse.ps1"
Invoke-Expression "$cmd"
Write-Host --------------------------------------------------------------------------
Write-Host Process ended at $(Get-Date)

Compare values with previous loop

I made this script to monitor free space of server partitions. Now I'm trying to compare latest loop with previous one with storing free space values to arrays, but I've never done anything like this.
I don't know how exactly to work with arrays - one has to be erased at the beginning of the loop and second has to store the previous values, then they have to be compared.
Could you please give me at least a hint?
#play star-wars imperial march
function play-alarm {
Start-Job {
[Console]::Beep(440,500)
}
}
$cred = Get-Credential domain\username
$hostname = "server1"
#if free space lower, then play alarm
$low_level = "10"
#get drives letters
$partitions = Get-WmiObject Win32_LogicalDisk -Computername $hostname -Credential $cred | foreach DeviceID
#create arrays
$old_values = New-Object System.Collections.ArrayList
$new_values = New-Object System.Collections.ArrayList
#noob loop
$repeat = "2"
while ($i -lt $repeat) {
Write-Host " *** " -ForegroundColor Yellow
Write-Host " *** " -ForegroundColor Yellow
Write-Host " *** " -ForegroundColor Yellow
Write-Host "Free space at server:" -BackgroundColor Black
#backup previouse values and clear array for new ones
$old_values = $new_values
$new_values.Clear()
foreach ($partition in $partitions) {
$device = "DeviceID='" + $partition + "'"
$size = Get-WmiObject Win32_LogicalDisk -Credential $cred -ComputerName $hostname -Filter $device |
ForEach-Object {$_.Size}
$size = [Math]::Round($size/1GB)
$free = Get-WmiObject Win32_LogicalDisk -Credential $cred -ComputerName $hostname -Filter $device |
ForEach-Object {$_.FreeSpace}
$free = [Math]::Round($free/1GB)
Write-Host $free
#add value rounded to GB
$new_values.Add($free)
#if device is CD-ROM, the size or space is zero - this cause error when calculating percentage of free space
if ($size -eq "0") {
Write-Host "disk " $partition "is CD-ROM" -ForegroundColor Yellow
} else {
$perc = ($free/$size)*100
$perc = [Math]::Round($perc, 3)
if ($perc -le $low_level) {
Write-Host "Not enough free space at partition " $partition "!!!" $perc "%" -BackgroundColor Red #| play-alarm
Start-Sleep -s 15
} else {
Write-Host "disk " $partition "is OK - " $perc "%" -ForegroundColor Green
}
}
}
if ($old_values -eq $new_values) {
Write-Host "no change..."
} else {
Write-Host "Attention - change!!!" -BackgroundColor Red
}
$time = $((Get-Date).ToString())
Write-Host "Loop finished..." $time -BackgroundColor Black
Write-Host " *** " -ForegroundColor Yellow
Write-Host " *** " -ForegroundColor Yellow
Write-Host " *** " -ForegroundColor Yellow
Start-Sleep -s 300
}
Copy previous values
$old_values = $new_values
$new_values.Clear()
The problem with this is $old_values is now referring to the same array as $new_values. Use
$old_values = $new_values.Clone()
to create a copy.
Compare
You don't want to use -eq to compare the contents of container objects. You can loop through the arrays and compare each element
for ($index=0;$index -lt $old_values.count;$index+=1) {
if ($old_values[$index] -ne $new_values[$index]) {
write-host "Changed!"
break
}
}
Or you can use Compare-Object
if (Compare-Object $old_values $new_values) {
write-host "No Change!"
} else {
write-host "Changed!"
}
Compare-Object returns a list of differences. If the contents you compare are the same, then it returns an empty (and boolean false) value.
If you want to compare current values to those of a previous run you need to store the current values somewhere at the end of the script (in a CSV file for instance), and read that file (if it exists) at the beginning of the script.
$csv = 'C:\path\to\data.csv'
if (Test-Path -LiteralPath $csv) {
$old_values = Import-Csv $csv
} else {
# initialize $old_values as an empty array if the file doesn't exist (yet)
$old_values = #()
}
...
$new_values | Export-Csv $csv -NoType
Build $new_values as a list of custom objects by selecting the relevant properties of the WMI data. Avoid formatting the diskspace data unless it's for output to the user.
$new_values = Get-WmiObject Win32_LogicalDisk -Computer $hostname -Credential $cred |
Select-Object DeviceID, Size, FreeSpace
You can use calculated properties for adding derived information (like the free disk space ratio):
... | Select-Object DeviceID, Size, FreeSpace, #{n='Ratio';e={$_.FreeSpace/$_.Size}}
Use Compare-Object for comparing old and new data:
Compare-Object -ReferenceObject $old_values -DifferenceObject $new_values -Property DeviceID, Size, FreeSpace

Debugging disk space cleanup script - Deleting files it should not be

I am working on a disk space script for our clients in my off time. I just tested it using the ISE, and it looks like it was working until I checked the transcript.
There are sections during the first removal cycle around line 32 where it is removing files in C:\Windows\System32\, which of course I didn't want it to. I am sure I did something wrong, but I have checked for typos, and I do not understand how it can get %system32% from a users directory.
If (([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
{
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + ' (Elevated)'
$Host.UI.RawUI.BackgroundColor = 'DarkBlue'
Clear-Host
}
Else
{
$newProcess = New-Object Diagnostics.ProcessStartInfo 'PowerShell'
$newProcess.Arguments = "& '" + $script:MyInvocation.MyCommand.Path + "'"
$newProcess.Verb = 'runas'
[Diagnostics.Process]::Start($newProcess) | Out-Null
exit
}
If ((Test-Path "C:\DiskSpaceCleanupLog\") -eq $False)
{
New-Item -ItemType Directory -Path "C:\DiskSpaceCleanupLog\"
}
$Date = [string]::Format( "{0:dd-MM-yyyy}", [datetime]::Now.Date )
$LogName = "C:\DiskSpaceCleanupLog\" + $Date + "Log.txt"
Start-Transcript $LogName
$Path = #()
$Array = #(Get-ChildItem C:\Users | Select-Object Name)
Read-Host -Verbose "Removing User Account temp files..."
Foreach ($Name IN $Array)
{
$Path = ("C:\Users\" + $Name.Name + "\AppData\Local\Temp\")
}
Foreach ($Path IN $Array)
{
Get-ChildItem | Remove-Item -Recurse -WhatIf
}
Remove-Variable Path
Read-Host -Verbose "Removing User Account crash dumps..."
Foreach ($Name IN $Array)
{
$Path = ("C:\Users\" + $Name.Name + "\AppData\Local\CrashDumps\")
}
Foreach ($Path IN $Array)
{
Get-ChildItem | Remove-Item -Recurse -WhatIf
}
Remove-Variable Path
Read-Host -Verbose "Removing User Account reporting files..."
Foreach ($Name IN $Array)
{
$Path = ("C:\Users\" + $Name.Name + "\AppData\Local\Microsoft\Windows\WER\ReportArchive\")
}
Foreach ($Temp IN $Path)
{
Get-ChildItem | Remove-Item -Recurse -WhatIf
}
Remove-Variable Path
Read-Host -Verbose "Removing User Account temp files from Internet Explorer..."
Foreach ($Name IN $Array)
{
$Path = ("C:\Users\" + $Name.Name + "\AppData\Local\Microsoft\Windows\Temporary Internet Files\")
}
Foreach ($Temp IN $Path)
{
Get-ChildItem | Remove-Item -Recurse -WhatIf
}
Read-Host -Verbose "Removing Recycle Bin files..."
Remove-Item -LiteralPath 'C:\$Recycle.Bin\' -Recurse -WhatIf
Read-Host -Verbose "Removing global crash dumps..."
Remove-Item "C:\ProgramData\Microsoft\Windows\WER\ReportQueue" -Recurse -WhatIf
Remove-Item "C:\ProgramData\Microsoft\Windows\WER\ReportArchive" -Recurse -WhatIf
Read-Host -Verbose "Removing Windows Update cached files..."
Stop-Service -DisplayName 'Windows Update'
Remove-Item "C:\Windows\SoftwareDistribution\Download\*" -Recurse -WhatIf
Start-Service -DisplayName 'Windows Update'
Remove-Variable Array, Path
Read-Host -Verbose "Cleaning base image of update cache..."
DISM.exe /Online /Cleanup-Image /SPSuperseded
Read-Host -Verbose "Running Windows Clean Manager..."
$OSVersion = Get-WMIObject -Class Win32_OperatingSystem | Format-Table Version
If ($OSVersion -le 6.1)
{
cleanmgr.exe /verylowdisk
}
Read-Host -Verbose "Removal is complete. Sending logs..."
Stop-Transcript
$SecurePassword = ConvertTo-SecureString "InsertPasswordHere" -AsPlainText -Force
$emailcredential = New-Object System.Management.Automation.PSCredential ("email#domain.com", $SecurePassword)
Send-MailMessage -To "Name Here <email#domain.com>" -From "Name Here <email#domain.com>" -Subject ("Disk Space Cleanup Log - " + $Date) -Body "Attached is the log from the script." -Attachments $LogName -SmtpServer "smtp.office365.com" -Credential $emailcredential -UseSSL -Port "587" -DeliveryNotificationOption OnFailure
Line 32 is Get-ChildItem | Remove-Item -Recurse -WhatIf
The are several things that should be adjusted in your code but the issue that is befalling you now is that you have not specified a -Path. Therefore Get-ChildItem will be returning items from the working directory!
Get-ChildItem | Remove-Item -Recurse -WhatIf
Should be instead
Get-ChildItem $path | Remove-Item -Recurse -WhatIf
Like I said though there are several potential pitfalls and areas of improvement there to be addressed. You use the same loop 5 times. A couple are exactly the same.
I believe the issue is on line 23, where the code is not populating the array with full pathnames. See Get full path of the files in PowerShell for some advice on how to get the full pathnames instead.

Ping a list of host names and output the results to a csv in powershell

I have a large list of hostnames I need to ping to see if they are up or down. I'm not really that great at scripting but I managed to figure this much out:
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Write-Host "$name is up" -ForegroundColor Green
}
else{
Write-Host "$name is down" -ForegroundColor Red
}
}
This gets me what I need but i now need to write out these results to a csv file and i have no idea how to do that.
Please Help!
You can use the following code instead (I simply altered the write-host calls to CSV formatting) and execute it with "PowerShell.exe script.ps > output.csv"
Note that you must execute it from the folder that contains hnames.txt, or simply change the "hnames.txt" to a full path.
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
Write-Host "$name,up"
}
else{
Write-Host "$name,down"
}
}
P.S. You can also use the Out-File Cmdlet to create the CSV file
I am a complete newbie to Powershell, so I took this on as a learning task, as I needed a quick and simple way to check a list of PC's for up/down status. These tweaks were needed to get it to output cleanly to the screen and to a txt file
$Output= #()
$names = Get-content "hnames.txt"
foreach ($name in $names){
if (Test-Connection -ComputerName $name -Count 1 -ErrorAction SilentlyContinue){
$Output+= "$name,up"
Write-Host "$Name,up"
}
else{
$Output+= "$name,down"
Write-Host "$Name,down"
}
}
$Output | Out-file "C:\support\result.csv"
$Output= #()
$names = Get-Content ".\input\Servers.txt"
foreach ($name in $names){
if (Test-Connection -Delay 15 -ComputerName $name -Count 1 -ErrorAction SilentlyContinue -quiet){
$Output+= "$name,up"
Write-Host "$Name,up" -ForegroundColor Green
}
else{
$Output+= "$name,down"
Write-Host "$Name,down" -ForegroundColor Red
}
}
$Output | Out-file ".\output\result.csv"
This is a tad cleaner, and includes the original foreground options but, BTW, the 'delay' switch seems to be ignored -PB
I would do it this way. Using a list of computers and -asjob works very well. The Responsetime property (confusingly the header is "Time(ms)") will be non-null if the host is up.
$names = Get-content hnames.txt
test-connection $names -asjob -count 1 | receive-job -wait -auto
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
COMP001 yahoo.com 74.6.231.21 32 39
COMP001 microsoft.com 40.113.200.201 32
Lately I do it this way. It requires threadjobs installed in powershell 5.1. Or just use get-port. I stick it in a mymod\mymod.psm1 module file somewhere in $env:psmodulepath. I can check a classroom in under 10 seconds.
function get-pport { # multi-threaded
param($list)
$list |
% { $_ | start-threadjob { get-port $input } -throttlelimit 20 } |
receive-job -wait -auto
}
function Get-Port {
Param (
[parameter(ValueFromPipeline)]
[string[]]$Hostname='yahoo.com'
)
begin {
$ports = 22,5988,3389,5985
$ping = New-Object System.Net.Networkinformation.ping
$Timeout = 200 # ms
}
process {
$hostname | foreach {
$openPorts = #()
foreach ($port in $ports) {
$client = New-Object System.Net.Sockets.TcpClient
$beginConnect = $client.BeginConnect($_,$port,$null,$null)
Start-Sleep -Milli $TimeOut
if($client.Connected) { $openPorts += $port }
$client.Close()
}
$result = $Ping.Send($_, $timeout)
if (! $result) { write-error "hostname $_ not found" }
$pingstatus = ($result.status -eq 'Success')
New-Object -typename PSObject -Property #{
HostName = $_
Port = $openPorts
Ping = $pingstatus
} | select hostname,port,ping
} # end foreach
} # end process
}
Example:
$avid = cat afile.txt
pport $avid
HostName Port Ping
-------- ---- ----
A006 {3389, 5985} True
A011 {3389, 5985} True
A015 {3389} True

Resources