May 21 2021 03:57 PM
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)
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.
6.) Fill in a logical name, set Runbook type “PowerShell”, followed by pressing the Create button below.
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
Confirm by pressing Yes.
9.) You will see the overview section of the Runbook, click on Schedules, click the button “+ Add a schedule”.
10.) Click on Link a schedule to your runbook.
11.) Click +Add a schedule, complete the form and press Create below.
12.) Click on Configure Parameters
13.) Fill in the designated resource group name and press OK twice.
14.) Go back to the overview of the runbook and press the Start button to validate.
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
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.
4.) Go the Actions tab, press New button; Take over the properties as shown in the screenshot and press OK.
5.) Go to the Settings tab, take over the properties as shown below and press OK.
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.
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.
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.
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
May 23 2021 04:56 AM - edited May 23 2021 04:58 AM
A Great article, very detailed.
For real, this is a gamechanger!
Keep going like this! 🙂
May 23 2021 05:03 AM
Jul 27 2021 04:11 PM
Jan 27 2022 10:03 AM
@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