Events, SharePoint Conference

Using a Flic Smart Button as a PowerPoint Remote

When speaking at conferences, you usually spend a lot of time time asking the organizer questions like “what inputs will you have, will there be a digital switcher, etc.”.  This usually leads to carrying an array of adapters to be sure you’re prepared for those circumstances where there’s just VGA at the podium when you need micro/mini HDMI, DVI, USB to RJ45, and more…

One of the most common requirements is something to control your presentation.  If you’re like me, you often find yourself either running out of USB ports or having large inputs making nearby ports inaccessible or don’t want to carry a USB hub around with you.  At the same time, many conferences don’t provide remotes for controlling your presentations (they have a propensity to “walk away” or someone leaves the session before yours with the input – leaving a hapless remote behind).

A couple of weeks ago, I picked up a Flic Smart Button that we had provided our MVP’s as swag at the 2019 MVP Summit and stashed it away in my desk drawer, not quite sure what I’d use it for.

If you’re not familiar with Flic, check out https://flic.io.

With the SharePoint Conference just a week away, I started to think about ways I could use the Flic Smart Button, but everything I found limited its’ use to a connection with a mobile device…  then I discovered the Flic App for Mac – perfect I thought – I’ll use the keyboard shortcuts action I saw in the mobile app on my MacBook as a way to remote control PowerPoint.

After installing the Flic App for Mac, I couldn’t find the keyboard actions so started to do a little research to find a plugin that would work…  I couldn’t find anything that would do what I wanted it to do so decided to build my own…

It took a little bit of time to get it right, at the same time, simple – but I was able to build a user defined plugin to use with my Flic Smart Button and Mac to act as a PowerPoint remote.

Screen Shot 2019-05-15 at 11.17.09 AM

If you’re interested in using the plugin, I’ve made it available here on Github.

Standard
OneDrive for Business, SharePoint

FileChecker Migration Assessment Sample Available for SharePoint Online and OneDrive for Business

Know before you go…

If you’re considering migrating file shares or other file-centric containers to OneDrive for Business or SharePoint Online it’s useful to understand if the source files are supported for storage or synchronization.  This code sample at https://github.com/wbaer/FileChecker helps to identify illegal characters, length restrictions, and more in one or more file names that can potentially prevent proper synchronization with Microsoft SharePoint and OneDrive for Business or fail to render through the Microsoft SharePoint or OneDrive for Business Web UX.

Extend FileChecker.exe at https://github.com/wbaer/FileChecker or download a working sample and read the documentation at http://wbaer.io/2oaQ9HP.

UX

filechecker_core

Command Line

filechecker_cl

Output

filechecker_results

Standard
Administration, Events

Quick Starting Demos and Windows PowerShell

Preparing virtual machines for demonstrations can be a tedious process, compounding this is when virtual machines need to be started or shut down in a specific order.  For example, starting database servers prior to starting web servers, or starting the preferred active node before the passive node.  Starting those machines; however, is only a portion of the process, in most cases you will want them to be “available” before starting a subsequent machine.  For example, having an iSCSI Target available before the consuming iSCSI initiators are available.  Windows PowerShell, is perfect to support this scenario – it’s something I use almost everyday and have shared an example (below) of how you can accomplish all of these tasks…

So what does it do?

Provides parameters to Start/Shut Down one or more virtual machines.

Checks for process elevation, escapes if the script is not run elevated.

Starts the Hyper-V Virtual Machine Management Service if not running.

Iterates through an array of virtual machines stored in a .txt file.

Starts each virtual machine in the .txt file and waits for the heartbeat status to report ‘OK’ before starting the next virtual machine in the list.  Virtual machines are started in the order they appear in the source file, waiting ensures a clean start up – particularly where a defined start order with dependencies exists.

Shuts down virtual machines in the reverse order they were started by reading the source file bottom to top.  Waits for the virtual machine heartbeat status to report ‘’ before processing the next virtual machine.

Displays a progress bar to report on the status of the operation.

Script

[CmdletBinding(ConfirmImpact="Low")]

Param(
     [Parameter(Mandatory=$True,Position=0,ValueFromPipeline=$False,HelpMessage="Operation to perform on one or more virtual machines.")][ValidateSet("Start","Stop")]
     [String]$operation,
     [Parameter(Mandatory=$True,Position=1,ValueFromPipeline=$False,HelpMessage="Collection of virtual machines on which operation is to be performed.")][ValidateNotNullorEmpty()]
     [String]$source
)

