Powershell Output saved to text file and write to console - powershell

i am new to powershell, i am trying to make a simple test-connection script that will ping a camera and say if its up or down and if its down save to a txt file.
My issue is, the output in the console works properly but when i go to view the text file it has other random camera names.
[string[]]$CamIP = Get-Content -Path 'C:\Users\johni\Documents\Cams\CamsIP.txt'
[string[]]$CamName = Get-Content -Path 'C:\Users\johni\Documents\Cams\CamNames.txt'
function Get-TimeStamp { return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date) }
while ($True) {
for ($i = 0; $i -lt ($CamIP.Length); $i++) {
if (Test-Connection $CamIP[$i] -ErrorAction SilentlyContinue) { write-host -ForegroundColor Green -BackgroundColor Black $CamName[$i] "Cam is Up" (Get-Date) }
else {write-host -ForegroundColor Red -BackgroundColor Black $CamName[$i] "Cam is Down" (Get-Date)}
if (Test-Connection $CamIP[$i] -ErrorAction SilentlyContinue) {write-output $CamName[$i] "Cam is Down" (Get-Date) >> 'C:\Users\johni\Documents\Cams\Down Times.txt'}}
Start-Sleep -seconds 5
}

Found the issue,
i forgot to add -NOT on the line that writes to the text file

Related

Powershell not reading .txt lines

I cannot get PowerShell to run this .txt file, what am I doing wrong? I tried changing the name of the .txt and checked passed scripts and everything seems to be the same but I keep getting an error saying that the ".txt in invalid"
$POSName = "$PSScriptRoot\Bex.txt"
foreach ($POS in (Get-Content $POSName)) {
$Bex = Get-Service -ComputerName $POSName | Where-Object { $_.name -eq "BexServ" }
}
If ($Bex -eq $null) {
# Service does not exist
Write-Host " doesn't exist." -ForegroundColor Red
}
Else {
# Service does exist
Write-Host "The $($Bex.Name) service found." -ForegroundColor Green
If ($Bex.Status -eq "Running") {
# Stop Service
Set-Service -status stopped -ComputerName $POSName -name $Box.Name -ErrorAction Stop
Write-Host "The $($Bex.Name) successfully stopped." -ForegroundColor Green
}
else {
#service already stopped
If ($Bex.Status -eq "Stopped") {
Write-Host "The $($Bex.Name) service already Stopped." -ForegroundColor Green
}
}
}
As commented, you are using the wrong variable in the loop. The code is reading the text file just fine, it is Get-Service that cannot deal with a path to a file in the -ComputerName parameter.
Also, the placing of the if..else should be inside the loop, not after.
Try
$POSName = "$PSScriptRoot\Bex.txt"
foreach ($POS in (Get-Content $POSName)) {
$Bex = Get-Service -ComputerName $POS | Where-Object { $_.name -eq "BexServ" }
If (!$Bex) {
# Service does not exist
Write-Host " doesn't exist." -ForegroundColor Red
}
Else {
# Service does exist
Write-Host "The $($Bex.Name) service found." -ForegroundColor Green
If ($Bex.Status -eq "Running") {
# Stop Service
Set-Service -status stopped -ComputerName $POSName -name $Box.Name -ErrorAction Stop
Write-Host "The $($Bex.Name) successfully stopped." -ForegroundColor Green
}
else {
#service already stopped
If ($Bex.Status -eq "Stopped") {
Write-Host "The $($Bex.Name) service already Stopped." -ForegroundColor Green
}
}
}
}
It might also be a good idea to output the computername ($POS) in the Write-Host lines too

Subexpression printing out same strings? Powershell

