Clicky

20230225

Click to run Powershell script

Sometimes it is more convenient to start the Powershell script by just clicking the filename in Windows Explorer. 

Here are a couple of methods to start a Powershell script by clicking the filename or just typing the script name in a command shell.

Method 1: the Shortcut

Create a new shortcut on the Windows desktop or in some folder with the next properties:

  • Create a shortcut on the desktop or in a folder with the next "Target" value:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -noprofile "%1"
  • Associate PS1 files with this shortcut.

It will run the Powershell script but the console will be closed on completion. You may want to add a Start-Sleep or Read-Key command at the end of the script to observe the script output.


 

Method 2: using a Batch file

  • Create a Batch file (c:\Scripts\psRun.bat) with the next content:
@cmd /k powershell -noprofile "%1"

  •  Associate PS1 files with this Batch file.

Now the script runs and leaves the Console open, so the script output can be inspected.


Method 3: embed Powershell code in Batch file

A Batch file will run when clicked. So when we embed the Powershell code in a Batch file we can run the Powershell script from the Batch file. In this case we do not have to change the file association of PS1 files.
 
Running this Batch file:

@echo off
set outFile=%temp%\temp.ps1
del /q %outFile% >nul 2>nul
certutil -decode %0 %outFile% >nul
cmd /k powershell -noprofile -file %outFile%
goto :EOF
 
-----BEGIN CERTIFICATE-----
d3JpdGUtaG9zdCAiKioqIEhlbGxvIHdvcmxkIg0KU2xlZXAgMw==
-----END CERTIFICATE-----

Will give this output:

So how does it work? The Powerschell script is converted to Base64. The Windows utility CERTUTIL is used to encode the Powershell scrip to Base64 and all the script will be between the BEGIN/END certificate markers. This is used by CERTUTIL to recognize the code that needs to be translated back to Powershell code.

So the manual steps to create a script like this is:

1. Use the Batch file decoder code

@echo off
set outFile=%temp%\temp.ps1
del /q %outFile% >nul 2>nul
certutil -decode %0 %outFile% >nul
cmd /k powershell -noprofile -file %outFile%
goto :EOF  

2. Convert the Powershell script to Base64

C:\> certutil -encode HelloWorld.ps1 HelloWorld.ps1.b64

 3. Append the HelloWorld.ps1.b64 output to the decoder code and save it as helloWorld.bat

 

To automate the whole process you may use the next script. Save as: makeBatchFromPowershell.bat:

@echo off

REM --- Usage: %0 {filenam}.PS1

REM --- Variables...
    if %1.==. goto :EOF
    if not exist %1 goto :EOF
    set batchFileName=%1-.bat
    set psCodeBase64=%temp%\%random%-%random%.tmp

REM --- Encode the Powershell file in CERTUTIL's Base64 code...
    certutil -encode %1 %psCodeBase64% >nul
    if %errorlevel% NEQ 0 (
        echo *** Certutil error...
        goto :EOF
    )    

REM --- Part 1: Create batch file - decoder section...
    echo @echo off>%batchFileName%
    echo set outFile=%%temp%%\temp.ps1>>%batchFileName%
    echo del /q %%outFile%% ^>nul 2^>nul >>%batchFileName%
    echo certutil -decode %%0 %%outFile%% ^>nul >>%batchFileName%
    echo cmd /k powershell -noprofile -file %%outFile%% >>%batchFileName%
    echo goto :EOF >>%batchFileName%
    echo. >>%batchFileName%

REM --- Part 2: Append Base64 code to batch file...
    type "%psCodeBase64%" >>%batchFileName%
    del /q %psCodeBase64% >nul

REM --- Show resulting filename...
    echo *** %1 embedded into %batchFileName%

 

Tying it all together:


You may rename the helloWorld.ps1-.bat to whatever BAT filename you want.


20230221

Backup script for Xenserver/XCP-ng Virtual Machine OVA files

The next batch file creates a backup ("vm-export") of Xenserver and XCP-ng Virtual Machines.