$ErrorActionPreference = "Stop"

Process
{
     $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
     $principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
     $role = [System.Security.Principal.WindowsBuiltInRole]::Administrator
     $elevated = $principal.IsInRole($role)

     If ($operation -eq "Start")
     {
         $service = Get-Service -Name vmms

         If ($service.Status -ne "Running")
         {
             Try
             {
                 If ($elevated)
                 {
                     Start-Service $service

                     Write-Host "Starting the Hyper-V Virtual Machine Management Service."

                     Start-Sleep -s 10

                     Clear-Host
                 }
                 Else
                 {
                     Write-Host "Requires elevation."
                     break
                 }
             }
             Catch
             {
                 [System.Exception]
                 Write-Host "Could not start Virtual Machine Management Service."
                 break
             }
         }

         $exists = Test-Path "$(Get-Location)$source.txt" 

         If ($exists -eq $True)
         {
             Try
             {
                 $list = Get-Content "$(Get-Location)$source.txt"
             }
             Catch
             {
                 [System.Exception]
                 break
             }
         }
         Else
         {
             Write-Host "The file could not be found: $source.  The document name or path is not valid."
             break
         }

         For ( $count = 0; $count -lt $list.Count; $count++
         { 
             $guest = $list[$count]

             $progress = 100 / $list.Count * ($count + 1)

             Write-Progress -Activity "Starting virtual machine…" -CurrentOperation "Starting…" -Status $guest -PercentComplete $progress

             Try
             {
                 If ($elevated)
                 {
                     Start-VM -Name $guest
                 }
                 Else
                 {
                     Write-Host "Requires elevation."
                     break
                 }
             }
             Catch
             {
                 Write-Host "Could not start virtual machine(s)."
                 break
             }

             Write-Progress -Activity "Starting virtual machine…" -CurrentOperation "Waiting…" -Status $guest -PercentComplete $progress

             do {Start-Sleep -milliseconds 100
             until ((Get-VMIntegrationService $guest | ?{$_.name -eq "Heartbeat"}).PrimaryStatusDescription -eq "OK")
         }
     }

     ElseIf ($operation -eq "Stop")
     {
         $exists = Test-Path "$(Get-Location)$source.txt" 

         If ($exists -eq $True)
         {
             Try
             {
                 $list = Get-Content "$(Get-Location)$source.txt"
             }
             Catch
             {
                 [System.Exception]
                 break
             }
         }
         Else
         {
             Write-Host "The file could not be found: $source.  The document name or path is not valid."
             break
         }

         For ($count = $list.Length1; $count -ge 0 ; $count)
         { 
             $guest = $list[$count]

             $progress = 100 / $list.Count * ($count + 1)

             Write-Progress -Activity "Stopping virtual machine…" -CurrentOperation "Stopping…" -Status $guest -PercentComplete $progress
    
             Try
             {
                 Stop-VM -Name $guest
             }
             Catch
             {
                 Write-Host "Could not stop virtual machine."
                 break
             }

             Write-Progress -Activity "Stopping virtual machine…" -CurrentOperation "Waiting…" -Status $guest -PercentComplete $progress

             do {Start-Sleep -milliseconds 100
             until ((Get-VMIntegrationService $guest | ?{$_.name -eq "Heartbeat"}).PrimaryStatusDescription -ne "OK")
         }

         Start-Sleep -s 10

         If ($elevated)
         {
             Try
             {
                 Stop-Service vmms
                 Write-Host "Stopping the Hyper-V Virtual Machine Management Service…"
             }
             Catch
             {
                 [System.Exception]
                 Write-Host "Could not stop the Hyper-V Virtual Machine Management Service."
                 break
             }
         }
         Else
         {
             Write-Host "Requires elevation."
             break
         }

         Clear-Host
     }
}

Usage

Using the scripts requires 1) saving the attached script as <name>.ps1 2) creating source .txt file with virtual machines listed in the preferred start up order.  For example,

Machine1

Machine2

Machine3

3) Saving the script and source .txt file in the same location.

4) Running the script as <name>.ps1 –Operation Start –Source <name>

Standard
Administration, Hybrid, SharePoint

Yammer Redirection in SharePoint Server 2013 Service Pack 1