I have this code which deletes User Profiles off a remote machine. The removal of profiles work just fine but, the Aesthetic of doing so doesn't. What do i mean?
I'm passing the user display names to an index and making a selection out of it, and that works fine in regards to assigning the proper names to the appropriate Index Number its associated to in C:\users.
The next line of code is it grabbing the selections i made, and running through them displaying the same name i did for the index, and then it goes off to delete the CIM instance.
So my question is, why is it not passing the subexpression $userinfo1 that is already made and not putting it into the next block of code, for example, the following works as in grabbing the proper Display Name and assigning it to the proper Number:
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - account not in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
but, the next block doesn't associate the display name (that was captured in $userinfo1) with the number i select and it just continues to display the first display name with the rest of the profiles its reiterating through:
foreach($Profile in $menu[$selection]){
Write-Host "Deleting user: $(,$userinfo1[$selection]) `
ID:$Profile "}
Hopefully this makes sense, and if anyone can point me in the right direction id greatly appreciate it!
Heres the rest of the script, please feel free to use it as it does work for deleting the actual profile off the system and not just the files.
#Deletes a profile properly off remote machine. WARNING: DOES NOT BACK UP DATA! Use at your own peril. Delprofile
$cn = Read-Host -Prompt "Enter Computer Name"
$ping = Test-Connection -ComputerName $cn -Count 1 -Quiet
If($ping -eq $false){ Write-Host "Computer seems to be offline, please check name spelling." -ForegroundColor DarkYellow; Write-Host ""; &PFL-Delete } else {
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - account not in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
foreach($Profile in $menu[$selection]){
Write-Host "Deleting user: $(,$userinfo1[$selection]) `
ID:$Profile "
$del = Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile }
If($del -eq $null){Write-Warning "No CIM instance found on system, profile has been deleted but files persist. Delete manually!"} else{
Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile } | Remove-CimInstance -WhatIf
Write-Host "user profile has been deleted" -ForegroundColor Red
Write-Host ""}
}
}
#CountPs $cn
12/31/2020 - EDIT:
Here is the finished result:
Function Delete-PFL{
#Deletes a profile properly off remote machine. WARNING: DOES NOT BACK UP DATA! Use at your own peril. Delprofile
$cn = Read-Host -Prompt "Enter Computer Name"
$ping = Test-Connection -ComputerName $cn -Count 1 -Quiet
If($ping -eq $false){ Write-Host "Computer seems to be offline, please check name spelling." -ForegroundColor DarkYellow; Write-Host ""; &Delete-PFL } else {
$menu = (get-childitem "\\$cn\c$\users" | sort LastWriteTime -Descending).Name
$userinfo1 = foreach ($user in $menu) {
Start-Sleep -Milliseconds 2
$userinfo = (net user $user /domain | Select-String "Full Name" -ErrorAction SilentlyContinue) -replace "Full Name ", "" 2>&1 | Out-String -Stream
if ($userinfo.Length -lt 4) {
"$user - NO DISPLAY NAME in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 2) {
"$user - ACCOUNT NOT in ADUC" # output
}
else {
if ($LASTEXITCODE -eq 0){
$userinfo # output
}
}
}
}
Write-Warning "Ensure user profiles are no longer active and/or, have profiles be backed-up!"
Write-Host "RESULTS:" -BackgroundColor Black -ForegroundColor White
for ($i=0; $i -lt $userinfo1.Count; $i++) {
Write-Host "$($i): $($userinfo1[$i])"
} #END LIST OF POSSIBLE NAMES
Write-Host ""
Write-Host "For multiple users, seperate using a SPACE(1 2 3)"
$selection = Read-Host "ENTER THE NUMBER of the user(s) or Q to quit"
$selection = $selection -split " "
foreach($index in $selection) {
$Profile = $menu[$index]
Write-Host "Deleting user: $($userinfo1[$index]) `
ID:$Profile "
$del = Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile }
If($del -eq $null){Write-Warning "No CIM instance found on system, profile has been deleted but files persist."
Write-Host "Attempting to delete files, please wait. . ."
Remove-Item -Path "\\$cn\c$\users\$Profile" -Force -WhatIf
Write-Host ""
Start-Sleep -Seconds 2
Write-Host "Checking if Files are still there. . ."
$TestPath = Test-Path -Path "\\$cn\c$\users\$Profile"
If($TestPath -eq $false){ Write-Host "Profile Files have been deleted. `
Continuing. . . ." -ForegroundColor Green
}
} else{
Get-CimInstance -ComputerName $cn -Class Win32_UserProfile | Where-Object { $_.LocalPath.split('\')[-1] -eq $Profile } | Remove-CimInstance -WhatIf
Write-Host "user profile has been deleted" -ForegroundColor Red
Write-Host ""
}
}
}
#CountPs $cn
}
Remember to remove the -whatif parameter. Enjoy!
$selection is an array of indices, so in your foreach loop you must refer to the single index at hand, not to $selection as a whole, to get the desired display output.
The conceptually clearest approach is probably to iterate over the indices contained in $selection:
foreach($index in $selection) {
$Profile = $menu[$index]
Write-Host "Deleting user: $($userinfo1[$index]) `
EDIPI:$Profile "
# ...
}

