23 April, 2024

Return an array from PSExec (via Powershell)

 I was 'geekily' excited, when this occurred to me this AM.

Largely because I have been frustratedly  looking for a way to deal with a limitation that PSExec presents, when I am trying to get information from a remote computer.
(More info on PSExec, which is part of the SysInternals suite, here)

When pointing a (PoSh) command at a remote computer via PSExec - Specifically one that returns a result that is an array - Capturing that returned result is not an array - It is a simple string, with multiple lines.

Parsing that returned string is certainly possible - But there are a few steps involve in parsing it.

It would be SO MUCH EASIER if that returned value was still an array... Right?!

Google searching for a way to do this... For YEARS - Has not provided anything on how to keep that array... an array...

This morning... I had a thought...

As it turns out - piping that array to 'ConvertTo-Csv' as part of the command being ran on the remote computer is the key!

Then capturing the returned CSV value and piping it to 'ConvertFrom-Csv' - and Ta-Da! It's an array!

Below is a simple example, and also a more involved sample.

In the more involved sample - I send an actual script to the remote computer and run that script.

There may be a better way to run a multiple line script via PSExec - I am not sure...

But the way I am doing it does make it much easier - If only that I don't have to 'escape' all of the quotes (") and dollar signs ($), with tilde marks (`) all over it (as seen in the simple example).

I certainly am open to suggestions...
(As always - Forgive me for using aliases for commands, that is just how I roll...)

NOTE: I have been in the habit of using this '.split()' for a while, on strings - Just to clean up 'array to string' situations, that invariably end up with leading and trailing spaces:
($Args).Split("`n|`r",[System.StringSplitOptions]::RemoveEmptyEntries) 

$Computer_Name = "<hostname>"

$DiskInfo = (C:\SysInternals\PsExec.exe -s -nobanner \\$Computer_Name /accepteula cmd /c powershell.exe `
"& { get-WmiObject win32_logicaldisk | ? {`$_.DeviceID -eq 'C:'} | Select FreeSpace, Size | ConvertTo-Csv }"`
 2> $null) | ConvertFrom-Csv

######## A more involved example #########

$CMD = 'C:\SysInternals\PsExec.exe -s -nobanner \\$Computer_Name /accepteula cmd /c powershell.exe "& { $C_Line }" 2> $null'

$RAM_Report = @'
$DiskInfo = get-WmiObject win32_logicaldisk | ? {$_.DeviceID -eq 'C:'} | Select FreeSpace, Size 

Get-ComputerInfo | Select `
@{ N = 'Hostname';  E = { $_.CsDNSHostName } },`
@{ N = 'Username';  E = { $_.CsUserName } },`
@{ N = 'Last Boot'; E = { $_.OsLastBootUpTime } },`
@{ N = 'RAM';       E = { "$([math]::round($_.CsPhyicallyInstalledMemory /1MB, 3)) GB" } },`
@{ N = 'Free RAM';  E = { "$([math]::round($_.OsFreePhysicalMemory /1MB, 3)) GB" } },`
@{ N = 'UpTime';    E = { "$($_.OsUptime.Days) Days(s), $($_.OsUptime.Hours) hour(s)" } },`
@{ N = 'DiskSize';  E = { "$([Math]::Round($DiskInfo.Size / 1GB,2)) GB" } },` 
@{ N = 'FreeSpace'; E = { "$([Math]::Round($DiskInfo.FreeSpace / 1GB,2)) GB" } },` 
@{ N = '%Free';     E = { "$(([Math]::Round(($DiskInfo.FreeSpace) / ($DiskInfo.Size),3)) * 100)%" } } | ConvertTo-Csv 
'@
########################################
$OutFilePath = "\\$Computer_Name\c$\Users\Public\Downloads\RAM_Report.txt" 
$RAM_Report | Out-File $OutFilePath -Force

$C_Line = "iex ((New-Object System.Net.WebClient).DownloadString('C:\Users\Public\Downloads\RAM_Report.txt'))"
$Remote_Info = (iex $CMD).Split("`n|`r",[System.StringSplitOptions]::RemoveEmptyEntries) | ConvertFrom-Csv

04 April, 2024

TeamViewer 'Console' - Manual automated update

 Some context may help...

We use TeamViewer to remotely support our users.

I don't know what the app used to manage / connect to others is actually called - So I call it the 'Console'.
'Host' is the client installed on all of the computers we support.

NOTE:
The 'Console' installs to this folder: C:\Program Files\TeamViewer\TeamViewer.exe
The 'Host' installs to this folder: C:\Program Files (x86)\TeamViewer\TeamViewer.exe


The console also acts as a 'Client' - So having the 'Client installed on my home computer, allows me to remote connect to my work computer and WFH easily... And conversely, from work, I can easily access my home computer, if needed.

From home - I kept running into (infrequently) not being able to connect to my work computer...
The computer would show, but as 'offline'...

  1. The updated file / installer would download...
  2. That installer would shutdown the current TeamViewer...
  3. And then the installer would fail to complete

(I think its ESET AV - but I am not going to try and explain this to my boss who controls that config - to much work - He turns everything into an argument.)

The end result - Teamviewer is not running, so I cant connect to the work computer...

I put together the below PoSh Script to get past the above problem...
It is a scheduled task that runs at 3AM every day - It takes about 6 minutes to complete
It downloads the most recent version of the Powershell 'Console'...
Compares the 'version info' of that download, to the 'version info' of the currently installed...

If needed, it proceeds to kill Teamviewer, and run the install from that download.
It presents a pop-up window beforehand with a warning, and other useful info...

After that completes, it restarts TeamViewer.
Verifies the downloaded vs. installed versions.

Even if no updates are needed - Regardless, it checks to see if TeamViewwer is running and if not will (attempt to) restart it.

Additionally, it logs all of the pertinent info (currently set to only hold 50 lines)
Log examples:
2024 APR 04, 03:08:39 AM | ~ UPDATED ~ From ver.: 15.52.3.0,  To ver.: 15.52.4.0 | Tv Running?: yes
2024 APR 04, 03:07:01 AM | Installed: 15.52.3.0 vs. Downloaded: 15.52.4.0 - Update needed?: YES | Tv Running?: yes
2024 APR 03, 03:06:55 AM | Installed: 15.52.3.0 vs. Downloaded: 15.52.3.0 - Update needed?: no | Tv Running?: yes
2024 APR 02, 03:06:55 AM | Installed: 15.52.3.0 vs. Downloaded: 15.52.3.0 - Update needed?: no | Tv Running?: yes


Here is the script (forgive my command abbreviations - That's just how I roll):

$LogFile_Path = "C:\Users\richie\Documents\PoSh_Stuff\TvUpdateReport.txt"

$DL_Path = "C:\Users\Public\Downloads"

$App_Path = "C:\Program Files\TeamViewer\TeamViewer.exe"
Function Running { Get-Process | ? { $_.Path -eq $App_Path } }

Function Report {"$Report`n$(gc $LogFile_Path | Select -First 50 | Out-String)" | Out-File $LogFile_Path -Force}

