(Exactly) 200 Crappy Words on Cross-Forest AD-Joined Azure File Shares

In a client deployment recently we leveraged Azure Files, joined to a Domain in the client’s primary AD Forest. AVD Sessions are established from other Domains and Forests and all was well with the world – until we needed to deploy security baselines for compliance. After introducing the locked-down configuration, we were no longer able to authenticate AzFiles across AD Forests, and User GPO processing was failing. 

Part of the CIS L1 benchmark is to disable any authentication encryption except AES128/256 and “future” methods… – When handling authentication cross-Forest, this authentication method is not supported, even if the storage account has been properly configured for AES256 encryption. It appears this authentication will work only using the RC4 encryption method. – This also affects the ability to process User Policy if this is linked to the other Forest using the Loopback policy option to enable cross-Forest processing User Policies. 

The real fun was in the troubleshooting and investigation; Once the policy has been applied (GPO > Security > Local Policies), it seems to “tattoo” the machine, so that even when the policy is removed, the session host won’t climb down from the elevated security stance. – The session host must be recreated. 

Azure Bastion: Connecting to a VM over RDP in [a tad over] 200 Crappy Words

Preface: Why only 200 crappy words?

In its Basic SKU, Azure Bastion provides the ability to securely connect to VMs running in Azure via the portal without the need for a public IP address, I say in its Basic SKU because Microsoft has recently released a more feature-rich variant in the form of Standard SKU.

The Standard SKU adds:

This post will focus on the ability to connect MSTSC

If you are going to follow along, you will first need to deploy a Bastion, ensuring it’s of the Standard SKU – click here for the KB describing the process in more detail

You’ll also need to install Azure CLI if you haven’t already, or if you have it installed already I’d suggest upgrading to the latest version to ensure you have the latest command libraries available – – AZ CLI KB here

After deploying a new Standard SKU Bastion and before attempting to connect to a VM you must enable Native Client Support. To do that navigate to your Bastion in the Azure portal, select Configuration from the navigation pane and click “Native Client Support” – note, that this took a few minutes to process.

Once you have completed the above you’re ready to connect to a VM, thankfully there are only a few simple AZ CLI commands to execute:

#The three lines below authenticate you to Azure and select the appropriate subscription which hosts the Bastion and target VM

az login
az account list
az account set --subscription "<subscription ID>"

#The line below initiates the Bastion session to the target VM

az network bastion rdp --name "<BastionName>" --resource-group "<ResourceGroupName>" --target-resource-id "<VMResourceId>"

You’ll be challenged for credentials as expected with any RDP connection and once successfully authenticated you’ll be presented with your remote session!

Word Count: 304! (Minus code excepts)

Azure VM Trusted Launch in 200 Crappy Words

Preface: Why only 200 crappy words?

Trusted Launch provides the ability to improve the security of Generation 2 Azure VMs (as they use UEFI instead of BIOS, thus supporting Secure Boot) and supports several VM SKUs and variants of Windows and Linux.

Trusted Launch provides the capability to deploy Azure VMs with verified boot loaders, OS kernels, and drivers as well as the ability to validate the integrity of a VMs entire boot chain to ensure no root kits have been maliciously inserted. 

Trusted Launch is made up of several technologies that can be enabled independently, they are:

  • Secure Boot protects against the installation of malware-based rootkits and boot kits by ensuring that only signed operating systems and drivers can boot
  • vTPM which is a virtualized version of a hardware Trusted Platform Module, compliant with the TPM2.0 spec, which enables attestation by measuring the entire boot chain of your VM including UEFI, OS and drivers
  • Virtualization Based Security is a secure and isolated region of memory that Windows uses to run various security solutions with increased protection against vulnerabilities and malicious exploits

There is no additional cost or performance overhead to using Trusted Launch!

Trusted Launch can be used for Azure Virtual Desktop session host VMs.

Word Count: 200

After Action Report: AVD Azure AD Join Checklist in 200 Crappy Words

Preface: Why only 200 crappy words?

I’m seeing a growing number of AVD deployments going AAD-only thus decoupling from the technical debt of legacy Active Directory, and in many cases opting for Intune over on-premises SCCM etc for device management.