Features:

  • On demand backups or scheduled backups
  • Creates logfile on local computer
  • Does not have credentials in the script
  • Will show all VMs when the script is invoked without parameters
  • Shows time/duration of the backup
  • Makes a local copy first (speed!)
  • Can copy to NAS other network location

Maybe first install the XCP-ng-Center-20.04.01.33.msi software.

Running the script without a parameter:

C:\scripts>backupXenVM.bat
*** Tue 02/21/2023 14:38:00.02 --- Start OVA backup -------------------------------------------------------------------
*** Tue 02/21/2023 14:38:00.02 VMs:
*** Tue 02/21/2023 14:38:00.04
     name-label ( RW): Control domain on host: xcp-ng-abcdefg
     name-label ( RW): x16. Airsonic
     name-label ( RW): x14. mailAdmin
     name-label ( RW): x10. ARCHDC001
     name-label ( RW): x15. Roundcube
*** Tue 02/21/2023 14:38:00.29
*** Tue 02/21/2023 14:38:00.29 Usage: backupXenVM.bat "{name-label}"
*** Tue 02/21/2023 14:38:00.29

 

A typical run of the script looks like:

C:\scripts>backupXenVM.bat "x15. Roundcube"
*** Tue 02/21/2023 14:40:11.15 --- Start OVA backup -------------------------------------------------------------------
The command completed successfully.

New connections will be remembered.


Status       Local     Remote                    Network

-------------------------------------------------------------------------------
OK           E:        \\192.168.1.3\Backups\xe
                                                Microsoft Windows Network
The command completed successfully.

*** Tue 02/21/2023 14:40:11.46 Tue 02/21/2023 14:40:11.44 - Backup VM: "x15. Roundcube"
*** Tue 02/21/2023 14:40:11.48
uuid ( RO)           : d2dccf2b-d9d9-e801-1e3f-59f426bc0bb1
     name-label ( RW): x15. Roundcube
    power-state ( RO): running


*** Tue 02/21/2023 14:40:11.66 Shutdown VM with name-label: "x15. Roundcube"...
*** Tue 02/21/2023 14:40:18.56 Backup of VM with name-label: "x15. Roundcube"...
*** Tue 02/21/2023 14:41:39.84 Restarting VM with name-label: "x15. Roundcube"...
*** Tue 02/21/2023 14:41:39.89 Copying file to NAS...
*** Tue 02/21/2023 14:42:50.75 Backup procedure of VM "x15. Roundcube" ready.
02/21/2023  02:41 PM     4,969,158,144 x15. Roundcube.ova
*** Tue 02/21/2023 14:42:51.35 Start  : Tue 02/21/2023 14:40:11.15
*** Tue 02/21/2023 14:42:51.35 VM up  : Tue 02/21/2023 14:41:39.89
*** Tue 02/21/2023 14:42:51.37 End    : Tue 02/21/2023 14:42:51.35
*** Tue 02/21/2023 14:42:51.37 Ready.


@echo off

    cls

::--- User Specific vars and constants...
    set Xenserver=192.168.1.5
    set UNC=\\192.168.1.2\Backups\xe
    set DRV=E:

::--- Vars and constants...
    set xe="C:\Program Files (x86)\XCP-ng Center\xe.exe"
    set t=%date% %time%
    set userName=root
    REM --- Store the Xen server 'root' password in seperate file. Still in plain text, but not part of the script..
    for /f %%i in (c:\scripts\pw.txt) do set pw=%%i
    set localBackupFolder=d:\temp\xe
    md "%localBackupFolder%" >nul 2>nul

    ::--- Remove quotes from VM label-name...
    set nameLabel=%~1
    ::--- Set backup paths...
    set localBackupFile=%localBackupFolder%\%nameLabel%.ova
    set remotefileName="%DRV%\%nameLabel%.ova"

    call :print --- Start OVA backup -------------------------------------------------------------------