Export-Csv multiple forloop results

I have multiple forloops running with different variables pulling from .txt files. I want to export this all into a single csv file while having it look just like it does in powershell. Attached is what I get for powershell output and I would like to see the first write-host as a header in excel, followed by server name in column 1 and UP or DOWN in column 2 beneath it.
Powershell
Write-Host "Starting SVR2000MS" -ForegroundColor Yellow
$servers = Get-Content C:\Users\username\Desktop\subfolder\SVR2000.txt
foreach($server in $servers){
if (Test-Connection -ComputerName $server -Count 1 -ErrorAction SilentlyContinue) {
Write-Host "$server, UP" -ForegroundColor Green
}
else{
Write-Host "$server, DOWN" -ForegroundColor Red
}
}
Write-Host "Starting SVR2003DC" -ForegroundColor Yellow
$servers = Get-Content C:\Users\username\Desktop\subfolder\SVR2003.txt
foreach($server in $servers){
if (Test-Connection -ComputerName $server -Count 1 -ErrorAction SilentlyContinue) {
Write-Host "$server, UP" -ForegroundColor Green
}
else{
Write-Host "$server, DOWN" -ForegroundColor Red
}
}
Simple way:
You just print it and put it in a file
Instead of Write-Host ..., do:
"$server,UP" | Out-File test.csv -append
You can have more elaborate options by using objects, but it may be overkill for your needs.
If you want a clean file, you need to delete it at the beginning, because of -append.
BTW, no space after comma in a CSV :)
Edit:
Write-Host writes specifically ... to the host, i.e. the console, so you cannot redirect it elsewhere.

Speed up Test-Connection before Foreach