Thankfully Microsoft makes the process of deploying AAD-only AVD session hosts pretty straightforward, however, the below calls out a few lessons from the field to be mindful of….

  1. Microsoft only supports a limited number of use cases for AAD-joined VMs
  2. There are additional RBAC roles (Virtual Machine User Login and Virtual Machine Administrator User Login) to be considered for AAD joined VMs, correct placement of these roles is key to ensuring ease of management at scale
  3. If the device a user is connecting to an AAD joined AVD VM from is not joined to the same Azure AD as AVD, add targetisaadjoined:i:1 as a custom RDP property to the host pool
  4. If you’re deploying Windows 10 version 1511 or earlier you’ll need to enable the PKU2U protocol, this is enabled by default in version 1607 or later
  5. When configuring Conditional Access for AAD joined AVD, disable legacy per-user MFA and exclude the Azure Windows VM Sign-In app
Word Count: 196

200 Crappy Words

As a short exercise to help me fight procrastination, taking inspiration from the below excerpt from Mark Manson’s brilliant book “The Subtle Art of Not Giving a F*ck” (which I highly recommend), I’m going to attempt to write and share a daily blog about a given subject matter in Azure in 200 crappy words! Word economy is going to be key. Wish me luck.

“I recently heard a story about a novelist who had written over 70 novels. Someone asked him how he was able to write so consistently and remain inspired and motivated every day, as writers are notorious for procrastination and for fighting through bouts of “writer’s block”. The novelist said, “200 crappy words per day, that’s it.” The idea is that if he forced himself to write 200 crappy words, more often than not, the act of writing would inspire him and before he knew it he’d have thousands down on the page.”

Mark Manson

Authors Note: It took me 56 minutes to write this intro, during which time I swapped between Mac and Windows laptop, went from my standing to sitting desk and switched from Word to OneNote

Word Count: 192

Troubleshooting connectivity from the RD Client to WVD Part 2 – Log Analytics

A few weeks ago I introduced an issue I was working on with a customer who was reporting intermittent issues connecting to Windows Virtual Desktop from the RD Client on their corporate devices, you can read that initial post here.

The focus of that post was doing some preliminary checks, namely comparing connectivity from the RD Client against the Web Interface at the time the user was reporting the issue, this would help narrow down whether the problem was environmental, as in, within the boundary of the users’ device, the customer VPN etc as a successful connection from the web interface would go some way to proving the health of WVD control plane, host pool and session host.

A quick recap on activities and results since that initial post…

Tests confirmed that the user could indeed connect using the web interface at the exact same time the RD Client would not connect, note the user in question was no longer working from home at the time, he was in the corporate office connected to their LAN.

Also note that at the time the user could not connect I confirmed that other users were connected to the same WVD resource, and I myself could connect using the RD Client.

We then worked with the customers’ network and security engineer (who maintains their Cisco Umbrella deployment) to systematically walk through the network topology to ensure traffic from that source IP was successfully traversing each node without error.

We also used WireShark to capture traffic from the users’ device, however, I’ll save that particular deep-dive for a follow-up post as I’m still analysing the results. What I’m hoping to do is compare the resultant capture with the WVD connectivity flow (from part 1) and correlate each step to a particular selection of the capture to hopefully show which step is having problems – this is still work in progress.


The below is an example WireShark capture I used with the customer as a comparison, I started the capture and then opened the RD Client, as mentioned in part 1 the RD Client will always refresh your subscriptions on start-up, this is shown from line 203 to 205 in which the initial connection is made after the RDWEB.WVD.MICROSOFT.COM hostname is resolved in DNS, from line 206 a secure connection is initiated to the WVD control plane which validates the TLS certificate against the GlobalSign certificate authority at line 217 to 221, this is one of the reasons internet access is required for WVD as the certificate authority is accessed from its public endpoint.

WVD-WireShark-Capture

I’m hoping to have this analysis and deep-dive completed soon, I’ll document it in a follow-up post after I’ve had it reviewed for and verified for accuracy by the WVD Community and Microsoft WVD Global Black Belts.

 


Back to the troubleshooting…

Frustratingly during the analysis of traffic logs on the customers’ perimeter firewall and having made zero changes the RD Client was then able to connect to WVD!

