Mastering Azure DevTest Labs: An Essential Setup Guide for SMBs

For many small and medium-sized businesses (SMBs), managing development and testing environments can be a significant drain on resources. Inconsistent setups, forgotten VMs running overnight, and the time spent manually provisioning environments all contribute to inefficiency and escalating costs. This is where Azure DevTest Labs becomes an indispensable tool. It provides a self-service model for developers and testers to quickly spin up, manage, and tear down Azure Virtual Machines (VMs) and environments, all while enforcing robust cost controls and streamlining workflows.
This comprehensive Azure DevTest Labs setup guide from SkyCore Solutions will walk you through the process of establishing a secure, cost-effective, and highly efficient DevTest Labs environment. By the end of this guide, you will have a fully configured Azure DevTest Lab, complete with essential policies, a network integration, and your first provisioned VM, ready for your team to accelerate their development and testing cycles.
Prerequisites
- Azure Subscription: An active Azure subscription with at least Contributor access at the subscription level or to a resource group where you intend to deploy the lab.
- Azure CLI: The latest version of the Azure CLI installed on your workstation. Refer to the official Microsoft documentation for installation.
- Azure PowerShell: The latest version of the Azure PowerShell Az module installed. Follow the Microsoft guide for installation.
- Basic Azure Knowledge: Familiarity with Azure Resource Groups and Virtual Machines is beneficial.
- Cost Implications: While Azure DevTest Labs itself is a free service, you incur costs for the underlying Azure resources (VMs, storage, network) consumed within the lab. This guide heavily emphasizes cost control features.
- Notification Endpoint: An email address or a webhook URL (e.g., for Microsoft Teams, Slack, or custom service) to receive autoshutdown notifications.
Step 1: Prepare Your Azure Environment
Before deploying any new Azure service, it's best practice to create a dedicated resource group. This helps organize your resources, simplifies management, and makes it easy to delete all related resources when they are no longer needed, preventing lingering costs.
az group create \
--name "SkyCore-DevTestLabs-RG" \
--location "canadacentral"
--name "SkyCore-DevTestLabs-RG": Specifies the name for your new resource group. We recommend a naming convention that indicates its purpose.--location "canadacentral": Sets the Azure region where the resource group will be deployed. Choose a region geographically close to your users for optimal performance.
New-AzResourceGroup `
-Name "SkyCore-DevTestLabs-RG" `
-Location "Canada Central"
-Name "SkyCore-DevTestLabs-RG": The name of the resource group.-Location "Canada Central": The Azure region for the resource group.
Portal alternative: From the Azure portal, search for "Resource groups" > Select "Create" > Fill in Subscription, Resource group name (e.g., SkyCore-DevTestLabs-RG), and Region (e.g., Canada Central) > Click "Review + create" then "Create".
Run this to verify:
az group show --name "SkyCore-DevTestLabs-RG" --query name
Get-AzResourceGroup -Name "SkyCore-DevTestLabs-RG" | Select-Object ResourceGroupName
Step 2: Create the Azure DevTest Lab
Now, let's create the DevTest Lab itself. This is the central hub where all your development and testing VMs will reside. When creating the lab, it's crucial for SMBs to immediately enable and configure autoshutdown policies to prevent VMs from running unnecessarily and incurring costs overnight or on weekends.
az lab create \
--resource-group "SkyCore-DevTestLabs-RG" \
--name "SkyCore-DevLab" \
--location "canadacentral" \
--autoshutdown-status Enabled \
--autoshutdown-time 1900 \
--autoshutdown-timezone "Canada/Eastern" \
--autoshutdown-notification-status Enabled \
--autoshutdown-notification-emails "devops@skycore.ca" \
--tags Project=DevTest Environment=DevOps
--resource-group "SkyCore-DevTestLabs-RG": The resource group created in Step 1.--name "SkyCore-DevLab": A unique name for your DevTest Lab.--location "canadacentral": The region for the lab, ideally matching your resource group.--autoshutdown-status Enabled: Crucial for SMB cost control. Automatically shuts down all lab VMs daily.--autoshutdown-time 1900: Sets the autoshutdown time to 7:00 PM (19:00 military time).--autoshutdown-timezone "Canada/Eastern": Specifies the time zone for the autoshutdown schedule.--autoshutdown-notification-status Enabled: Sends a notification before autoshutdown.--autoshutdown-notification-emails "devops@skycore.ca": The email address to receive autoshutdown notifications. You could also use--autoshutdown-notification-webhooks "https://your-teams-webhook-url"for a Teams or Slack channel.--tags Project=DevTest Environment=DevOps: Applies tags for better resource management and cost allocation.
New-AzDevTestLab `
-ResourceGroupName "SkyCore-DevTestLabs-RG" `
-Name "SkyCore-DevLab" `
-Location "Canada Central" `
-AutoShutdownStatus Enabled `
-AutoShutdownTime 1900 `
-AutoShutdownTimeZone "Canada/Eastern" `
-AutoShutdownNotificationStatus Enabled `
-AutoShutdownNotificationMail "devops@skycore.ca" `
-Tag @{ Project="DevTest"; Environment="DevOps" }
- Parameters are analogous to the Azure CLI commands.
-Tag @{ Project="DevTest"; Environment="DevOps" }: PowerShell syntax for defining tags.
Portal alternative: Search for "DevTest Labs" > Select "Create" > Fill in "Subscription", "Resource group" (SkyCore-DevTestLabs-RG), "Lab name" (SkyCore-DevLab), and "Location". On the "Auto-shutdown" tab, set "Enabled" to On, specify "Scheduled shutdown" time (e.g., 19:00) and "Time zone" (e.g., Canada/Eastern), and configure notifications. Finally, click "Review + create" then "Create".
Run this to verify:
az lab show --resource-group "SkyCore-DevTestLabs-RG" --name "SkyCore-DevLab" --query '{name: name, location: location, autoshutdownStatus: autoshutdown.status}'
Get-AzDevTestLab -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name "SkyCore-DevLab" | Select-Object Name, Location, AutoShutdownStatus
The single biggest cause of unexpected Azure costs in DevTest environments is forgetting to shut down VMs. Even if you don't configure all policies initially, ensure autoshutdown is enabled from day one. It's the most effective immediate cost-saving measure for any Azure DevTest Labs setup guide.
Step 3: Configure Essential Lab Policies for Cost Control
With your lab created, the next step is to enforce more granular cost controls and governance. DevTest Labs policies allow you to define limits on VM sizes, the number of VMs a user can create, and the total number of VMs in the lab. For SMBs, these policies are critical for managing budget and preventing resource sprawl.
# Set policy for allowed VM sizes
az lab policy update \
--lab-name "SkyCore-DevLab" \
--resource-group "SkyCore-DevTestLabs-RG" \
--name AllowedVmSizesInLab \
--properties '{"status": "Enabled", "threshold": "[ \"Standard_D2s_v5\", \"Standard_E2s_v5\" ]"}'
# Set policy for maximum VMs per user (e.g., 1 VM per developer)
az lab policy update \
--lab-name "SkyCore-DevLab" \
--resource-group "SkyCore-DevTestLabs-RG" \
--name MaxVmsAllowedPerUser \
--properties '{"status": "Enabled", "threshold": "1"}'
# Set policy for maximum total VMs in the lab
az lab policy update \
--lab-name "SkyCore-DevLab" \
--resource-group "SkyCore-DevTestLabs-RG" \
--name MaxTotalVmsAllowed \
--properties '{"status": "Enabled", "threshold": "5"}'
--name AllowedVmSizesInLab: This policy defines which VM sizes users can select when creating new VMs. For SMBs,Standard_D2s_v5(2 vCPUs, 8 GB RAM) orStandard_E2s_v5(2 vCPUs, 16 GB RAM) offer a good balance of performance and cost for most dev/test workloads. Thethresholdvalue is a JSON array of allowed SKU names.--name MaxVmsAllowedPerUser: Limits the number of active VMs a single lab user can create. Setting this to1(or2) is a strong recommendation for SMBs to prevent individual users from consuming excessive resources.--name MaxTotalVmsAllowed: Sets the overall maximum number of VMs that can exist in the lab at any given time. This helps manage the lab's total capacity and cost.--properties '{"status": "Enabled", "threshold": "..."}': This JSON string defines the policy's status (EnabledorDisabled) and its specific limit (threshold). Note the escaped double quotes within the threshold JSON forAllowedVmSizesInLab.
# Set policy for allowed VM sizes
$labName = "SkyCore-DevLab"
$rgName = "SkyCore-DevTestLabs-RG"
$allowedSizes = '[ "Standard_D2s_v5", "Standard_E2s_v5" ]'
Set-AzDevTestLabPolicy -LabName $labName -ResourceGroupName $rgName -Name AllowedVmSizesInLab -Status Enabled -Threshold $allowedSizes
# Set policy for maximum VMs per user
Set-AzDevTestLabPolicy -LabName $labName -ResourceGroupName $rgName -Name MaxVmsAllowedPerUser -Status Enabled -Threshold "1"
# Set policy for maximum total VMs in the lab
Set-AzDevTestLabPolicy -LabName $labName -ResourceGroupName $rgName -Name MaxTotalVmsAllowed -Status Enabled -Threshold "5"
-Name AllowedVmSizesInLab,-Name MaxVmsAllowedPerUser,-Name MaxTotalVmsAllowed: Specifies the policy to update.-Status Enabled: Enables the policy.-Threshold "...": Sets the limit for the policy. Note that forAllowedVmSizesInLab, the threshold is a JSON string representing an array.
Portal alternative: Navigate to your DevTest Lab > Under "Settings", select "Configuration and policies" > Under "Policies", click on each policy (e.g., "Allowed virtual machine sizes", "Virtual machines per user", "Virtual machines per lab") > Set "Status" to On and configure the specific limits > Click "Save".
Run this to verify:
az lab policy show --lab-name "SkyCore-DevLab" --resource-group "SkyCore-DevTestLabs-RG" --name AllowedVmSizesInLab --query '{name: name, status: status, threshold: threshold}'
az lab policy show --lab-name "SkyCore-DevLab" --resource-group "SkyCore-DevTestLabs-RG" --name MaxVmsAllowedPerUser --query '{name: name, status: status, threshold: threshold}'
az lab policy show --lab-name "SkyCore-DevLab" --resource-group "SkyCore-DevTestLabs-RG" --name MaxTotalVmsAllowed --query '{name: name, status: status, threshold: threshold}'
Get-AzDevTestLabPolicy -LabName "SkyCore-DevLab" -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name AllowedVmSizesInLab | Select-Object Name, Status, Threshold
Get-AzDevTestLabPolicy -LabName "SkyCore-DevLab" -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name MaxVmsAllowedPerUser | Select-Object Name, Status, Threshold
Get-AzDevTestLabPolicy -LabName "SkyCore-DevLab" -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name MaxTotalVmsAllowed | Select-Object Name, Status, Threshold
For repeatable and consistent VM configurations, consider creating a "Formula" within your DevTest Lab. A formula is a base VM image with pre-installed software and settings. Users can then create VMs from this formula, ensuring everyone starts with the same environment. This significantly reduces setup time and ensures consistency across your dev/test teams.
Step 4: Add a Virtual Network (Optional but Recommended)
While DevTest Labs creates a default virtual network, for most SMBs, integrating your lab with an existing or new dedicated virtual network is highly recommended. This allows for better network isolation, controlled access to other Azure resources (like databases or storage accounts), or even hybrid connectivity back to your on-premises network. Note that associating a VNet directly via CLI during az lab create is not explicitly supported by the provided documentation; it's typically configured via the Azure portal during creation or added afterwards.
First, let's create a new virtual network and subnet if you don't have one already. If you already have a VNet, you can skip this step and use its details in the portal configuration.
# Create a new Virtual Network
az network vnet create \
--resource-group "SkyCore-DevTestLabs-RG" \
--name "SkyCore-DevLab-VNet" \
--location "canadacentral" \
--address-prefix "10.0.0.0/16"
# Create a subnet within the VNet for DevTest Labs VMs
az network vnet subnet create \
--resource-group "SkyCore-DevTestLabs-RG" \
--vnet-name "SkyCore-DevLab-VNet" \
--name "DevLabSubnet" \
--address-prefix "10.0.1.0/24"
--name "SkyCore-DevLab-VNet": Name for your new virtual network.--address-prefix "10.0.0.0/16": The address space for the VNet. Ensure this doesn't conflict with other networks if you plan for hybrid connectivity.--name "DevLabSubnet": A dedicated subnet within the VNet for your DevTest Labs VMs.--address-prefix "10.0.1.0/24": The address range for the subnet.
# Create a new Virtual Network
New-AzVirtualNetwork `
-ResourceGroupName "SkyCore-DevTestLabs-RG" `
-Name "SkyCore-DevLab-VNet" `
-Location "Canada Central" `
-AddressPrefix "10.0.0.0/16"
# Create a subnet within the VNet for DevTest Labs VMs
Add-AzVirtualNetworkSubnetConfig `
-Name "DevLabSubnet" `
-AddressPrefix "10.0.1.0/24" `
-VirtualNetwork (Get-AzVirtualNetwork -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name "SkyCore-DevLab-VNet") | `
Set-AzVirtualNetwork
- Parameters are analogous to the Azure CLI commands.
Add-AzVirtualNetworkSubnetConfig ... | Set-AzVirtualNetwork: PowerShell pattern to add a subnet to an existing VNet object and then update the VNet in Azure.
Portal alternative (for associating VNet with Lab): Navigate to your DevTest Lab > Under "Settings", select "Configuration and policies" > Select "Virtual networks" > Click "+ Add" > Select the desired Virtual Network (e.g., SkyCore-DevLab-VNet) and its subnet (e.g., DevLabSubnet) > Click "Save". Alternatively, during lab creation, on the "Networking" tab, select your custom VNet and subnet.
Run this to verify (VNet and Subnet creation):
az network vnet show --resource-group "SkyCore-DevTestLabs-RG" --name "SkyCore-DevLab-VNet" --query '{name: name, subnets: subnets[].name}'
Get-AzVirtualNetwork -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name "SkyCore-DevLab-VNet" | Select-Object Name, @{Name='Subnets'; Expression={$_.Subnets.Name}}
Step 5: Provision a Virtual Machine within the Lab
With the lab and policies in place, it's time to create your first virtual machine. This VM will automatically inherit the policies defined for the lab, ensuring it adheres to your organization's cost controls and standards. For SMBs, using a public Marketplace image is the quickest way to get started.
az lab vm create \
--lab-name "SkyCore-DevLab" \
--resource-group "SkyCore-DevTestLabs-RG" \
--name "DevVM-001" \
--image "MicrosoftWindowsDesktop:windows11-22h2-pro:win11-22h2-pro-g2:latest" \
--size "Standard_D2s_v5" \
--admin-username "devuser" \
--admin-password "P@ssw0rdSkyCore!1" \
--generate-public-ip true
--lab-name "SkyCore-DevLab": The name of your DevTest Lab.--resource-group "SkyCore-DevTestLabs-RG": The resource group containing your lab.--name "DevVM-001": A unique name for your new VM.--image "MicrosoftWindowsDesktop:windows11-22h2-pro:win11-22h2-pro-g2:latest": Specifies the base image for the VM. We recommend a recent Windows 11 Pro image for development. You can find other image URNs in the Azure portal or viaaz vm image list.--size "Standard_D2s_v5": The VM size. This must be one of the sizes allowed by your lab's policies (e.g.,Standard_D2s_v5).--admin-username "devuser": The administrator username for the VM.--admin-password "P@ssw0rdSkyCore!1": A strong password for the administrator user. Note: For production-like environments or increased security, consider using Azure Key Vault to store and retrieve passwords, although this adds complexity.--generate-public-ip true: Assigns a public IP address to the VM, allowing direct RDP/SSH access. For private internal networks, you might omit this and use bastion host or VPN.
New-AzDevTestLabVM `
-LabName "SkyCore-DevLab" `
-ResourceGroupName "SkyCore-DevTestLabs-RG" `
-Name "DevVM-001" `
-ImageName "MicrosoftWindowsDesktop:windows11-22h2-pro:win11-22h2-pro-g2:latest" `
-Size "Standard_D2s_v5" `
-AdminUsername "devuser" `
-AdminPassword "P@ssw0rdSkyCore!1" `
-GeneratePublicIp
- Parameters are analogous to the Azure CLI commands.
-GeneratePublicIp: Equivalent to--generate-public-ip true.
Portal alternative: Navigate to your DevTest Lab > Click "+ Add" > On the "Choose a base" page, select your desired image (e.g., Windows 11 Pro, version 22H2) > On the "Basic Settings" tab, fill in "Virtual machine name", "User name", "Password", and select "Virtual machine size" > Click "Create".
Run this to verify:
az lab vm show --lab-name "SkyCore-DevLab" --resource-group "SkyCore-DevTestLabs-RG" --name "DevVM-001" --query '{name: name, size: size, provisioningState: provisioningState}'
Get-AzDevTestLabVM -LabName "SkyCore-DevLab" -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name "DevVM-001" | Select-Object Name, Size, ProvisioningState
Step 6: Automate VM Setup with Artifacts (Advanced)
Artifacts are powerful pre-packaged scripts or tools that can be automatically applied to a VM during or after its creation. They allow you to automate tasks like installing software, running configuration scripts, or deploying applications. This ensures consistency across your dev/test environments and reduces manual setup time. While the core DevTest Labs provides a public repository of artifacts, SMBs often benefit from creating their own private repositories for custom scripts and applications.
# Add a private Git artifact repository to your lab
az lab artifact-source create \
--lab-name "SkyCore-DevLab" \
--resource-group "SkyCore-DevTestLabs-RG" \
--name "SkyCorePrivateArtifacts" \
--display-name "SkyCore Private Artifacts" \
--source-type GitHub \
--uri "https://github.com/your-org/devtest-artifacts.git" \
--folder-path "/artifacts" \
--branch-ref "main" \
--personal-access-token "YOUR_GITHUB_PAT"
--name "SkyCorePrivateArtifacts": A name for your artifact source within the lab.--source-type GitHub: Specifies the type of repository (GitHub, VSTS, etc.).--uri "https://github.com/your-org/devtest-artifacts.git": The URL of your private Git repository.--folder-path "/artifacts": The path within your repository where artifacts definitions are stored.--branch-ref "main": The default branch to pull artifacts from.--personal-access-token "YOUR_GITHUB_PAT": A GitHub Personal Access Token (PAT) withreposcope to access your private repository. Store this securely in Azure Key Vault and retrieve programmatically in production!
# Add a private Git artifact repository to your lab
$pat = ConvertTo-SecureString "YOUR_GITHUB_PAT" -AsPlainText -Force
Add-AzDevTestLabArtifactSource `
-LabName "SkyCore-DevLab" `
-ResourceGroupName "SkyCore-DevTestLabs-RG" `
-Name "SkyCorePrivateArtifacts" `
-DisplayName "SkyCore Private Artifacts" `
-SourceType GitHub `
-Uri "https://github.com/your-org/devtest-artifacts.git" `
-FolderPath "/artifacts" `
-BranchRef "main" `
-PersonalAccessToken $pat
- Parameters are analogous to the Azure CLI commands.
ConvertTo-SecureString ... -AsPlainText -Force: Used to convert your PAT into a secure string for PowerShell.
Portal alternative: Navigate to your DevTest Lab > Under "Settings", select "Configuration and policies" > Under "Artifacts", select "Artifact sources" > Click "+ Add" > Fill in details for your Git repository (e.g., "Name", "Display Name", "Source type", "Git clone URI", "Branch", "Personal Access Token") > Click "Save". Once added, when creating a new VM, you can select the "Artifacts" tab and choose artifacts from your newly added repository.
Run this to verify:
az lab artifact-source show --lab-name "SkyCore-DevLab" --resource-group "SkyCore-DevTestLabs-RG" --name "SkyCorePrivateArtifacts" --query '{name: name, sourceType: sourceType, uri: uri}'
Get-AzDevTestLabArtifactSource -LabName "SkyCore-DevLab" -ResourceGroupName "SkyCore-DevTestLabs-RG" -Name "SkyCorePrivateArtifacts" | Select-Object Name, SourceType, Uri
When to bring in a consultant
While this Azure DevTest Labs setup guide provides a solid foundation for most SMBs, more complex scenarios can benefit significantly from expert guidance. Consider engaging SkyCore Solutions if you need assistance with:
- Integrating DevTest Labs with existing hybrid Active Directory environments or complex network topologies.
- Developing advanced custom artifacts and robust artifact repositories for specialized software deployments or CI/CD pipelines.
- Implementing comprehensive security hardening and compliance frameworks for your dev/test environments.
- Optimizing costs across multiple labs or complex subscription structures.
- Migrating existing on-premises dev/test infrastructure to Azure, ensuring minimal disruption and maximum efficiency.
Our team of Azure architects at SkyCore Solutions specializes in tailoring cloud solutions to your unique business needs, ensuring you get the most out of your Azure investment.
Book a free consultation