I made a script to check if users desktop folder are under the cuota limitation, if they're under the cuota limitation the backup to the server will be done correctly.
each user have his computer, so source CSV looks like:
pc1,user1
pc2,user2
pc800,user800
Some computers are Windows Xp and some W7, and the paths can be different 'cause of that I'm using Test-Path
W7 = C:\users\$user\desktop
XP = C:\document and settings\$user\desktop
But Test-Path is SUPER SLOW and I started to use a Test-Connection -count 1 before each Test-path
Anyway, the script still SLOW, in each "bad ping test" I lose lot of time.
CODE:
$csvLocation = '~\desktop\soourceReport.csv'
$csv = import-csv $csvLocation -Header PCName, User
$OuputReport = '~\desktop\newReport.csv'
# info:
# "209715200" Bytes = 200 MB
$cuota = "209715200"
$cuotaTranslate = "$($cuota / 1MB) MB"
Write-Host "Cuota is set to $cuotaTranslate"
$count=1
foreach($item in $csv)
{
write-host "$count# Revisando" $item.User "en" $item.PCName "..." #For debug
if (Test-Connection -Quiet -count 1 -computer $($item.PCname)){
$w7path = "\\$($item.PCname)\c$\users\$($item.User)\desktop"
#echo $w7path #debug
$xpPath = "\\$($item.PCname)\c$\Documents and Settings\$($item.User)\Escritorio"
#echo $xp #debug
if(Test-Path $W7path){
$desktopSize = (Get-ChildItem -Recurse -force $w7path | Measure-Object -ErrorAction "SilentlyContinue" -property length -sum)
write-host -ForegroundColor Green "access succeed"
if($($desktopSize.sum) -gt $cuota){
$newLine = "{0},{1},{2}" -f $($item.PCname),$($item.User),"$("{0:N0}" -f $($desktopSize.sum / 1MB)) MB"
$newLine | add-content $outputReport
Write-Host -ForegroundColor Yellow "cuota exceeded! -- added"
}
else{
Write-Host -ForegroundColor DarkYellow "cuota OK"
}
}
elseif(Test-Path $xpPath){
$desktopSize = (Get-ChildItem -Recurse -force $xpPath | Measure-Object -ErrorAction "SilentlyContinue" -property length -sum)
write-host -ForegroundColor Green "access succeed"
if($($desktopSize.sum) -gt $cuota){
$newLine = "{0},{1},{2}" -f $($item.PCname),$($item.User),"$("{0:N0}" -f $($desktopSize.sum / 1MB)) MB"
$newLine | add-content $outputReport
Write-Host -ForegroundColor Yellow "cuota exceeded! -- added"
}
else{
Write-Host -ForegroundColor DarkYellow "cuota OK"
}
else{
write-host -ForegroundColor Red "Error! - bad path"
}
}
else{
write-host -ForegroundColor Red "Error! - no ping"
}
$count++
}
Write-Host -ForegroundColor green -BackgroundColor DarkGray "All done! new report stored in $report"
To improve it I stored all computers in a $list using another Foreach, before the firstly mentioned SLOW-Foreach loop.
foreach($pcs in $csv){
$alivelist += #( $pcs.PCName )
}
Test-Connection -quiet -count 2 -computer $alivelist
Now, I don't now how to UPDATE or remove the rows ("dead" pc,user) from the SOURCE CSV before to enter into the second Foreach.
I need some of your "magic", or at least some ideas!
thanks
To speed up your script you need to run the checks in parallel (as others have already mentioned). Put your checks and the worker code in a scriptblock:
$sb = {
Param($computer, $username)
if (Test-Connection -Quiet -Count 2 $computer) { return }
$w7path = "\\$computer\c$\users\$username\desktop"
$xpPath = "\\$computer\c$\Documents and Settings\$username.TUITRA..."
if (Test-Path $W7path) {
#...
} elseif (Test-Path $xpPath) {
#...
} else {
#...
}
}
Then run the scriptblock as parallel jobs:
$csv | ForEach-Object {
Start-Job -ScriptBlock $sb -ArgumentList $_.PCName, $_.User
}
# wait for completion
do {
Start-Sleep -Milliseconds 100
} while (Get-Job -State 'Running')
# cleanup
Get-Job | ForEach-Object {
Receive-Job -Id $_.Id
Remove-Job -Id $_.Id
} | Out-File $outputReport
Use a queue if you need to limit the number of parallel jobs.
test-connection is weirdly fast with the -asjob parameter, pinging about 200 computers in 4 seconds:
$list = cat hp.txt
test-connection $list -AsJob ; job | receive-job -wait -AutoRemoveJob

Why does PowerShell script fail to close mspaint when the image's filename contains brackets?

Here's the whole code:
$kansio = ($args[0]).replace("'","")
$tiedostot = 0
Get-ChildItem $kansio | foreach {if ($_.Extension -eq ".jpg"){$_.FullName}} |
foreach {
$id = (Start-process mspaint $_ -ea 0 -PassThru).Id
$vikaTallennus = (Get-ChildItem $_).LastWriteTime
[void] [System.Reflection.Assembly]::LoadWithPartialName("'Microsoft.VisualBasic")
[void] [System.Reflection.Assembly]::LoadWithPartialName("'System.Windows.Forms")
do
{
try
{
if ((Get-Process -Id $id -ea 'stop').WaitForInputIdle())
{
[Microsoft.VisualBasic.Interaction]::AppActivate($id)
[System.Windows.Forms.SendKeys]::SendWait("^s")
[System.Windows.Forms.SendKeys]::SendWait("{esc}")
for ($i=1; $i -le 10; $i++)
{
if ((Get-ChildItem $_).LastWriteTime -gt $vikaTallennus)
{
try
{
Stop-Process -Id $id -force -ea 'stop'
Write-Host "Done: $_" -foregroundcolor Green
break
}
catch
{
Write-Host "Failed to close the process" -foregroundcolor DarkRed
Start-Sleep -milliseconds 100
}
}
}
}
}
catch
{
Write-Host "Failed to catch the process" -foregroundcolor DarkRed
Start-Sleep -milliseconds 100
}
} until ((Get-Process -Id $id -ea 0) -eq $null -or $id -eq $null)
$tiedostot++
}
Write-Host "Files saved: $tiedostot" -foregroundcolor Green
If the filename of a JPG picture contains "[" or "]" PowerShell fails to close MSPaint. Why?
What workarounds are there? Can I use some literalname thing in here?
Yes, if you want to use Get-ChildItem to return files/folders with brackets (and some other weird characters), you should use the -LiteralPath parameter. Here's a decent writeup.
So if you expect such characters in the filenames, and you don't need wildcard support, the pertinent line should be:
if ((Get-ChildItem -LiteralPath $_).LastWriteTime -gt $vikaTallennus)