This now has us, me and the customers tech team, scratching our heads – are we now looking for something dynamic in an otherwise static local architecture that under certain conditions cause these connectivity problems, such as asymmetric routing? Again, more on that in a later post as I’m working with the customer and their other supporting partners to build a diagram of their holistic network architecture.

After a productive conversation with the customers’ aligned Microsoft DSE it was agreed that whilst we wait for the issue to reoccur we would configure the WVD tenant to send logs to an Azure Log Analytics workspace to see if that could shed any light on the situation.

Setting up a new Log Analytics Workspace is very straight forward, from the search bar in the Azure Portal enter Log Analytics, from the results select Log Analytics Workspaces.

WVD-Log-Anal-Setup-3

Select Add.

WVD-Log-Anal-Setup-4

Select an appropriate Subscription and Resource Group, then provide a suitable name for the Workspace and a region to host it, ideally use the same region as the WVD session hosts are deployed.

WVD-Log-Anal-Setup-1

Click ‘Next: Pricing Tier’ to proceed.

Select a pricing tier, details on pricing tiers can be found here.

WVD-Log-Anal-Setup-2

Proceed through the next two options entering appropriate tags and finally review and create to create the Log Analytics workspace.

Once created the Log Analytics workspace will display a Workspace ID, note this down as you’ll need it later.

Note, the next steps to connect the WVD tenant apply to the Fall 2019 release only, the setup for Spring 2020 release differs.

Binding your WVD tenant quick and straight forward following this Microsoft article.

Before you start you’ll need the below info:

* WVD tenant name
* Azure subscription ID
* Log Analytics Workspace ID (available from Overview pane on the workspace itself)
* Log Analytics Primary Key (available from Advanced Settings > Connected Sources)

Once you have the above open PowerShell and run the below commands:

Add-RdsAccount -DeploymentUrl https://rdbroker.wvd.microsoft.com
Set-RdsTenant -Name  -AzureSubscriptionId  -LogAnalyticsWorkspaceId  -LogAnalyticsPrimaryKey

This will bind your WVD tenant to the Log Analytics workspace.

Be aware it can take up to an hour before log data will start to appear so be patient.

Note, connecting the WVD tenant to the Log Analytics Workspace will send log data at the tenant only, such as user-driven connection events, it will not send VM or guest OS performance stats, for that you must install the Windows Agent on the VM following this article.

WVD creates the three below custom logs in the workspace.

WVD-Log-Anal-Work-1

Like any log managment solution the key is often knowing what you’re looking for and how to find it, Log Analytics is no different, Microsoft provides two great example queries to get you started, here.

To query the workspace select Logs under General, this will open a new pane for the query editor.

I adapted one Microsoft’s example queries to show just failed connections from the RD Client where ClientType_s == “com.microsoft.rdc.windows.msrdc.x64” denotes connections from the RD Client, see below:

WVDActivityV1_CL

| where Type_s == "Connection"

| where Outcome_s == "Failure"

| where ClientType_s == "com.microsoft.rdc.windows.msrdc.x64"

| join kind=leftouter

(

WVDErrorV1_CL

| summarize Errors = makelist(pack('Time', Time_t, 'Code', ErrorCode_s , 'CodeSymbolic', ErrorCodeSymbolic_s, 'Message', ErrorMessage_s, 'ReportedBy', ReportedBy_s , 'Internal', ErrorInternal_s )) by ActivityId_g

)

on $left.Id_g  == $right.ActivityId_g

| join  kind=leftouter

(

WVDCheckpointV1_CL

| summarize Checkpoints = makelist(pack('Time', Time_t, 'ReportedBy', ReportedBy_s, 'Name', Name_s, 'Parameters', Parameters_s) ) by ActivityId_g

)

on $left.Id_g  == $right.ActivityId_g

|project-away ActivityId_g, ActivityId_g1

Select a time range, highlight the entire query (Control+A) and click Run.

The results will be displayed as below, this shows two failed attempts from the RD Client in the past 24 hours, this also shows the user is running Windows 10 build 1809 (11763) and version 1.2.945.0 of the RD Client – both extremely useful pieces of the information when analysing data and looking for trends.

WVD-Log-Anal-Work-2

Drilling down into each of those failed connections though is where the gold is!

From the left-most side of each log is an arrow to expand, the first tier provides a summary of the connection.

