Blog

Eastern is sharing technical stuff about IT
Workplace & VDI Test-automation with Powershell

Why to do test automation with PowerShell?
I was involved in a project to implement a new Virtual Desktop Infrastructure (VDI) within a large company

To ensure the environment is stable and well-functioning all the time on the same way, I made a script to test the basic functionality of the virtual desktop image. The script also measures the time it takes to complete an action, so this can be used to set a baseline for performance measurement.

The basic functionality which is scripted is:

  • Start Outlook and Send an email
  • Can the user browse the internet
  • Open a Word document, Print Preview, Print, and save the document
  • Open an Excel sheet, Save it under a different name
  • Browse the AppV content store
  • Start an AppV application

Other actions can be scripted as well and added to the script.

When the script has run completely with success it will ensure you that the basic functionality is still working.

See the script in action
This video gives a quick overview what kind of actions are scripted and how it works.

Workplace and VDI test automation with PowerShell from EasternNL on Vimeo.

What kind of results do the script make?
When using the script a PDF document is generated which shows the following for each step:

2015-09-23_164405

An example PDF document can be found here: 2015-09-23 163927 Workplace and VDI test-automation with PowerShell.pdf

Also an .su file is generated which has the following content:

2015-08-26    07:23:13.439    07:23:14.48    0.6092069    Excel - start    C:\Users\Erik\AppData\Local\Temp\2015-08-26 07.23.13.439 Excel - start.png

This file is tab-separated and can be imported in Excel or other tools for further investigation.

An example SU file can be found here: test.su

My baseline tools
The script is tested on:

  • Windows 7 with Office 2010
  • Windows 10 with Office 2013

Powershell version 3 or higher is enough to run this script. I am sure this will work on other platforms like Windows 8 e.g. as well.

The iTextSharp module is needed to generate the PDF’s and can be found here: http://sourceforge.net/projects/itextsharp/

Boxcutter.exe is used to make screenshots during the test. Boxcutter can be found here: http://keepnote.org/boxcutter/

Script code
Basically the following code is needed to start Excel:

$excel = new-object -comobject Excel.Application
$excel.visible = $True

Other example is the code to start Internet Explorer:

$ie = new-object -com "InternetExplorer.Application"
$ie.visible = $true

(In a second blog item I will explain how to do the automation of browsing on the internet.)

Basically it is simple, however in my coding some extra steps are involved to measure the start of Excel, and to make a screenshot to ensure everything went well. The Write-Transaction function is included in my module and will write the measured times to the .su file and take the screenshot as well.

$transactionname = "Excel - start"
Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $excel = new-object -comobject Excel.Application
            $excel.visible = $True
            } ) 

In the end of the script the complete .su file is converted to PDF format together with the screenshots.

Conclusion
Windows endpoint testing, for VDI but also for physical desktop can be done with scripts. When launching every day, and eventually on different times of a day the performance and functionality of an Windows desktop can be measured.

The complete script

# load the modules
Import-Module "$(Split-Path -parent $PSCommandPath)\Test-Availability.psm1" -Force
Import-Module "$(Split-Path -parent $PSCommandPath)\iTextSharp.psm1" -Force

# -------------------------------------------
# configure variabeles

$testdocument = "$(Split-Path -parent $PSCommandPath)\testdocument.docx"
$testworkbook = "$(Split-Path -parent $PSCommandPath)\testworkbook.xlsx"
$printer = "Bullzip PDF Printer"
$appvcontentstore = "C:\AppV"

# -------------------------------------------

# Stop if an error has occured, will be handled by the try/catch
$ErrorActionPreference = "Stop"

# Verbose messages on
#$VerbosePreference = "Continue"
# wait time between the steps
$sleepseconds = 1
# temporary path to log the output to
$outputfile = "$($env:temp)\test.su"
if (Test-Path $outputfile) { rm $outputfile }