::--- No VM label-name provided...
    if %1.==. (

        call :print VMs:
        %xe% -s %Xenserver% -u %userName% -pw %pw% vm-list | find  "name-label"

        call :print
        call :print Usage: %0 "{name-label}"

        goto :EOF

    )


::--- Check VM label-name---
    %xe% -s %Xenserver% -u %userName% -pw %pw% vm-list | find "%nameLabel%" >nul
    if %errorlevel% NEQ 0 (

        call :print VM not found...
        goto :EndAll

    )  


:doBackup
   
    setlocal ENABLEDELAYEDEXPANSION

    net use %DRV% /d /y >nul 2>nul
    md "%DRV%\previous" >nul 2>nul
    net use %DRV% %unc% >nul 2>nul
    net use %DRV%


   
    call :print %date% %time% - Backup VM: "%nameLabel%"
    call :print

    ::--- Show VM data...
    %xe% -s %Xenserver% -u %userName% -pw %pw% vm-list name-label="%nameLabel%"

    ::--- Delete 'previous' backup file and move current backup to 'previous' folder ...
    del /q "%DRV%\previous\%nameLabel%.ova" >nul 2>nul
    move   "%remoteFileName%" "%DRV%\previous\" >nul 2>nul

    ::--- Shutdown VM...
    call :print Shutdown VM with name-label  : "%nameLabel%"...
    %xe% -s %Xenserver% -u %userName% -pw %pw% vm-shutdown name-label="%nameLabel%"
    ping -n 3 localhost >nul

    ::--- Backup VM to local storage...
    call :print Backup of VM with name-label : "%nameLabel%" to "%localBackupFile%"...
    %xe% -s %Xenserver% -u %userName% -pw %pw% vm-export name-label="%nameLabel%" filename="%localBackupFile%" >nul

    ::--- Restart VM...
    call :print Restarting VM with name-label: "%nameLabel%"...
    %xe% -s %Xenserver% -u %userName% -pw %pw% vm-start name-label="%nameLabel%"    
    set VMstart=%date% %time%
   
    ::--- Copy backup to NAS...
    call :print Copying file to NAS...
    dir "%localBackupfile%" | find "%nameLabel%"
    xcopy /y "%localBackupFile%" %DRV%\ >nul
    del   /q "%localBackupFile%" >nul 2>nul
   
    call :print Backup procedure of VM "%nameLabel%" ready.


:EndAll

    net use %DRV% /d /y >nul 2>nul

    call :print Scripts start : %t%
    call :print VM start      : %vmstart%
    call :print Script end    : %date% %time%

    call :print Ready.

goto :EOF

   
:print
   
    echo *** %date% %time% %*
    echo *** %date% %time% %* >> "c:\logs\%nameLabel%.log"

goto :EOF

20230212

Make screenshot and send to email

The next Powershell script makes a screenshot and sends it to your email address by pressing the Control + Alt + P keys simultaniously.

Some highlights:

  • Secure: SMTP account password is bound to USERPROFILE and is never stored in plaintext.
  • Supported email servers: Google, Microsoft (SMTP and Exchange) and other SMTP servers. 
  • Enterprise desktops: The installer will store the script and the encoded password in %LOCALAPPDATA%, so it will work in most enterprise Windows desktops.
  • Install: The script has an installation procedure and password set procedure build-in.

Installation:

  • Store the Powershell code in some location (e.g.: C:\Temp\psEmailScreenshot.ps1)
  • To install the "psEmailScreenshot" script, run the next command:

C:\Temp\> powershell -file psEmailScreenshot.ps1 install

  • An icon is created on the Desktop:

 

  • To set (or change) the SMTP sender's password, issue this command:

 C:\Temp\> powershell -file psEmailScreenshot.ps1 password

How it works: Press: Ctrl+Alt+P keys simultaneously and click "Yes" in the Yes/No dialog. The dialog is shown for 10s. When the "No" button is pressed or when no button pressed, the screenshot will be discarded.



$scriptFilename   = $MyInvocation.MyCommand.Path
$scriptName       = $MyInvocation.MyCommand
$scriptFolder     = "$home\appdata\Local\mailScreenshot"
$smtpPasswordFile = "$scriptFolder\EmailPassword.txt"