WVD-Log-Anal-Work-3

Afterwhich is a subset of the log displaying any errors, you can see from the below that this session failed with error code 2055, SSL_ERR_LOGON_FAILURE.

Now, I’m not going to profess to know exactly what that particular error is caused by but it is certainly enough to either focus and further troubleshooting or raise a support request with Microsoft.

WVD-Log-Anal-Work-4

Lastly, the final hive in the log in extremely interesting in that it walks you through the checkpoints of the connection flow, you can view the result of each checkpoint.

WVD-Log-Anal-Work-5

If you’re interested in engaging more with others using and learning more about WVD please consider following the WVD Community on Twitter and joining the Slack channel, both ran by Neil and Stefan, and both great sources of info!

Thanks.

QuickFix > Azure VM Run Command (A Life Saver)

As a long time SysAdmin managing estates of hundreds, if not thousands of servers, especially in the pre-virtualisation days, it wasn’t uncommon to look at your taskbar and see dozens of open RDP sessions, one for every ticket you’d dealt with that day, or week if you weren’t the most disciplined at logging off and became ‘that guy’ who hogged all the sessions, you know who you are 🙂

But what if RDP wasn’t available, the session didn’t connect, what then?

Thankfully in those days, and even now, we typically have the safety net of an out-of-band management console, such as Dell iDRAC or HP iLO to use, but what if that was unavailable too, we would have to resort to remote execution tools such as PSExec to get us out of dodge.

This can be a common scenario with server workloads running in public clouds too, given the options around remote connectivity and management are greatly improving, negating the need to use protocols such as RDP as a primary means to connect to a server GUI, Azure Bastion for example, but what if you come across that same scenario, total lack of a GUI on a VM that has no public-facing interfaces?

I had that exact scenario this week, I span up a Windows 10 Azure VM, nonchalantly enrolled it into Azure AD, restarted and could no longer connect via RDP.

As it happens the Intune Configuration Policies and taken effect, removed the standard local administrator account but for some reason failed to create the new admin account meaning I was locked out.

Now, in hindsight, I could have just as easy deleted the VM and recreated it after I’d rectified the issue with the policies, but instead, I used the hidden gem that is the Run Command to fix it.

What is the Run Command?

“The Run Command feature uses the virtual machine (VM) agent to run PowerShell scripts within an Azure Windows VM. You can use these scripts for general machine or application management. They can help you to quickly diagnose and remediate VM access and network issues and get the VM back to a good state”

In short, it’s a quick and easy way to access an administrative console on a Windows VM, even if you do have remote connectivity into the GUI, if the task you need to run, or the question you need to answer is answerable from the command line it’s often quicker to use the Run Command. 

The Run Command is found under Operations.

WVD-RunCommand

Microsoft provide a number of handy preset commands for performing common tasks such as executing an IPConfig or enabling the local admin account, however, if you simply need access to the command line there is the top option to RunPowerShellScript.  

WVD-RunCommand-Menu

This presents a text box to enter commands, clicking Run to execute.

Note, commands are run as NT Authority\System. 

WVD-RunCommand-WhoAmI

It was using this option that I resolved my connectivity issues, I used the below hash of command and Powershell to create a temporary admin account to allow me to logon to the VM.

net user /add TempAdmin 7ABLT*C#4!Lz
Set-LocalUser -Name TempAdmin -PasswordNeverExpires $True
Set-LocalUSer -Name TempAdmin -Description "Azure Run Command Created Temp Admin User"
net localgroup Administrators /add TempAdmin

The Output dialog shows the results of the script.

WVD-RunCommand-Fix
Lastly, for the wider Windows Virtual Desktop community, especially those managing Fall 2019 workloads, I’ve found it far quicker to run QWINSTA from the Run Command on a WVD VM to report on session states than it is to launch Powershell, connect to Azure AD, connect to RDWeb and query the hostpool, with the obvious limitation you’re querying a single host.

WVD-RunCommand-QWINSTA

Troubleshooting connectivity from the RD Client to WVD Part 1 – Getting Started

This week I’ve been working with a customer that is experiencing intermittent issues connecting into Windows Virtual Desktop from the RD Client installed on their corporate Windows 10 devices and was asked to formulate a list of troubleshooting steps that their IT team could follow to help find and resolve the root cause.