try {

    # Start Outlook 
        $transactionname = "Outlook - Start"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $outlook = new-object -com "Outlook.Application"
            #$outlook.visible = $true
            $namespace = $outlook.GetNamespace("MAPI")
            $folder = $namespace.GetDefaultFolder(16)
            $folder.Display()
            } )

        # Write email
        # (not sending an email because that is blocked by default in Outlook)
        $transactionname = "Outlook - Write message in draft"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $mail = $outlook.CreateItem(0)
            $mail.Subject = "$(Get-Date -format "yyyy-MM-dd HH:mm:ss") VDI Test Script"
            $mail.To = "user@domain.com"
            $mail.Body = "Testing the VDI functionality with a script"
            $mail.save()
            } )

        # Close Outlook 
        $transactionname = "Outlook - Exit"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $outlook.Quit()
            } )
            
    # Internet Explorer - eastern.nl
        $transactionname = "Internet Explorer - Start"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $ie = new-object -com "InternetExplorer.Application"
            $ie.visible = $true
    
            Set-ForegroundWindow "Internet Explorer"
            } )
    
        # navigate
        $transactionname = "Internet Explorer - www.eastern.nl"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $ie.Navigate("http://www.eastern.nl/")
    
            # wait until the page is ready, all parameters okay and the text Erik van Oost is inside the document
            Wait-InternetExplorer -ie $ie -text "Erik van Oost"
            } )
        
        $transactionname = "Internet Explorer - Quit"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            # Internet Explorer Exit
            $ie.quit()
            } )
	
        # Word
        $transactionname = "Word - Start"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $word=new-object -ComObject "Word.Application"
            $word.visible = $true
            } )
    
        # Document open 
        $transactionname = "Word - Open testfile"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $doc = $word.Documents.Open($testdocument)
            } )

        # Document saven op temp
        $transactionname = "Word - Save testfile"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $filename = "$($env:temp)\test.docx"
            $file_formatcode = 12

            if (Test-Path filename) { rm $filename -Force -ErrorAction SilentlyContinue }
            $doc.SaveAs([ref]$filename, [ref]$file_formatcode)
        } )

        $transactionname = "Word - Print preview"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $doc.PrintPreview()        
            } )

        <#
        $transactionname = "Word - Print"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            # set active printer             
            $word.activeprinter = $printer
            
            # Document afdrukken
            $doc.PrintOut([ref]$false)
            } )
        #>

        $transactionname = "Word - Quit"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            # Close document
            $doc.Close()
            # Exit Word
            $word.Quit()
                } )

    # Start Excel 
        $transactionname = "Excel - start"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $excel = new-object -comobject Excel.Application
            $excel.visible = $True
            } )
    
        # Open workbook
        $transactionname = "Excel - Open testfile"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $wb = $excel.Workbooks.Open($testworkbook)
            } )
    
        # Save workbook
        $transactionname = "Excel - Save testfile"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $filename = "$($env:temp)\test.xlsx"        
            $excel.displayalerts = $false
            $wb.SaveAs($filename)
            } )
    	
    # Close Excel
        $transactionname = "Excel - Quit"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $excel.quit()
        } )

    # Visit AppV Content Store
        $transactionname = "AppV - ContentStore"
        Write-Transaction -transactionname $transactionname -sleepseconds $sleepseconds -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $app = Start-Process cmd.exe -PassThru -ArgumentList " /k dir `"$($appvcontentstore)`" /ogneds" -WorkingDirectory "C:\" 

            } )
        
            # Content store Sluiten
            Stop-Process -Id $app.Id 

    # AppV Application
    # %ALLUSERSPROFILE%\Microsoft\AppV\Client\Integration\4C39F850-61B3-4ED5-95E0-0F21C0B6D0BB\Root\DLS.exe
        $transactionname = "AppV Application Dymo Label Writer"
        Write-Transaction -transactionname $transactionname -sleepseconds 5 -outputfile $outputfile -screenshot $true -measured ( Measure-Command {
            $app = Start-Process  C:\ProgramData\Microsoft\AppV\Client\Integration\4C39F850-61B3-4ED5-95E0-0F21C0B6D0BB\Root\DLS.exe -PassThru 

            } )
        
            # Content store Sluiten
            Stop-Process -Id $app.Id 

   

    # End of test
        $result = Show-PopUp -Message "Test ended sucessfully" -Title "Test Automation" -ButtonSet OK -IconType Information
}
catch
{
    # log if an error occurs, including a screenshot with the error
    $Error

    Write-Transaction -transactionname "ERROR $transactionname" -sleepseconds 0 -outputfile $outputfile -screenshot $true -measured ( Measure-Command { sleep -Milliseconds 100 } )

    $result = Show-PopUp -Message "Test ended with error" -Title "Test Automation" -ButtonSet OK -IconType Exclamation
}

# Ask for conversion to PDF
    $result = Show-PopUp -Message "Do you want to write the results to a PDF file?" -Title "PDF file" -ButtonSet YNC -IconType Question

    if ($result -eq 6)
    {
        # when in an Citrix Session log some information about Citrix
        $sessioninfo = (Get-ItemProperty -Path HKLM:SOFTWARE\Citrix\ICA\Session -ErrorAction SilentlyContinue )
        $outputpdf = (Get-FileName -defaultFileName "$(get-date ((ls $outputfile).CreationTime) -Format "yyyy-MM-dd HHmmss") Workplace and VDI test-automation with PowerShell.pdf" )        
        $message = "VDI Desktop Image testscript run on $(get-date -Format "yyyy-MM-dd HH:mm"). `n`nOutput PDF: $(split-path $outputpdf -leaf)`nComputername: $($env:computername)`n"
		# when in an Citrix Session log some information about Citrix
		if ($sessioninfo -ne $null)
		{
			$message += "Clientaddress: $($sessioninfo.clientaddress) `nClientname: $($sessioninfo.clientname) `nClientversion: $($sessioninfo.clientversion)"
		}
		$message += "`n`n"
        convert-su-to-pdf -inputsu $outputfile -outputpdf $outputpdf -title "Desktop Image functional Script" -message $message
    }

# ask for logoff
    $result = Show-PopUp -Message "Do you want to logoff?" -Title "Logoff" -ButtonSet YNC -IconType Question

    if ($result -eq 6)
    {
        start-process logoff
    }

Download
Download the script, module and test-files here: sources.zip

Mount VMFS on Centos 7 or another Linux distro

Why VMFS on CentOS 7?
I was running a VMware ESXi server since 4 years. In VMware ESXi the options for local storage are limited, especially when you like redundant storage. The option is to buy a Raid adapter. I decided to move to CentOS with ZFS storage.
Moving the VM’s to a Linux disk can be done by moving all the files to a NFS share, but an another option is to mount the VMFS filesystem. This is not natively available in Linux, but with fuse and vmfs-tools you can make it available. In this article I will describe how I did the installation and movement of the VM’s.

My baseline tools
I did a plain CentOS 7 installation, after the installation finished succesfully I connected my harddrive with the VMFS file system to the Linux box.
The VMFS driver is not natively available, so you need to compile it yourself. Glandium programmed a userspace VMFS driver for linux, more about this can be found here: http://glandium.org/blog/?p=2539

Howto compile VMFS for CentOS 7?
Enable the EPEL repository for additional packages

wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-1.noarch.rpm
sudo rpm -Uvh epel-release-7*.rpm

Install the following packages for userspace filesystems

yum install libuuid-devel
yum install fuse fuse-devel 

Download the VMFS sourcecode

wget http://glandium.org/projects/vmfs-tools/vmfs-tools-0.2.5.tar.gz
tar -xvzf vmfs-tools*

Unpack and compile

cd vmfs-tools-0.2.5
./configure
make
make install

The programs will be installed in /usr/local/sbin

To mount a VMFS file system:

./vmfs-fuse /dev/sdb1 /mount

*When you recognize that vmfs-fuse command is not available on you’re system make sure that fuse, fuse-devel and libuuid-devel is installed before compiling.

Files can now be accessed from /mount

Thin-provisioned VMDK’s are copied as Pre-Allocated VMDK’s
I copied all the vm’s to my local Linux file system, but had some issues with the thin-provisoned vmdk’s. The become pre-allocated vmdk’s which uses a lot of space. With the following command you can convert them back to growing disks:

vmware-vdiskmanager -r sourceDisk.vmdk -t 0 targetDisk.vmdk

Conclusion
Installing the VMFS drives on Linux / CentOS can be usefull in case of migrations, but also in case of an emergency. It is realative easy to browse you’re VMFS filesystem from a non-VMware host.

Force update of a Computer Account in Active Directory

When? When using systems with a snapshot and the Active Directory requires updating the computer account password.

Why? Active Directory is normally configured to have the computeraccounts updated every 30 days for security reasons. This policy can be changed, but sometimes you don’t have the control over this policy.

How?

nltest /sc_change_pwd:easternnet.com

Tooling? In Windows 7 and Windows 8 the command “nltest” is part of the operating system. On older versions it is located in the Resource Kit.