MUST READ - WVD Personal VM - Proper Shutdown process and deallocate VM to save costs!

Iron Contributor

First things first!
Recently Microsoft introduced a really cool feature which is called “Start VM on Connect” and was a long awaiting feature. This feature is groundbreaking because the WVD management plane is able to start the VM on behalf of the user. End-users no longer have to call their IT admins to start the VM. Once the end-user hits the button to start their desktop, the VM will be started automatically and will take approximately 5 minutes. This mystery has been resolved, now the next piece of the puzzle to shutdown and deallocate a WVD personal VM and save consumption costs.

Challenge!
Most of the end-users will shutdown the VM but the result is a VM in “stopped” state. This means the VM is still allocated to a host server in the Azure datacenter and the subscription owner will still be charged. Other possible scenario’s might be that end-users disconnect from the session instead of shutdown the VM or they totally forgot their session which becomes inactive/idle.

An easy, almost effortless two-step solution 😊
In order to resolve our challenge it’s good to understand the difference between “idle” and “disconnected” terminology and it’s highly recommended to determine idle session time and disconnected session time within your organization or with your customer. Idle time means that the session is inactive, the end-user is not doing anything in the desktop session. Disconnected time means that the user closes the desktop session without properly signing out or shutting down the VM. My advise here, during onboarding provide proper instructions for your end-users on how to end a desktop session.

Important note/Prerequisite! This will only work when Logon/Logoff events are audited (success/failure). Almost all enterprises have this enabled in a security or default domain policy but please double check!

Configure Azure Automation Account – Finding VM’s with “stopped” state and execute Stop-AzVm to bring VM in “stopped de-allocated” state.
An Azure Automation Account is very powerful and can basically do anything within your Azure environment. For this purpose the Automation Account requires to be created along with a “Run as Account”. This account will act as a trusted service principal and is allowed to execute runbooks (regardless what a runbook contains).

1.) Open up the Azure portal with your admin privileges, in the search bar find Automation Accounts and hit the icon.
2.) Press the create button, complete the fields (provide a clear name, associate the corresponding resource group, set location, and toggle the switch on YES to Create Azure Run As Account! Press the Create button. The creation process will take a few minutes. Time to grab a drink or whatever you might want to do 😊
3.) Once completed, open the Automation Accounts, scroll a little bit down, click on Modules, click on Browse Gallery. Find the following modules
- Az.Accounts (select it, press Import, wait a few minutes)
- Az.Compute (select it, press Import, wait a few minutes)


raymondesray_0-1621637304389.png

 



4.) In the Modules overview hit “Refresh” to validate whether both modules are present and have “Available” status.

5.) Within the “Process Automation” section click on Runbooks, followed by pressing the “+ Create a runbook” button.

raymondesray_1-1621637304407.png

 

6.) Fill in a logical name, set Runbook type “PowerShell”, followed by pressing the Create button below.

raymondesray_2-1621637304413.png

 

7.) Now you need to enter a script, completely copy following content starting at param.

Content

param (

    [parameter(Mandatory = $true)][String]$ResourceGroupName

)

 

$ConnectionName = "AzureRunAsConnection"

try

{

    $Connection = Get-AutomationConnection -Name $ConnectionName

 

    # Get the connection "AzureRunAsConnection "

    $ServicePrincipalConnection = Get-AutomationConnection -Name $ConnectionName

 

    "Logging in to Azure..."

    Connect-AzAccount `

        -ServicePrincipal `

        -Tenant $ServicePrincipalConnection.TenantId `

        -ApplicationId $ServicePrincipalConnection.ApplicationId `

        -CertificateThumbprint $ServicePrincipalConnection.CertificateThumbprint

 

    Set-AzContext -Subscription $Connection.SubscriptionID -Tenant $Connection.TenantId

}

catch {

    if (!$ServicePrincipalConnection)

    {

        $ErrorMessage = "Connection $ConnectionName not found."

        throw $ErrorMessage

    } else {

        Write-Error -Message $_.Exception

        throw $_.Exception

    }

}

 

#Get VMs Status

       [array]$StoppedAzVMs = Get-AzVM -Status

 

       if($StoppedAzVMs) {

#Loop through each VM in this Resource Group

           ForEach($StoppedAzVM in $StoppedAzVMs) {

  

#If PowerState is VM stopped

                   if($StoppedAzVM.PowerState -eq "VM stopped") {

       Stop-AzVM -Name $StoppedAzVM.Name -ResourceGroupName $StoppedAzVM.ResourceGroupName -Force

              }

           }

       }