I reached out to Jim Moyle, our aligned WVD Global Black Belt for his thoughts, and in this post, I want to share those initial troubleshooting steps, along with the rationale for each, put them out to the wider WVD community for feedback and over the coming weeks update this article with the identified root cause and the steps taken to resolve the issue.

For info and early clarification, this customer is using the Fall 2019 release of Windows Virtual Desktop.

So, what is the problem?

As introduced, the customer is reporting that certain users, not all, are intermittently unable to connect to their WVD resource using the RD Client installed on their corporate Windows 10 device, furthermore, they never receive any errors (such as resources not available) nor have they indicated that the connection attempt times out (I’ll double-check this and update if required), it simply doesn’t connect.

The below screenshot was provided by an affected user, this shows the RD Client attempting to connect to a selected remote desktop.

WVD-Connection-Issue

Before delving any deeper and starting to define the tests to be undertaken lets quickly recap on how a user connects to WVD, that way once we do start defining a particular test we better understand the rationale behind it, that is, what are we trying to prove or disprove.

The diagram below shows the WVD connection flow.

WVD-Traffic-Flow

Step 1 > The user launches the RD Client which connects to Azure AD, user signs in, and Azure AD returns token.
Step 2 > The RD Client uses the previously generated token and authenticates to Web Access, the Broker then queries the database to determine the resources (Remote Apps and Desktops) that the user is assigned to.
Step 3 > The user selects a resource (Remote App or Desktop) and the RD Client connects to the Gateway.
Step 4 > Finally, the Broker orchestrates the connection from the WVD instance (the Azure VM) to the Gateway (aka Reverse Connect).

Note, on start-up the RD Client will always refresh your feed, as below, this is the RD Client running through steps 1 and 2 in the above connection flow.

WVD-Refreshing-Feed

Now, let’s quickly cover some basic assumptions before we get into the testing, again I’ll update these as I speak with the customer IT team and understand more of the nuances of the issue.

Assumption 1 > WVD is a global service, as such, for resilience, it operates many instances of the WVD control plane (the backend services, such as Web Access, Broker and Gateway shown in the connection flow diagram) in each region, however, based on availability at the time the control plane managing your user’s connections into WVD may not be running in the same region as the WVD VM’s themselves. Azure use their Front Door (and Traffic Manager) service to provide a resilient and optimised connection to the control plane – let’s assume a control plane is always up and available.

However, if we wanted to clarify the control plane you’re using is healthy we could use the below Powershell commands.

# Import Fall 2019 WVD module
Import-Module -Name Microsoft.RDInfra.RDPowerShell

# Connect to WVD
Add-RdsAccount -DeploymentUrl "https://rdbroker.wvd.microsoft.com"

# Get control plane info
Invoke-RestMethod -Uri "https://rdweb.wvd.microsoft.com/api/health"

The below shows the results of the script, note the service is reporting as healthy and more importantly the Region URL shows that actual control plane you’re using.

WVD-Get-Control-Plane

Assumption 2 > This is only affecting certain users, other users can successfully connect to the same WVD resource at the same time others cannot indicating that the WVD VM’s themselves are healthy.

Again, if we want to verify the health of the WVD session hosts within a given host pool we could use the below Powershell commands.

# Import Fall 2019 WVD module
Import-Module -Name Microsoft.RDInfra.RDPowerShell

# Connect to WVD
Add-RdsAccount -DeploymentUrl "https://rdbroker.wvd.microsoft.com"

# Set Session Host Status
Get-RdsSessionHost -TenantName WVD-Tenant-Name -HostPoolName WVD-Host-Pool-Name | Select SessionHostName, Status

The results will be shown as below.

WVD-Session-Health

Assumption 3 > This is only affecting users on their corporate devices, I’ll double-check this with the customer IT team and update if needed.

Assumption 4 > The customer has all the WVD backend services opened and available through the corporate firewall and web proxies.  I know they use Cisco Umbrella for web proxy services, so something to be mindful of.

Assumption 5 > The user sees the same issue if they are on their corporate LAN, VPN or connected to the open internet from their home broadband as the majority of their staff are working from home.

So, what tests are we going to run when a user reports they cannot connect and why?