In previous articles I discussed redirecting OneDrive for Business to Office 365 in SharePoint Server 2013 Service Pack 1.  In addition to new functionality that allows IT administrators to redirect OneDrive for Business and Sites pages to Office 365, Service Pack 1 also allows IT administrators to activate Yammer as the default social experience for SharePoint Server 2013.

When activated, the Newsfeed link in the navigation bar is replaced with a Yammer link that directs users to the organizations’ Yammer network. 

Yammer Configuration page in Central Admin

While a simplified Activate/Deactivate configuration is provided in Service Pack 1, proper identity management planning is required to deliver a consistent and integrated user experience.

For organizations with an established Active Directory and identity management infrastructure and practices

Directory Synchronization with Office 365 is a distinct configuration that provides an integrated identity solution for applications within the Office 365 suite with the exception of Yammer.  Integrated identity with Yammer requires a separate implementation of a directory synchronization solution provided by Yammer, Yammer Directory Sync (DSync).

Similar to Directory Synchronization, Yammer Directory Sync (DSync) is a Windows application that automates user provisioning in your Yammer network by querying your Active Directory (AD) host(s).

 

Sites Page

The Sites page introduced in SharePoint Server 2013 is designed to provide users a unified location to create new sites and view sites they are following.

Managing Sites Page Redirection

Service Pack 1 also includes within OneDrive for Business redirection the option to redirect users Sites page to Office 365.

Example 1 C#

        static void Main(string[] args)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPWebApplication webApp = SPWebApplication.Lookup(new Uri("http://sharepoint.spc.com.co"));
 
                foreach (SPSite site in webApp.Sites)
                {
                    foreach (SPWeb web in site.AllWebs)
                    {
                        if (web.Features != null)
                        {
                            web.Features.Remove(new Guid("{043C4BDD-9745-441a-A9A7-0BCD9B910319}"));
                        }
                    }
                }
            });
        }

Example 1 Windows PowerShell

This example can be used to deactivate a specific Feature across all sites within the specified Web application.

$webApp = Get-SPWebApplication -Identity http://sharepoint.contoso.com

$webApp | Get-SPSite -limit all | ForEach-Object {Disable-SPFeature -Identity “FeatureName” -Url $_.Url}

The Uninstall-SPFeature cmdlet removes the specified feature definition from the collection of feature definitions in the farm.

Uninstall-SPFeature –Identity

Other Social Capability Considerations

In addition to addressing scenarios such as Following Content you should also consider the management of other SharePoint Server 2013 social capabilities such as:

  • Removing the SharePoint Server social web parts from My Sites and Team Sites.
  • Hiding user interface controls that provide social functionality.

For additional information on replacing the social features in SharePoint Server 2013 with equivalent Yammer features in a SharePoint Server 2013 on-premises deployment see also http://technet.microsoft.com/en-us/library/dn270535(v=office.15).aspx.

Resources