8.) Hit the “Publish” button

raymondesray_3-1621637304423.png

 


Confirm by pressing Yes.

raymondesray_4-1621637304426.png

 

9.) You will see the overview section of the Runbook, click on Schedules, click the button “+ Add a schedule”.

raymondesray_5-1621637304437.png

 



10.) Click on Link a schedule to your runbook.

raymondesray_6-1621637304442.png

 

11.) Click +Add a schedule, complete the form and press Create below.

raymondesray_7-1621637304447.png

 

12.) Click on Configure Parameters

raymondesray_8-1621637304451.png

 

13.) Fill in the designated resource group name and press OK twice.

raymondesray_9-1621637304456.png

 

14.) Go back to the overview of the runbook and press the Start button to validate.

raymondesray_10-1621637304462.png

 

15.) Since this is a manual start and validation action, enter the resource group name and press OK. Wait for the job to complete.

16.) In the overview section you will see the completed Job, click on it , press the output tab and validate it ran successfully. It depends whether you already have stopped state VM’s.

Next part is to configure session time limits in conjunction with a scheduled task to trigger a shutdown action.


Configure group policy to end idle and disconnected sessions, which means the user will be logged off;
Start your group policy management console, right mouse click on the OU that contains the WVD personal VM computer accounts, create a new GPO and ensure it is linked. Enable and configure the following settings;

Part 1 of GPO:
Computer Configuration\Administrative Templates\Windows Components\Remote Desktop Services\Remote Desktop Session Host\Session Time Limits
1.) End session when time limits are reached > enable
2.) Set time limit for disconnected sessions > enable (for demo purpose 5 minutes but this is what you need to discuss with your organization/customer!)
3.) Set time limit for active but idle Remote Desktop Services sessions > enable (for demo purpose 5 minutes but this is what you need to discuss with your organization/customer!)

Part 2 of GPO:
Computer Configuration\Preferences\Control Panel Settings\Scheduled Tasks
1.) Right mouse click on Scheduled Tasks > New > Scheduled Task (At least Windows 7)

2.) Leave default Action setting on Update, provide a name and take over rest of the properties

raymondesray_11-1621637304475.png

 



3.) Go to Triggers tab, press New button; The trigger will ignite the Action which will be configured in next step. Event ID 4647 corresponds with a Logoff user event. Press OK when properties are completed as shown in the screenshot.

raymondesray_12-1621637304489.png

 

4.) Go the Actions tab, press New button; Take over the properties as shown in the screenshot and press OK.

raymondesray_13-1621637304495.png

 

5.) Go to the Settings tab, take over the properties as shown below and press OK.

raymondesray_14-1621637304510.png

 



6.) When the VM receives the group policy, the WVD personal VM will shutdown once the user logs off (ending idle/disconnected session 😊).

7.) Signed in with my user, GPO has been applied. As a user you won’t see the scheduled task.

raymondesray_15-1621637304569.png


8.) After 5 minutes the user is logged off automatically due to Remote Desktop Services time limit policy.

9.) Once user is signed out, the shutdown action will be triggered.

10.) Confirmed, that VM entered a stopped state.

raymondesray_16-1621637304585.png

 

11.) With the next hour the earlier created Automation Account runbook will find the stopped state VM and will force a Stop-AzVm that puts the VM in stopped de-allocated state.

raymondesray_17-1621637694379.png


Done! Of course you can apply this runbook to other schedules and resource groups to ensure you won’t be charged unnecessary.

In case you have any questions, please let me know.

Kind regards,

Raymond van Garderen
Modern Workplace / WVD / M365
Consultant @SCCT The Netherlands

 

4 Replies

A Great article, very detailed.

For real, this is a gamechanger!

Keep going like this! 🙂

Hi! Thanks for your positive feedback, highly appreciated 😉
Have a great day!

@raymondesray  Hey there, just wondered if this could be targeted at a host pool in AVD.  Also, interested to know where you are setting the group policy if its purely azure AD, no domain controllers in cloud or on-prem domain controllers. Could we do this via MDM/MEM/Intune instead.

 

Cheers,

 

Glyn