$MailTo           = "yourEmailAddress@yourEmailDomain.com"

$yesNoDialog      = $true


#--- MAIN ------------------------------------------------------------------------------------------

if (!(test-path $scriptFolder)) {New-Item $scriptFolder -ItemType Directory | out-null}


#--- Install the script and create a shortcut on the desktop...
if ($($args[0]).toLower() -EQ "install")
{
    #--- Copy the PS file to $home and create a shorcut with HotKey...
    copy-item $scriptFilename "$scriptFolder\$scriptName" -force | out-null

    #--- Create shortcut on desktop...
    $iconFile = "$home\Desktop\Email Screenshot.lnk"
    $hotkey                = "CTRL+ALT+P"
    $wsh                   = New-Object -comObject WScript.Shell
    $Shortcut              = $wsh.CreateShortcut($iconFile)
    $Shortcut.TargetPath   = "powershell"
    $shortcut.Arguments    = " -file $scriptFolder\$scriptName emailscreenshot"
    $shortcut.IconLocation = "SHELL32.dll,24"
    $shortcut.HotKey       = $hotkey
    $shortcut.WindowStyle  = 7 #--- 7 => hidden
    $Shortcut.Save()

    write-host "`n*** $scriptName is installed. Keyboard shortcut: $hotkey"
   
    Exit

}


#--- Store the SMTP sender's password as SecureString. Note: this password is bound to the Windows Account of the logged on user...
if ($($args[0]).toLower() -EQ "password")
{

    $password = Read-Host -assecurestring "Enter sender's email password now"
    $password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
    $password | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File $smtpPasswordFile
    $password = $null

    Exit

}    


#--- Take a screenshot...
$screenPropertiesshotFilename = "$($env:temp)\" + $env:computername + "_" + $env:username + "_" + "$((get-date).tostring("yyyy.MM.dd-HH.mm.ss")).png"

Add-Type -AssemblyName System.Windows.Forms
$screenProperties = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds
$imageObject      = New-Object System.Drawing.Bitmap($screenProperties.Width, $screenProperties.Height)
$graphic          = [System.Drawing.Graphics]::FromImage($imageObject)
$point            = New-Object System.Drawing.Point(0, 0)
$graphic.CopyFromScreen($point, $point, $imageObject.Size);
$cursorBounds     = New-Object System.Drawing.Rectangle([System.Windows.Forms.Cursor]::Position, [System.Windows.Forms.Cursor]::Current.Size)
[System.Windows.Forms.Cursors]::Default.Draw($graphic, $cursorBounds)


#--- Yes/No dialog...
if ($yesNoDialog) {
    $wshell = New-Object -ComObject Wscript.Shell
    $answer = $wshell.Popup("Email screenshot?",10,"Send screenshot to: $EmailTo...",4+32)
    if ($answer -NE 6) {Exit}
}

#--- Email the screenshot file...
$imageObject.Save($screenPropertiesshotFilename, [System.Drawing.Imaging.ImageFormat]::Png)
$MailSubject    = "Screenshot from computer: $($env:computername), user: $($env:username)"
$Attachment     = $screenPropertiesshotFilename


#--- Hotmail...
    # $SmtpServer  = "smtp-mail.outlook.com"
    # $MailFrom    = "yourEmailAddress@hotmail.com"
    # $smtpUser    = $MailFrom

    # $Credentials    = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $smtpUser, (Get-Content $smtpPasswordFile | ConvertTo-SecureString)
    # Send-MailMessage -To "$MailTo" -from "$MailFrom" -Subject $MailSubject -SmtpServer $SmtpServer -UseSsl -Credential $Credentials -Attachments $Attachment