Add Yammer to the navigation bar for SharePoint 2013 [http://technet.microsoft.com/en-us/library/dn627521(v=office.15).aspx]

Standard
OneDrive for Business, SharePoint

Code Samples, Simple Translation using CSOM, REST, and Machine Translation Services

Machine Translation Services is a powerful Shared Service Application in SharePoint Server 2013 that provides automation synchronous and asynchronous translation of documents, folders, and sites.  The code samples in this post provide simple examples of synchronous document translation using the Client Side Object Model in managed (C#) applications and Windows PowerShell in addition to accessing Machine Translation Services via REST.

CSOM

The Client Side Object Model (CSOM) provides a rich alternative to earlier Web Services in providing an object-oriented system for interoperating with SharePoint data from a remote (client) machine.

The foundation of CSOM interop is the client context object which represents the current request context.  In the provided samples, it is represented in the C# example as:

new ClientContext(site)

or in the Windows PowerShell sample as:

New-Object Microsoft.SharePoint.Client.ClientContext($url) where the $url in this example is a parameter passed to the script.

Through this context you can obtain access to client objects to include site collections and their subordinates as represented in the above examples.

For example, in the sample scripts (download link below), the top-level site collection represents the new client context which is passed to the SyncTranslator class of the TranslationServices namespace as the Context property.

$context = New-Object Microsoft.SharePoint.Client.ClientContext($url)
$credentials = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($username, $password)
$context.Credentials = $credentials

$job = New-Object Microsoft.Office.Client.TranslationServices.SyncTranslator($context, $language)

REST

REST or Representational State Transfer in the context of SharePoint 2013 development opens it to standard Web languages and technologies.  Technologies such as CSOM (as shown above) have been available to SharePoint over several releases; however, such APIs are limited to .NET applications and languages.  REST; however, enables accessing SharePoint capabilities and entities with standard Web languages such as JavaScript and preprocessor hypertext (PHP) in addition to any technology stack that supports REST.   One of the predominant benefits of REST is that it allows for limiting the footprint of Web applications, as such, reduces the barrier to entry to accessing cloud services and deploying applications whose target is such.

In SharePoint the REST service is implemented in client.svc contained within _vti_bin; however, through substitution _api is used and establishes the base Url for each endpoint.

The service Url of specific endpoints is appended to the base Url, in the sample code:

$request = [System.Net.WebRequest]::Create($url +"/_api/TranslationJob.EnumerateSupportedLanguages")

These samples are intended to illustrate how Windows PowerShell can be used to access cloud services through CSOM and REST.

Download the samples here:

CSOM (Windows PowerShell and C# Samples)

REST (Windows PowerShell Samples)

Standard
Administration, OneDrive for Business

File and Folder Considerations with OneDrive for Business [UPDATED 12/10/2014]

Updated 8/12/2014 – Removed & as an illegal character.  & character is now supported with OneDrive for Business sync client and Web UX.

Updated 8/22/2014 – Updated to include prohibited types per http://office.microsoft.com/en-us/office365-sharepoint-online-small-business-help/types-of-files-that-cannot-be-added-to-a-list-or-library-HA101907868.aspx.

Updated 8/23/2014 – Updated to include optional UI-based scanning (FileCheckerUI.exe).

Updated 8/31/2014 – Updated FileChecker.exe (integrated desktop and command line application).

Updated 12/10/2014  Updated to remove prohibited characters {, }, [, ], ~, and ..  Updated FileChecker.exe

When considering a migration to OneDrive for Business you should be aware of the specific File and Folder considerations and restrictions.  While some considerations exist that are explicit to OneDrive for Business and SharePoint; others are derivatives of the underlying client and/or server file system.  For example, on Microsoft Windows the following characters cannot be used in paths or files:

Files

<
>
|
♠♫
§
:
*
?
/

Paths

<
>
|
♠♫
§
:
*
?
/

NOTE

The above represents an array returned by the Path.GetInvalidFileNameChars and Path.GetInvalidPathChars methods respectively.  These methods; however, do not return a complete set of characters invalid in file and path names as they can differ depending on the underlying file system.  On Windows-based desktop platforms, invalid path characters might include ASCII/Unicode characters 1 through 31, as well as quote (“), less than (<), greater than (>), pipe (|), backspace (b), null () and tab (t) in addition to those in the example above.

File and Folders preceded with (_).

Files and Folders whose name is preceded with the (_) are considered ‘hidden’.  This limitation is derived from the Win32FileAttributes in the WebDAV protocol.  In scenarios where a File and/or Folder are preceded with (_), such as _Documents or _document.docx, in both cases the File and/or Folder will be visible in the OneDrive for Business Sync Client as well as the Web UI; however, when using Explorer View in the Web UI, Files and Folders preceded with (_) will not be visible.  Explorer View in OneDrive for Business uses the WebDAV protocol.  WebDAV refers to Web Distributed Authoring and Versioning, an extension of the HTTP protocol that is used to enable management of documents stored on WWW servers.  The scenario herein is based on limitations implied in FrontPage 2000 (see also http://support.microsoft.com/kb/219193).

In OneDrive for Business Explorer View can be instantiated by selected the Open with Explorer option in the Ribbon.

Picture1

When you use Open with Explorer, it opens Windows Explorer on your computer, but it displays the folder structure on the server computer that underlies the site.  You can manipulate the files in the folder, such as copying, renaming, deleting, etc.

Customers who have deployed OneDrive for Business on-premises can nullify the Win32FileAttributes using Windows PowerShell or C# as illustrated in the samples below:

Windows PowerShell

For IT Professionals you can use Windows PowerShell to remove the vti_winfileattribs folder metadata as shown in the example below.

$Folder = (Get-SPWeb http://contoso.sharepoint.com).Folders[“<DocLib_Name>”].SubFolders[“<_Folder_Name>”]

$Folder.Properties[“vti_winfileattribs”]=””

C#

Developers can use the SPFolder.Properties property to enumerate the hash table that contains the metadata for folders and implement the DeleteProperty method to deletes the element with the vti_winfileattribs key from the metadata for the folder.  See also http://msdn.microsoft.com/en-us/library/office/microsoft.sharepoint.spfolder.deleteproperty(v=office.15).aspx for an explanation and examples of using the SPFolder.DeleteProperty method.

WebDAV Resources

WebDAV API Functions [http://msdn.microsoft.com/en-us/library/windows/desktop/dd408161(v=vs.85).aspx]

[MS-WDV]: Web Distributed Authoring and Versioning (WebDAV) Protocol: Client Extensions [http://msdn.microsoft.com/en-us/library/cc250046.aspx]

[MS-WDVSE]: Web Distributed Authoring and Versioning (WebDAV) Protocol: Server Extensions [http://msdn.microsoft.com/en-us/library/cc250200.aspx]

Files and Folders preceded or followed with (.).

A number of restrictions with File and Folder naming convention are derivative of the the File System, developers who use the Windows APIs for file and device I/O in many cases, understand the various rules, conventions, and limitations of names for files and directories.

Files and Folders whose name is preceded or followed with the (.) character cannot be stored or synchronized with the OneDrive for Business.  All file systems follow the same general naming conventions for an individual file: a base file name and an optional extension, separated by a period.   The assumption in this case is (.) separates the base file name from the extension in the name of a directory or file.

Restricted Characters in File and Folder Names

Beyond those limitations documented above, users can create Files and Folders using any character including Unicode characters and characters in the extended character set (128–255), except for the following reserved characters:

  • < (less than)
  • > (greater than)
  • : (colon)
  • ” (double quote)
  • / (forward slash)
  • (backslash)
  • | (vertical bar or pipe)
  • ? (question mark)
  • * (asterisk)

These limitations are applicable to Microsoft Windows.

In addition you cannot use the:

  • ~ (Tilde)
  • # (Number Sign)
  • % (Percent)
  • [ ] (Braces)
  • { } (Angle Brackets)
  • ? (Question Mark)
  • You cannot use the period character consecutively in the middle of a folder name.  In the Windows File System, two consecutive periods (..) are used as a directory component in a path to represent the parent of the current directory, for example “..temp.txt”.

These limitations are applicable to OneDrive for Business and SharePoint 2013.  For additional information see also http://support.microsoft.com/kb/905231.

Other Considerations

SharePoint 2013 and OneDrive for Business do not provide support for POSIX semantics, that is a Folder “Foo” and “foo” are considered the same, as opposed to differing paths.

Validating File and Folder Names

Developers can validate File and Folder names using a number of methods.  The sample code at http://tinyurl.com/opcjfor uses Regular Expressions to deterministically identify illegal characters in a File name.

Syntax

FileChecker.exe -d C:Temp

Screenshots

Source Directory

image

Filechecker.exe

image

Output

image

Standard
Administration, Events

Quick Starting Demos with Windows PowerShell

Preparing virtual machines for demonstrations can be a tedious process, compounding this is when virtual machines need to be started or shut down in a specific order.  For example, starting database servers prior to starting web servers, or starting the preferred active node before the passive node.  Starting those machines; however, is only a portion of the process, in most cases you will want them to be “available” before starting a subsequent machine.  For example, having an iSCSI Target available before the consuming iSCSI initiators are available.  Windows PowerShell, is perfect to support this scenario – it’s something I use almost everyday and have shared an example (below) of how you can accomplish all of these tasks…

So what does it do?

Provides parameters to Start/Shut Down one or more virtual machines.

Checks for process elevation, escapes if the script is not run elevated.

Starts the Hyper-V Virtual Machine Management Service if not running.

Iterates through an array of virtual machines stored in a .txt file.

Starts each virtual machine in the .txt file and waits for the heartbeat status to report ‘OK’ before starting the next virtual machine in the list.  Virtual machines are started in the order they appear in the source file, waiting ensures a clean start up – particularly where a defined start order with dependencies exists.

Shuts down virtual machines in the reverse order they were started by reading the source file bottom to top.  Waits for the virtual machine heartbeat status to report ‘’ before processing the next virtual machine.

Displays a progress bar to report on the status of the operation.

Script

[CmdletBinding(ConfirmImpact="Low")]

Param(
     [Parameter(Mandatory=$True,Position=0,ValueFromPipeline=$False,HelpMessage="Operation to perform on one or more virtual machines.")][ValidateSet("Start","Stop")]
     [String]$operation,
     [Parameter(Mandatory=$True,Position=1,ValueFromPipeline=$False,HelpMessage="Collection of virtual machines on which operation is to be performed.")][ValidateNotNullorEmpty()]
     [String]$source
)

$ErrorActionPreference = "Stop"

Process
{
     $identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
     $principal = New-Object System.Security.Principal.WindowsPrincipal($identity)
     $role = [System.Security.Principal.WindowsBuiltInRole]::Administrator
     $elevated = $principal.IsInRole($role)

     If ($operation -eq "Start")
     {
         $service = Get-Service -Name vmms

         If ($service.Status -ne "Running")
         {
             Try
             {
                 If ($elevated)
                 {
                     Start-Service $service

                     Write-Host "Starting the Hyper-V Virtual Machine Management Service."

                     Start-Sleep -s 10

                     Clear-Host
                 }
                 Else
                 {
                     Write-Host "Requires elevation."
                     break
                 }
             }
             Catch
             {
                 [System.Exception]
                 Write-Host "Could not start Virtual Machine Management Service."
                 break
             }
         }

         $exists = Test-Path "$(Get-Location)$source.txt" 

         If ($exists -eq $True)
         {
             Try
             {
                 $list = Get-Content "$(Get-Location)$source.txt"
             }
             Catch
             {
                 [System.Exception]
                 break
             }
         }
         Else
         {
             Write-Host "The file could not be found: $source.  The document name or path is not valid."
             break
         }

         For ( $count = 0; $count -lt $list.Count; $count++
         { 
             $guest = $list[$count]

             $progress = 100 / $list.Count * ($count + 1)

             Write-Progress -Activity "Starting virtual machine…" -CurrentOperation "Starting…" -Status $guest -PercentComplete $progress

             Try
             {
                 If ($elevated)
                 {
                     Start-VM -Name $guest
                 }
                 Else
                 {
                     Write-Host "Requires elevation."
                     break
                 }
             }
             Catch
             {
                 Write-Host "Could not start virtual machine(s)."
                 break
             }

             Write-Progress -Activity "Starting virtual machine…" -CurrentOperation "Waiting…" -Status $guest -PercentComplete $progress

             do {Start-Sleep -milliseconds 100
             until ((Get-VMIntegrationService $guest | ?{$_.name -eq "Heartbeat"}).PrimaryStatusDescription -eq "OK")
         }
     }

     ElseIf ($operation -eq "Stop")
     {
         $exists = Test-Path "$(Get-Location)$source.txt" 

         If ($exists -eq $True)
         {
             Try
             {
                 $list = Get-Content "$(Get-Location)$source.txt"
             }
             Catch
             {
                 [System.Exception]
                 break
             }
         }
         Else
         {
             Write-Host "The file could not be found: $source.  The document name or path is not valid."
             break
         }

         For ($count = $list.Length1; $count -ge 0 ; $count)
         { 
             $guest = $list[$count]

             $progress = 100 / $list.Count * ($count + 1)

             Write-Progress -Activity "Stopping virtual machine…" -CurrentOperation "Stopping…" -Status $guest -PercentComplete $progress
    
             Try
             {
                 Stop-VM -Name $guest
             }
             Catch
             {
                 Write-Host "Could not stop virtual machine."
                 break
             }

             Write-Progress -Activity "Stopping virtual machine…" -CurrentOperation "Waiting…" -Status $guest -PercentComplete $progress

             do {Start-Sleep -milliseconds 100
             until ((Get-VMIntegrationService $guest | ?{$_.name -eq "Heartbeat"}).PrimaryStatusDescription -ne "OK")
         }

         Start-Sleep -s 10

         If ($elevated)
         {
             Try
             {
                 Stop-Service vmms
                 Write-Host "Stopping the Hyper-V Virtual Machine Management Service…"
             }
             Catch
             {
                 [System.Exception]
                 Write-Host "Could not stop the Hyper-V Virtual Machine Management Service."
                 break
             }
         }
         Else
         {
             Write-Host "Requires elevation."
             break
         }

         Clear-Host
     }
}

Usage

Using the scripts requires 1) saving the attached script as <name>.ps1 2) creating source .txt file with virtual machines listed in the preferred start up order.  For example,

Machine1

Machine2

Machine3

3) Saving the script and source .txt file in the same location.

4) Running the script as <name>.ps1 –Operation Start –Source <name>

Standard