$URL = "https://download.teamviewer.com/download/TeamViewer_Setup_x64.exe"
$SaveFile = "$DL_Path\$(Split-path $URL -Leaf)" 
iwr -Uri "$URL" -OutFile $SaveFile

$DataInfo = gci $SaveFile | Select Name, FullName

If (!$DataInfo) {Break}
$DL_Path = ($DataInfo).FullName
#####################################
$shellfolder = $null; $shellfolder = (New-Object -COMObject Shell.Application).Namespace($(Split-Path ($DataInfo).FullName))
$shellfile = $null; $shellfile = $shellfolder.ParseName($(Split-Path ($DataInfo).FullName -Leaf))

$Downloaded = $null; $Downloaded = [System.Version]::Parse($($shellfolder.GetDetailsOf($shellfile, 166) )) # 166 is the metatag value for the "File version" tag.
$Installed = $null; $Installed = (Get-Item $App_Path).VersionInfo.FileVersionRaw

$D_version = "$($Downloaded.Major).$($Downloaded.Minor).$($Downloaded.Build).$($Downloaded.Revision)"
$I_version = "$($Installed.Major).$($Installed.Minor).$($Installed.Build).$($Installed.Revision)"
$NeedsUpdate = "no"
If ($Downloaded -gt $Installed) {$NeedsUpdate = "YES"}

$Tv_Running = "NO"; If ( Running ) { $Tv_Running = "yes" }
If ($Tv_Running -ne "yes") {Start $App_Path}

$FormatDate = $((Get-Date).ToString("yyyy '$(((Get-Culture).DateTimeFormat.GetAbbreviatedMonthName(((Get-Date)).Month)).ToUpper())' dd, hh:mm:ss tt"))
$Report = "$FormatDate | Installed: $I_version vs. Downloaded: $D_version - Update needed?: $NeedsUpdate | Tv Running?: $Tv_Running"
Report
##  gc $LogFile_Path

If (!$Downloaded -or !$Installed) {Break}
    If ($Downloaded -gt $Installed) {
        If ($Host.Name -match "ISE") { 
        ''; Write-Host "DOWNLOADED TeamViewer version:" -F 14 -B 2; Write-Host ($Downloaded | Out-String).Trim() -F 10
        ''; Write-Host "INSTALLED TeamViewer version:" -F 5 -B 14; Write-Host ($Installed | Out-String).Trim() -F 14
        }

$PopUpMsg = @"
In about 30 seconds...
TEAMVIEWER WILL SHUTDOWN...
It will be UPDATED... 
From: $Installed 
To  : $Downloaded
THEN Teamviewer will restart.

THIS TAKES ABOUT A MINUTE OR TWO.
You should then be able to reconnect to this comouter if needed.
"@
Msg /TIME:30 * $PopUpMsg
Sleep 29.5

Get-Process | ? {$_.Path -match "TeamViewer.exe" } | Stop-Process -Force
Sleep 3; cmd.exe /c "$SaveFile" /S
Sleep 3; Start $App_Path
}