Test 1 > Can the user access the same WVD resource from the HTML5 interface at https://aka.ms/wvdweb?

Why? We need to narrow down whether the issues is only with the connection initiated from the RD Client, testing from the WVD Web Interface should help prove this.  If the user is able to authenticate to the web interface, see all of their assigned WVD resources and then successfully logon to a desktop it proves the issue is not with the control plane, their assignment or WVD session host.

Test 2 > Can the user resolve the WVD Global URL in DNS?

Why?  I’m almost certain that whatever the route cause is it will be environmental, that is, something occurring at that exact time on that device that is hindering the connection attempt. This is a very simple test to ensure that the device is able to resolve the WVD Global URL that is used to forward to the regional control plane instance and initiate the connections.

C:\Users\DeanLawrence>nslookup rdweb.wvd.microsoft.com
Server:  SkyRouter.Home
Address:  192.168.0.1

Non-authoritative answer:
Name:    waws-prod-db3-3dec2181.cloudapp.net
Address:  137.116.248.148
Aliases:  rdweb.wvd.microsoft.com
          rdweb-prod-geo.trafficmanager.net
          mrs-neur1c100-rdweb-prod.wvd-ase-neur1c100-prod.p.azurewebsites.net
          waws-prod-db3-3dec2181.sip.p.azurewebsites.windows.net

We could build on that test slightly using the Test-NetConnection cmdlet to test connectivity to the Regional URL (from test 2) over HTTP.

PS C:\Users\DeanLawrence> Test-NetConnection rdweb-neu-r1.wvd.microsoft.com -CommonTCPPort HTTP -InformationLevel Detailed

ComputerName : rdweb-neu-r1.wvd.microsoft.com
RemoteAddress : 137.116.248.148
RemotePort : 80
NameResolutionResults : 137.116.248.148
MatchingIPsecRules : 
NetworkIsolationContext : Internet
IsAdmin : False
InterfaceAlias : WiFi
SourceAddress : 192.168.0.124
NetRoute (NextHop) : 192.168.0.1
TcpTestSucceeded : True

Test 3 > Clear RD Client Subscriptions and Re-Subscribe

Why? As mentioned earlier, on start-up the RD Client performed a refresh of the feed, this then caches the subscription details in the registry.  This test is looking at whether there could potentially be an issue either with the cached settings or the authentication token. You can unsubscribe from the RD Client itself by clicking the 3 dot menu next to the tenant name and selecting Unsubscribe or running the below from a command prompt.

msrdcw.exe /reset /f

Well, that’s it for now (16/05/20), if you have any suggestions please send them my way on Twitter, I’ll update this post as soon as I’ve had a chance to work with the customers IT team and investigate these issues closer.

Thanks.

Part 2 in this blog series is up now, click here.

QuickFix > Starting and Stopping Azure VM’s using a Powershell Menu

This is another quick fix to help automate a somewhat monotonous task, this time it’s using Powershell to build a simple menu to give the ability to start and stop a set of Azure virtual machines using a Service Principal.

The script is pretty straight forward and can be easily adapted to add additional VM’s should you need to power up, or down, a larger set of virtual machines.

For ease and to keep this as hands-off as possible this script uses a Service Principal to authenticate to Azure AD and perform the task setting the VM power state, this saves having to manually enter credentials and responding to MFA challenges.
I’ve borrowed and adapted the Powershell commands directly from this Microsoft Windows Virtual Desktop article which can be used to create a Service Principal if you don’t have one created already.
# This script creates a Service Principal within Azure AD

# Install and import AzureAD PS Module
Install-Module AzureAD
Install-Module AzureAD
# Connect to Azure AD with a Global Admin account.
$aadContext = Connect-AzureAD

# Create Service Principal
$svcPrincipal = New-AzureADApplication -AvailableToOtherTenants $true -DisplayName "VM Power Menu Service Principal"

# Get Service Principal ID and Key 
$svcPrincipalCreds = New-AzureADApplicationPasswordCredential -ObjectId $svcPrincipal.ObjectId

# Return Service Principal App ID
$svcPrincipal.AppId

# Return Service Principal Secret Key
$svcPrincipalCreds.Value

# Return Azure AD Tenant ID
$aadContext.TenantId.Guid

