Something to scroll through...
03 November, 2026
16 October, 2026
Gathering meter reads from Konica printers.
I needed to come up with a way to create some logs of the usage on the fleet of 10 Konica printers across four locations / print servers.
These logs will be used to validate the pricing figures our printer support vendor provides (pricing is based on estimated / averaged usage per device - that data is audited on a quarterly basis, via automated auditing tools on each print server - FM Audit).
Currently - Without any of our own historical data - We have to take the vendors word, that those quarterly averages are 'OK'...
So, someone from accounting asked me for help.
The below script collects settings info for all of the Konica printers on each of the print servers, then uses the 'PortName' (IP address) to run an SNMP query on each device (SNMP must be enabled in the devices network settings).
The critical items are the OID's.
Figuring out which ones pull in the values for 'black' and for 'color' were the first challenge.
Once I got that figured out - It took me a while to find the OID that gathers the devices serial number - I could NOT find this documented anywhere, and AI (Copilot, Gemini, Claude) wasn't able to either.
For this last bit - I ended up walking (snmpwalk) through ALL 7125 OID's to locate the one that gathers the serial number.
wsl snmpwalk -v2c -c public <printer IP> 1.3.6.1 > 'C:\Users\richie\Documents\PoSh_Stuff\OID_walk.txt'
And looking for the serial number I already knew for that printer.
Select-String -Path 'C:\Users\richie\Documents\PoSh_Stuff\OID_walk.txt' -Pattern '<SerialNumber>'
OID_walk.txt:460:iso.3.6.1.2.1.43.5.1.1.17.1 = STRING: "<SerialNumber>"
Posting what I figured out.
I will pipe '$Report' to a Csv, and save it accordingly.
I'll be scheduling this to run on a quarterly basis - And handing the Csv's over to accounting when they ask for them.
$AllKonica = @() ("Site1,site2,site3,site4").Split(',') | % { If (Test-Connection $_ -Count 1 -ErrorAction SilentlyContinue) { $AllKonica += Get-Printer -ComputerName $_ | ? { $_.DriverName -match 'Konica' -and $_.PortName -NOTmatch "portprompt|microsoft|_"} | Select ComputerName, Name, PortName, DriverName } } # (NOTE: You could also just list the name, and port name - This removes the need to use domain auth for the script - Since SNMP does not reqiure authentication) <# $AllKonica = @" "Name","PortName" "S1-PTR-066","192.168.1.66" "S1-PTR-062","192.168.1.62" "S1-PTR-061","192.168.1.61" "S1-PTR-060","192.168.1.60" "S1-PTR-064","192.168.1.64" "S2-PTR--034","192.168.2.34" "S3-PTR-060","192.168.3.60" "S3-PTR-061","192.168.3.61" "S4-PTR-068","192.168.4.68" "S4-PTR-066","192.168.4.66" "@ | ConvertFrom-Csv #> $OIDs = [Ordered]@{ "Model" = ".1.3.6.1.4.1.18334.1.1.1.3.5.2.2.0" "SerialNo" = ".1.3.6.1.2.1.43.5.1.1.17.1" "Total" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.1.1.0" "Print_Black" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.1.2" "Copy_Black" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.1.1" "ScanFax_Black" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.3.1.7.1" "Copy_SingleColor" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.3.1" "Print_Color" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.2.2" "Copy_Color" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.2.1" "Print_2Color" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.4.2" "Copy_2Color" = ".1.3.6.1.4.1.18334.1.1.1.5.7.2.2.1.5.4.1" } $Report = @() $SNMP_Community = "public" $AllKonica | % { $PrinterIP = $_.PortName $PrinterName = $_.Name If (Test-Connection $PrinterIP -Count 1 -ErrorAction SilentlyContinue) { $snmp = New-Object -ComObject OlePrn.OleSNMP try { $snmp.Open($PrinterIP, $SNMP_Community, 2, 1200) $Data = [Ordered]@{ "Printer" = "$PrinterName - $PrinterIP" } foreach ( $Key in $OIDs.Keys ) { try { $Value = $snmp.Get($OIDs[$Key]) if ( $null -eq $Value -or $Value -eq "" ) { $Data[$Key] = 0 } #{ $Data[$Key] = $null } else { $Data[$Key] = $Value } } # END 'try' # 2 # catch { $Data[$Key] = $null } catch { $Data[$Key] = 0 } } # END 'foreach ($Key in $OIDs.Keys)' $TotalColor = 0; $OIDs.Keys | ? { $_ -match "color" } | % { $TotalColor += [int]$Data["$_"] } $TotalBlack = 0; $OIDs.Keys | ? { $_ -match "black" } | % { $TotalBlack += [int]$Data["$_"] } $Report += [PSCustomObject][Ordered]@{ "Model" = $Data["Model"] "SerialNo" = $Data["SerialNo"] "Printer" = $Data["Printer"] "Total" = $Data["Total"] "Black" = $TotalBlack "Color" = $TotalColor } } # END 'try' # 1 catch { Write-Host "Connection failed: $PrintServer -> $PrinterIP ($PrinterName)" -F 14 } finally { $snmp.Close() } } # END 'If (Test-Connection...' else { $Report += [PSCustomObject][Ordered]@{ "Model" = "Unk" "SerialNo" = "Unk" "Printer" = "$PrinterName - Unreacheable" "Total" = "NULL" "Black" = "NULL" "Color" = "NULL" } } # END 'else' } # END '$AllKonica | %' $Report | ft
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 involved 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'...
- The updated file / installer would download...
- That installer would shutdown the current TeamViewer...
- 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.
Looks like newer firmware added a way to do this in the GUI as follows:
(see the last bit below the FTP stuff - For a PowerShell method)
Log into the Web interface / GUI (duh!)
Navigate to:
Configuration > General > User Config file:
'Uploading Configuration INI File'
Download the Config.ini: [Download]
Edit the dates on these two lines:
(line number 368): BatteryDate=05/30/2025
(line number 378): BatteryPack1CartridgeAInstallDate=05/30/2025
Save the file...
Note: You may need to log back into the device (sessions do time out quickly)
Configuration > General > User Config file:
'Uploading Configuration INI File'
[Browse] to and select the file you saved.
[Apply]
The emails:
Expect to see a bunch of 'Informational - Configuration file warning: Invalid value on line ###'
On the 'home' tab you should see:
'UPS: Battery measured life near end cleared'
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously (older firmware versions (I think) this has to be done via telnet and ftp as follows:
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...
Log into the Web interface / GUI (duh!)
Navigate to:
Configuration > Network > FTP Server: 'Enable' (checkbox) [Apply]
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) [Apply]
It can also be enabled via telnet... In fact, all of this can 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) 'Battery' in this file...
(line number 368): BatteryDate=05/30/2025
(line number 378): BatteryPack1CartridgeAInstallDate=05/30/2025
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)
$NewDate = "06/02/2025"
####### You do still have to enable FTP ###########
########### Download a file via FTP ###################
$Username = "admin"
$Password = "<passwords>"
$LocalFile = "C:\Users\<user>\Downloads\UPSConfig.ini" # The folder location, and the name you want the downloaded file to have.
$RemoteFile = "ftp://192.168.48.132/config.ini"
Write-Host "Downloading 'config.ini'..." -F 10
# Create a FTPWebRequest
$FTPRequest = [System.Net.FtpWebRequest]::Create($RemoteFile)
$FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username,$Password)
$FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$FTPRequest.UseBinary = $true
$FTPRequest.KeepAlive = $false
# Send the ftp request
$FTPResponse = $FTPRequest.GetResponse()
# Get a download stream from the server response
$ResponseStream = $FTPResponse.GetResponseStream()
# Create the target file on the local system and the download buffer
$LocalFileFile = New-Object IO.FileStream ($LocalFile,[IO.FileMode]::Create)
[byte[]]$ReadBuffer = New-Object byte[] 1024
# Loop through the download
do {
$ReadLength = $ResponseStream.Read($ReadBuffer,0,1024)
$LocalFileFile.Write($ReadBuffer,0,$ReadLength)
}
while ($ReadLength -ne 0)
$LocalFileFile.Close()
################## Modify the lines to change the battery replacement date ###################
Write-Host "Changing the dates in the 'config.ini'..." -F 10
$UPSConfig = gc $LocalFile
$00 = @('BatteryDate=','BatteryPack1CartridgeAInstallDate=')
$BatteryDate = $UPSConfig.IndexOf( ( $UPSConfig | ? { $_ -match $00[0] } ) ) # Should be line 367
$BP1CartDate = $UPSConfig.IndexOf( ( $UPSConfig | ? { $_ -match $00[1] } ) ) # Should be line 377
If ( $BatteryDate -ne 367 -or $BP1CartDate -ne 377 ) {
Write-Host "Check the file... look for extra lines at the end..." -F 14 -B 5
Write-Host " '" -F 14 -B 1 -N; Write-Host "$($00[0])" -F 12 -B 1 -N;Write-Host "' index value: $BatteryDate" -F 14 -B 1
Write-Host " '" -F 14 -B 1 -N; Write-Host "$($00[1])" -F 12 -B 1 -N;Write-Host "' index value: $BP1CartDate" -F 14 -B 1
& notepad $LocalFile
Return; Break
}
Write-Host "Lines ($BatteryDate and $BP1CartDate) before:" -F 11
$UPSConfig | ? { $_ -match "BatteryDate=|BatteryPack1CartridgeAInstallDate=" }
$UPSConfig[$BatteryDate] = "$($00[0])$NewDate"
$UPSConfig[$BP1CartDate] = "$($00[1])$NewDate"
<#
By default, 'Out-File' encoding is utf16 -- utf16 will not work for this config file
The downloaded file is utf8 without BOM - So the 'ASCII' encoding is what is needed.
utf8 is is the encoding that shows in Notepad when viewing the file.
When using '-Encoding ASCII'... reading the encoding (see below) reports:
BodyName : iso-8859-1
EncodingName : Western European (Windows)
HeaderName : Windows-1252
#>
($UPSConfig | Out-String).TrimEnd() | Out-File -Force $LocalFile -Encoding ASCII
$UPSConfig = gc $LocalFile
# Confirm the dates:
''
Write-Host "Lines ($BatteryDate and $BP1CartDate) after:" -F 10
$UPSConfig | ? { $_ -match "BatteryDate=|BatteryPack1CartridgeAInstallDate=" }
$Choice = ('y','n','x')
Do {
Write-Host "Update the config ? ( Y / N [or 'x']): " -Fore 11 -No; $DoEeeet = Read-host
} Until ($Choice -contains $DoEeeet)
If ($DoEeeet -ne "y") {
Write-Host "Stopping..." -F 14 -B 4
Return; Break
}
################## Push the file back to the device via FTP ###################
$request = [System.Net.FtpWebRequest]::Create($RemoteFile)
$request.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$request.UsePassive = $true
$fileStream = [System.IO.File]::OpenRead($LocalFile)
$ftpStream = $request.GetRequestStream()
$fileStream.CopyTo($ftpStream)
$ftpStream.Dispose()
$fileStream.Dispose()
Return; Break
##################################################################
# Read the encoding of a file:
$reader = [System.IO.StreamReader]::new($LocalFile,[System.Text.Encoding]::default,$true)
$peek = $reader.Peek()
$encoding = $reader.currentencoding #.EncodingName
$reader.close()
$encoding
##################################################################
06 March, 2023
'Bookmarklets' (JavaScript bookmark) - Search for Dell 'Service Tags', or 'Service Code'
I think 'Bookmarklets' have fallen out of fashion - But I still find them handy -
I just made one for Dell.com - To look for service tags, but it also works for the longer 'Service Code' too.
This pops up a window / prompt asking for the (Dell) 'Service Tag', then pulls up the info about it, on the Dell Support site.
The result opens in a new tab.
There are a few caveats* to note - So 'Bookmarklets' can be used in modern browsers (listed below)
There's a few reasons I put this together - Mostly to make it easier to get info on a Dell computers warranty...
Less clicks, no dropdowns... Takes me right to the pertinent info.
Here is the basis:
https://www.dell.com/support/home/en-us/product-support/servicetag/<SERVICE_TAG>
Here is the 'Bookmarklet':
javascript:(function(){var input = window.prompt("Dell Service Tag #:");if (!input) return;input = input.replace(/\s/g, "");var targetUrl = "https://www.dell.com/support/home/en-us/product-support/servicetag/";window.open(targetUrl + input, "_blank");})();
If you are not familiar with how 'Bookmarklets' work...
You are creating a new Bookmark / Favorite... So open the new Bookmark / new Favorite dialog up, to start...
Your call - I put mine on the 'Bookmarks Bar'/ 'Favorites Bar'...
Copy / pasta the entire JavaScript line, as the URL for new Bookmark / Favorite, and give it a name (I called it 'Dell Tag?').
Save it.
When you click on this Bookmark / Favorite, a dialog opens - Enter in the Dell 'Service Tags', or 'Service Code' and click 'OK' or hit enter...
* Caveats:
- It behaves pretty straight forward in Edge, and Firefox, but in Chrome, it was blocked as a pop-up, so I had to click on that notification, and 'Allow' it.
- Also - JavaScript is not enabled / allowed on the 'New Tab' page - So this will not work until some / any other webpage is loaded.
06 January, 2023
PoSh Command History - Create your own
I wasn't sure how helpful, or novel this approach is - but thought I would share it.
> I also posted this on reddit - And someone commented:
"Was Add-History on vacation?"
I replied:
~~~~~~~~~~~~~~~~~~~
You know - When I went looking for ways to do this - That command never presented itself.
I will have to play around with it.
~~~~~~~~~~~~~~~~~~~
Then I replied again the next day, after looking at that command:
~~~~~~~~~~~~~~~~~~~
From what I am seeing - playing around with 'Add-History'...
THAT is much more complicated... Actually a pain in the ass.
This thing I hacked together is MUCH simpler across the board.
Though, all the same - Thanks for pointing that out!
~~~~~~~~~~~~~~~~~~~
From that - I feel like what I put together is rather novel.
Explanation:
I have a script ("disabled termed expired.ps1") that I have scheduled to run at 8:15, am M-F.
It pops up a PoSh CLI window on top of everything else, names it, sizes it (and will kill off any previous instances of that window)*, lists out what AD accounts are disabled, or expired, the reason why (terminated, KnowBe4 training is late...), how long it has been that way, etc...
The meat of that script is wrapped in a function named 'dte' - This way, while the window is open, I can easily refresh the window, with updated results, just by typing 'dte'...
Then I got lazy, and wanted to see if there was a way for that 'dte' command to be in the command history, so I could just 'up-arrow' and hit enter...
You know, rather than having to go through the laborious tasks of finding each of the three letters on the keyboard and having to press each one...
Really - I just wanted to see if I could do it...
Anyway - I added the below to the bottom part of the 'dte' function...
It sets the history for that specific Posh session to 'dte' (I don't think it works / will cause errors if used in ISE).
Obviously - It can be used for all kinds of things...
If you wanted to have several items you want in the history you could do:
"dte
cls
Get-Process" | Out-File $HxPath -Encoding ascii -Force
Or assign that text to a variable:
$Hx = "dte
cls
Get-Process"
$Hx | Out-File $HxPath -Encoding ascii -Force
If ($Host.Name -NOTmatch "ISE") {
# This sets the command history for the PoSh session to 'dte',
# so you can just up arrow, and get the command that re-runs the 'dte' function
$HxPath = "$env:USERPROFILE\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\dte.txt"
"dte" | Out-File $HxPath -Encoding ascii -Force
Set-PSReadLineOption -HistorySavePath $HxPath
}
(Hit the up-arrow '▲' or, type 'dte', and hit 'ENTER' to refresh...)
* If anyone is interested - Here is what I use to create and control the PoSh CLI window:
NOTE:
I like to include the PID in my Posh instances 'WindowTitle' - In case they lock up, so I can easily 'Stop-Process -Id XXXX'
$Window_Title = "Disabled, Terminated, or Expired AD Accounts"
If ($Host.Name -NOTmatch "ISE") {
$ScrWidth = $null; $ScrHeight = $null
$ScrWidth = 135
$ScrHeight = 35
$Already = $null; $Already = Get-Process | ? { $_.MainWindowTitle -match $Window_Title }
If ($Already) { $Already | Stop-Process }
$host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.size($ScrWidth,$ScrHeight)
$host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.size($ScrWidth,$ScrHeight)
$Host.UI.RawUI.WindowTitle = "$Window_Title (PID: $PID)"
############ Keeps the Console window on top ###############
$signature = @'
[DllImport("user32.dll")]
public static extern bool SetWindowPos(
IntPtr hWnd,
IntPtr hWndInsertAfter,
int X,
int Y,
int cx,
int cy,
uint uFlags);
'@
$type = Add-Type -MemberDefinition $signature -Name SetWindowPosition -Namespace SetWindowPos -Using System.Text -PassThru
$handle = (Get-Process -id $Global:PID).MainWindowHandle
$alwaysOnTop = New-Object -TypeName System.IntPtr -ArgumentList (-1)
$type::SetWindowPos($handle, $alwaysOnTop, 0, 0, 0, 0, 0x0003)
############## END - On top ################
} # END 'If ($Host.Name -NOTmatch "ISE")'