If (!( Running )) {
Sleep 10; Start $App_Path
}

If ($Host.Name -match "ISE") { If ($Downloaded -le $Installed) { Write-Host "TeamViewer is Up-to-date..." -F 10 -B 2} }

If ($Downloaded -gt $Installed) {
$Prev_I_version = $I_version
$Installed = $null; $Installed = (Get-Item $App_Path).VersionInfo.FileVersionRaw
$I_version = "$($Installed.Major).$($Installed.Minor).$($Installed.Build).$($Installed.Revision)"
Sleep 10
$Tv_Running = "NO"; If ( Running ) { $Tv_Running = "yes" }
If ($Tv_Running -ne "yes") {Start $App_Path}

$FormatDate = $((Get-Date).ToString("yyyy '$(((Get-Culture).DateTimeFormat.GetAbbreviatedMonthName(((Get-Date)).Month)).ToUpper())' dd, hh:mm:ss tt"))
$Report = "$FormatDate | ~ UPDATED ~ From ver.: $Prev_I_version,  To ver.: $I_version | Tv Running?: $Tv_Running"
Report
##  gc $LogFile_Path
}

12 February, 2024

APC by Schneider - Change battery date - Clear battery error.

You may find that ftp first needs to be enabled on the UPS...

Explaining the telnet and ftp enablement bit is the longest part really...
If you already have FTP enabled - and don't care how to do it via CLI...
Scroll down to: '
Here is how to update the 'Battery Date'

#########################################
Check to to see that ftp is enabled first - here:

Enable FTP...

Configuration > Network > FTP Server: 'Enable' (checkbox)

NOTE: You will return to this page to later DISABLE FTP - It is seen as a security risk - Don't leave it enabled

Disable FTP...

Configuration > Network > FTP Server: 'Enable' (uncheck)


It can also be enabled via telnet... In fact, all of this  an be done via CLI...

~~~~~~~~~~~~~~~~~~~~~~~~~~

Note: This command will enable telnet - (needed if you get an error about 'telnet' not being a recognized command):

At an elevated command prompt type: pkgmgr /iu:"TelnetClient"

This enables telnet...
~~~~~~~~~~~~~~~~~~~~~~~~~~

How to get telnet connected - Open a CMD prompt and:

telnet <UPS IP address> 23 (port 23 is the default telnet port)

User Name: apc (the default)

Password: apc (this is the default password - you should change this)

Once logged in, type: ? <and hit enter>

(this just lists all available commands)

Type: ftp <and hit enter>

apc>ftp
E000: Success
Service:        disabled
Ftp Port:       21

apc>ftp ?

Usage: ftp --  Configuration Options
    ftp [-p <port-number>] (21 and 5001-32768)
        [-S <enable | disable>]

NOTE: The '-S' is case sensitive - it is an uppercase letter

ftp -S enable <and hit enter>

apc>ftp -S enable
E002: Success
Reboot required for change to take effect.

Use the reboot command -

apc>reboot <and hit enter>

apc>reboot
E000: Success
Reboot Management Interface
Enter 'YES' to continue or <ENTER> to cancel :

(You do have to reopen the telnet and log back in after a reboot.)

Conversely (once you are done using ftp)...

ftp -S disable <and hit enter>

apc>ftp -S disable
E002: Success
Reboot required for change to take effect.

apc>reboot <and hit enter>

apc>reboot
E000: Success
Reboot Management Interface
Enter 'YES' to continue or <ENTER> to cancel :

OK - With THAT part explained - 

Here is how to update the 'Battery Date'

Take note of the current directory shown at the command prompt - THIS is where the 'config.ini' is going to be sent / saved to.

At the command prompt:

ftp <UPS IP address>

Log in as prompted...

Type THIS command:

get config.ini 

Once that completes (it takes a few seconds)...

Locate 'config.ini' as noted above -
Open it on Notepad (or other text editor - I don't care - I'm not your mom!)

Search for (Ctrl+F) 'BatteryDate' in this file (it was / is on line 368 in mine)...

Edit the correct date value in this format: mm/dd/yyyy

Save the file and close it.

At the ftp prompt type: 'put ' (note the space after 'put' - include that space)

Then drag and drop that 'config.ini' file into the ftp window (or type in the full path... But drag and drop is way easier, tho)

put C:\Users\Yourself\config.ini <and hit enter>

Allow that to complete...

To end the ftp session type: bye <and hit enter>

If you have notifications set up... 

Processing the new config is going to generate about twenty something emails...

One of those emails will include: 'Informational - UPS: Battery life exceeded cleared.'

REMEMBER TO DISABLE FTP
(as explained above)