#--- Gmail...
    # $SmtpServer = "smtp.gmail.com"
    # $MailFrom   = "yourEmailAddress@gmail.com"

    # # The password is an app-specific password, you MUST have 2-factor-auth enabled
    # $Password = "dcobnrrnmvrfyuex" | ConvertTo-SecureString -AsPlainText -Force
    # $Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $MailFrom, $Password
    # Send-MailMessage -From $MailFrom -To $mailTo -Subject $MailSubject -SmtpServer $smtpServer -port 587 -UseSsl -Credential $Credential -Attachments $Attachment


#--- Exchange/Outlook...
    # $outlook = new-object -comobject outlook.application
    # $email = $outlook.CreateItem(0)

    # $email.To = "yourEmail@someDomain.com"
    # $email.Subject = $MailSubject
    # $email.Body = " "
    # $email.Attachments.add($Attachment)
    # $email.Send()

    # $outlook.Quit()


#--- hMailserver...
    $SMTPServer       = "mail.yourEmailDomain.com"
    $smtpUser         = "smtp@yourEmailDomain.com"
    $mailFrom         = $smtpUser
    [INT]$SMTPport    = 587 #--- STARTTLS...
    $Credentials    = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $smtpUser, (Get-Content $smtpPasswordFile | ConvertTo-SecureString)
    Send-MailMessage -To $MailTo -from $MailFrom -Subject $MailSubject -SmtpServer $SmtpServer -UseSsl -port $SMTPport -Credential $Credentials -Attachment $Attachment

   
#--- Cleanup...
remove-item $screenPropertiesshotFilename -force

start-sleep 10


20221226

Fritz!Box - telephony logging

This post describes a simple (!) data logging system for those with a Fritz!Box that is also used for wired/DECT telephony.

Many Fritz!Box models have a build-in method to share the telephony data. The data can be obtained through a TCP port on the Fritz!Box. The data sharing port is (fixed to) port 1012. To enable/disable the sharing, dial the next with a phone that is connected to the Fritz!Box:

Enable data sharing:  #96*5*

Disable data sharing: #96*4*


To check if the data sharing is working, use Telnet:


C:\> telnet {IP address of Fritz!Box} 1012

 

You will see a black screen. Make a phone call to the Fritz!Box and you will see something like this:

 

26.12.22 14:37:00;RING;0;0610901234;+31123456091;SIP0;
26.12.22 14:37:01;DISCONNECT;0;0;
26.12.22 14:37:18;CALL;0;0;+31123456091;0610901234;SIP0;
26.12.22 14:37:25;CONNECT;0;0;0610901234;
26.12.22 14:37:34;DISCONNECT;0;8;

 

RING = incoming call

CALL = outgoing call 

CONNECT =  pick up of a phone connected to the Fritz!Box

DISCONNECT = hang up of a phone connected to the Fritz!Box

 

Now, to log data to a log file use this Powershell script:


<#
   
    Date: 26-Dec-2022
    Purpose: Read Fritz!Box' phone data
   
    TCP port: 1012
    Enable port : Dial #96*5*
    Disable port: Dial #96*4*

#>

$fritzBox  = "{Fritz!Box IP address}"
$fritzPort = 1012
$logFile   = "{path to}\Fritz!BoxPhoneData.log"

$now = get-date -Format s
write-output "$now;--- Start Fritz!Box telephony data logging..." | out-file $logFile -append

while ($true) {

    $tcpClient = new-Object System.Net.Sockets.TcpClient($fritzBox,$fritzPort)
    $tcpStream = $tcpClient.GetStream()
    $reader = New-Object System.IO.StreamReader($tcpStream)  
   
    while ($tcpClient.Connected) 
    {
 
        while ($tcpStream.DataAvailable) 
        {
                   
            $response = $reader.ReadLine()
            $now = get-date -Format s
            write-output "$now;$response" | out-file $logFile -append
            write-host "$now;$response"
           
        }

        #--- Poll port every 5s...
        Start-Sleep 5

    }
   
    #--- Recover from a connection loss, e.g. Fritz!Box reboot...
    Start-Sleep 60

}    


You may use a tool like NSSM to create a Windows Service. Then the script will run in the background and it will log all phone calls to the log file.

Real Time Web Analytics