Before proceeding please ensure that the newly created Service Principal has the adequate permissions to start and stop the required virtual machines, this can be set directly on the VM’s themselves or on the Resource Group.

Note, if you’re setting these permissions in a production environment or an environment that is particularly security-sensitive please be mindful of the principle of least privileges, that is, avoid using elevated roles such as Owner or Contributor role if a lesser role would suffice, such as Virtual Machine Contributor.

Once the Service Principal has been created copy and paste the below into your code editor of choice, you will need to set the initial variables in the header, you can use the resultant output from the script above for the Azure AD Tenant ID  and Service Principal details.  You will also need to populate the two Azure VM variables and the associated hosting Resource Group.

Import-Module Az.Compute

$AADTenant = [Azure-AD-Tenant-ID-Here]
$SPAppID = [Service-Principal-App-ID]
$SPAppSecret = [Service-Principal-Secret-Key-Here]
$WVDVM1 = [Second-VM-To Start]
$WVVM2 = [Second-VM-To Start]
$AzRG = [Resource-Group-Hosting-VMs-Here]
$passwd = ConvertTo-SecureString $SPAppSecret -AsPlainText -Force
$pscredential = New-Object System.Management.Automation.PSCredential('$SPAppID', $passwd)

Connect-AzAccount -ServicePrincipal -Credential $pscredential -Tenant $AADTenant

function Show-Menu
{
     param (
           [string]$Title = 'Start-Stop WVD Lab'
     )
     cls
     Write-Host "================ $Title ================"
     Write-Host "1: Press '1' to start WVD."
     Write-Host "2: Press '2' to stop WVD."
     Write-Host "Q: Press 'Q' to quit."
}
do
{
     Show-Menu
     $input = Read-Host "Please make a selection"
     switch ($input)
     {
           '1' {
                cls
                'Starting WVD'
                    Start-AzVM -Name $WVDVM1 -ResourceGroupName $AzRG
                    Start-AzVM -Name $WVDWM2 -ResourceGroupName $AzRG
           } '2' {
                cls
                'Stopping WVD'
                    Stop-AzVM -Name $WVDVM1 -ResourceGroupName $AzRG -Force
                    Stop-AzVM -Name $WVDWM2 -ResourceGroupName $AzRG -Force
           } 'q' {
                return
           }
     }
     #pause
}
until ($input -eq 'q')

When executed the script presents a simple menu with 3 choices as below.

WVD-Start-Script

Note, after executing either options 1 or 2 the script will intentionally not exit and close, instead, it will await a response of Q to quit – this was done intentionally as a gentle reminder to the user to stop the VM’s they’d originally started.

Lastly, as a matter of good practice, for lab environments that don’t need to spin around the clock, for subscriptions that use free credit, or you’re just trying to keep costs down I’d always recommend setting an Auto-Shutdown time on non-production VM’s as a backup.

QuickFix > Set Working Directory on WVD RemoteApp

This one might be old news to some but I thought I’d share it as it got me out of jam earlier this week.

I needed to create a RemoteApp in WVD (Spring Release) for a proof of concept I was working on, however, this application required a working directory to be set for it to launch correctly.

I’ll stress this was a quick fix and there may be a more elegant way to achieve the same result but I was against the clock at the time.

The quick fix was to create a batch file locally on the WVD instance with the below syntax:

start /d c:\App-Working-Directory-Here c:\Executable-To-Launch-Here.exe

eg.

start /d c:\EstGrp\CM\830\CM\USER c:\EstGrp\CM\BINW\cmw32.exe

Once the batch file is saved, from the WVD Management UI create a new RemoteApp in the relevant Application Group and enter the path the batch file in the Application Path, continue to enter the Display Name and set the Icon Path to the path to the executable as normal:

WVDv2-Batch

As expected when you launch the application from the RD Client or WVD Web Interface (from the correct URL, thanks to Tom and Marcel 🙂 the batch file will launch, albeit quickly and then launch the required app.

Note, the same could be achieved using PowerShell with the below syntax:

Start-Process -FilePath C:\EstGrp\CM\BINW\cmw32.exe -WorkingDirectory C:\EstGrp\CM\830\CM\USER

If you have been in a similar situation in needing to set the Working Directory, or some other attribute and have a more elegant solution please feel free to share it, either in the comments below or on Twitter.