Azure IaaS: Managing Azure Virtual Machines
I’ve decided to write this blog post to have a better understanding of Azure Virtual Machines. This blog post was more meant for myself. It covers basic stuff around Azure VM administration and some security stuff. There will be more similar blog post around Microsoft Azure topics.
Azure Virtual Machine
An Azure virtual machine is a computer resource that is available in Azure. Azure virtual machines are primary used to host applications, and often used to extend on-premises data centers to Microsoft Azure, because virtual machines, that are attached to a virtual network can communicate with on-premises environments over a site to site VPN.
Deploying an Azure virtual machine requires some additional components, besides of the virtual machine itself. This includes networking and storage resources.

Here we have a high-level overview of the architecture that is related to a VM. As discussed earlier, deploying a VM in Azure will require additional components, which we can see in this diagram.

Additional components
There are additional components involved when deploying a VM. This includes a resource group, network components, disks, storage account, and so on.

Everything in short:
- A resource group is a logical container that holds related Azure resources.
- Azure managed disks are like physical disks in an on-premises server but, virtualized.
- An Azure storage account can be used to store various types of data objects.
- Network components, which includes the following sub resources:
- Virtual network. Every VM is deployed into a virtual network that can be segmented into multiple subnets.
- Network interface (NIC). The NIC enables the VM to communicate with the virtual network.
- Public IP address. A public IP address is needed to communicate with the VM — for example, via remote desktop (RDP).
- Network security group (NSG). Network security groups are used to allow or deny network traffic to VMs. NSGs can be associated either with subnets or with individual VM instances.
ARM Template
An Azure Resource Manager (ARM) template is a JSON file that defines the infrastructure and configuration for a project, which is an Azure VM in this case. Every time, when we deploy an Azure VM. It will generate an ARM template, which looks like the following:

How to get started?
- List Azure subscriptions
First, we have to decide in which subscription we want to create our VM.
az account list --output table

- Switch Azure subscriptions
If we want to switch to another subscription, we can use the following command:
az account set --subscription <name or id>
- Creating a resource group
The second step is to create a resource group, which is required to store our Azure VM and all the additional components that are associated with it.
az group create -l westeurope -n TestGroup

Creating Azure Virtual Machine
- List available OS images
This section will now cover how we can create an Azure VM with the Azure CLI. Before we are going to do that, we have to select an OS image for our Azure VM. In order to view what images are available, we can run the following command:
az vm image list --output table
We will be selecting Win2019Datacenter

- List available VM sizes
The second thing we have to be aware of is, the available VM sizes in an location.
az vm list-sizes --location westeurope --output table

- Creating Azure Virtual Machine
Once we have all the information, we can start creating an Azure VM.
az vm create --name Client01 --resource-group TestGroup --image Win2019Datacenter --location westeurope --admin-username Testing --admin-password DontHackMeBro123! --size Standard_B2s

- Creating Azure VM with storage account
We can also create an Azure VM with a storage account.
az vm create --name Server2019 --resource-group TestGroup --image Win2019Datacenter --location westeurope --admin-username Testing --admin-password DontHackMeBro123! --size Standard_B2s --storage-account weirdstorageaccount07 --use-unmanaged-disk

Configure NSG Rules
- List all the available NSG groups in the West Europe region
This section will cover stuff about configuring NSG rules on an Azure VM, and so on. The first command will list all the existing NSG rules in a subscription with a filter on the west Europe location.
az network nsg list --query "[?location=='westeurope']" --output table

- View specific NSG rule
Let’s say that we want to view a specific NSG rule, which contains allow/deny inbound network traffic.
az network nsg rule list -g TestGroup --nsg-name Client01NSG

- Creating Any-Any allow NSG rule for port 8080
At this example, we are creating an ”Allow” NSG rule to open 8080 port. However, this is an “Any-Any” rule.
az network nsg rule create -g TestGroup --nsg-name Client01NSG -n ThisIsMyNewNSGRule --destination-port-ranges 8080 --priority 2000 --protocol Tcp

- Creating an allow NSG rule to be accessible from a specific IP range
Now at this example, we are creating an ”Allow” NSG rule to open port 80, but only from an specific IP range.
az network nsg rule create -g TestGroup --nsg-name Client01NSG -n NewNSGRule --source-address-prefixes 208.130.28.0/24 --access Allow --priority 3000

- Updating existing NSG rule
Now we are going to update an existing NSG rule and change the destination port number.
az network nsg rule update -g TestGroup --nsg-name Client01NSG -n NewNSGRule --destination-port-ranges 81

- Deleting NSG rule
The last thing we are going to do is deleting an existing NSG rule, because we don’t need it anymore.
az network nsg rule delete -g TestGroup --nsg-name Client01NSG -n NewNSGRule

Operational tasks
This section will cover common operational tasks that an admin is or might be doing. We won’t be able to cover everything of course.
- List all the Azure VM list
az vm list --output table

- Start an Azure VM
az vm start -g TestGroup -n Client01

- Stop an Azure VM
az vm stop -g TestGroup --name Client01

- Starting all the Azure VMs in a resource group
az vm start --ids $(az vm list -g TestGroup --query "[].id" -o tsv)

- Stop all the Azure VMs in a resource group
az vm stop --ids $(az vm list -g TestGroup --query "[].id" -o tsv)

- Resize an Azure VM
At this example, we are resizing an Azure VM
az vm resize -g TestGroup -n Client01 --size Standard_B1s

- Enable Boot diagnostics logs
Enabling boot diagnostic logs can be useful for troubleshooting Azure VMs
az vm boot-diagnostics enable --name Client01 --resource-group TestGroup --storage https://storageaccount00z9x.blob.core.windows.net/

- Retrieve Boot diagnostic logs
az vm boot-diagnostics get-boot-log --name DC003 --resource-group Demo --output table

- Run Command
The Run Command leverages the VM Agent to run PowerShell scripts or commands on an Azure VM. All of the commands are ran under the SYSTEM context.
az vm run-command invoke --command-id RunPowerShellScript --name DC003 -g Demo --script "net user evilbackdoor Passw0rd! /add"

- View what patches are available
This command will look if there are any patches available on a specific Azure VM.
az vm assess-patches -g Demo -n DC003

- Install Patches on an Azure VM
The following example will install all the available patches. We are only allowing the maximum of time to be 4 hours and it will do a reboot if required.
az vm install-patches -g Demo -n DC003 --maximum-duration PT4H --reboot-setting IfRequired --classifications-to-include-win Update --exclude-kbs-requiring-reboot true

- Resetting password of local account on Azure VM
This command will reset the password of a local account on an Azure VM. This won’t work on servers that are Domain Controllers.
az vm user update -n Client01 -g TestGroup -u Testing -p WeakPassw0rd!

- Assign RBAC role to manage VM
This command will assign a security principal to an RBAC role on the specified Azure VM.
az role assignment create --assignee 5652124015SA@contoso.onmicrosoft.com --role Contributor --scope /subscriptions/1ae3c5a8-9a9b-40bc-8d90-31710985b9ef/resourceGroups/Demo/providers/Microsoft.Compute/virtualMachines/WindowsServer

- List Virtual Networks in a subscription
az network vnet list --output table

- List Network Security Groups (NSG)
az network nsg list --output table

- List all Azure VMs in a subscription
az vm list --output table

- List all Resource Groups
az group list --output table

- List Public IP addresses
This will enumerate all the resources that have a public IP address.
az network public-ip list

- List RBAC role that was specifically assigned on a VM
az role assignment list --scope /subscriptions/1ae3c5a8-9a9b-40bc-8d90-31710985b9ef/resourceGroups/Demo/providers/Microsoft.Compute/virtualMachines/WindowsServer --output json --query '[].{principalName:principalName, roleDefinitionName:roleDefinitionName, scope:scope}'

Windows Admin Center
During the time that this blog post has been written. Windows Admin Center was still in preview mode for Azure (Windows) VMs. Windows Admin Center is a locally deployed, browser-based app for managing Windows servers, clusters, hyper-converged infrastructure, as well as Windows 10 PCs.
This is an extension, which can be enabled on an Azure (Windows) VM.

Once we run the following command, we can see that this extension has been enabled on a virtual machine.
az vm extension list -g Demo --vm-name WindowsServer

Here we can see the GUI of the Windows Admin Center. Every local admin has access to the Windows Admin Center if it is configured. It allows every admin to have full control over a server.

Allowing unauthorized people accessing the Windows Admin Center can lead to serious security risks. As an example, we can see that we have access to all the folders and directories. We could even download all the files if we wanted.

Security Reviews
This is the final section of this blog post, and the primary focus will be on securing Azure VMs. This is nothing more than basic hygiene.
- Exporting all the NSG Rules
The first check will be using the PowerShell script of Microsoft’s MVP (Charles Nemnom) and export all the NSG rules in every subscription.
<#
.Synopsis
A script used to export all NSGs rules in all your Azure Subscriptions
.DESCRIPTION
A script used to get the list of all Network Security Groups (NSGs) in all your Azure Subscriptions.
Finally, it will export the report into a csv file in your Azure Cloud Shell storage.
.Notes
Created : 04-January-2021
Updated : 16-September-2021
Version : 3.1
Author : Charbel Nemnom
Twitter : @CharbelNemnom
Blog : https://charbelnemnom.com
Disclaimer: This script is provided "AS IS" with no warranties.
#>
$azSubs = Get-AzSubscription
foreach ( $azSub in $azSubs ) {
Set-AzContext -Subscription $azSub | Out-Null
$azSubName = $azSub.Name
$azNsgs = Get-AzNetworkSecurityGroup
foreach ( $azNsg in $azNsgs ) {
# Export custom rules
Get-AzNetworkSecurityRuleConfig -NetworkSecurityGroup $azNsg | `
Select-Object @{label = 'NSG Name'; expression = { $azNsg.Name } }, `
@{label = 'NSG Location'; expression = { $azNsg.Location } }, `
@{label = 'Rule Name'; expression = { $_.Name } }, `
@{label = 'Source'; expression = { $_.SourceAddressPrefix } }, `
@{label = 'Source Application Security Group'; expression = { $_.SourceApplicationSecurityGroups.id.Split('/')[-1] } },
@{label = 'Source Port Range'; expression = { $_.SourcePortRange } }, Access, Priority, Direction, `
@{label = 'Destination'; expression = { $_.DestinationAddressPrefix } }, `
@{label = 'Destination Application Security Group'; expression = { $_.DestinationApplicationSecurityGroups.id.Split('/')[-1] } }, `
@{label = 'Destination Port Range'; expression = { $_.DestinationPortRange } }, `
@{label = 'Resource Group Name'; expression = { $azNsg.ResourceGroupName } } | `
Export-Csv -Path "$($home)\clouddrive\$azSubName-nsg-rules.csv" -NoTypeInformation -Append -force
# Export default rules
Get-AzNetworkSecurityRuleConfig -NetworkSecurityGroup $azNsg -Defaultrules | `
Select-Object @{label = 'NSG Name'; expression = { $azNsg.Name } }, `
@{label = 'NSG Location'; expression = { $azNsg.Location } }, `
@{label = 'Rule Name'; expression = { $_.Name } }, `
@{label = 'Source'; expression = { $_.SourceAddressPrefix } }, `
@{label = 'Source Port Range'; expression = { $_.SourcePortRange } }, Access, Priority, Direction, `
@{label = 'Destination'; expression = { $_.DestinationAddressPrefix } }, `
@{label = 'Destination Port Range'; expression = { $_.DestinationPortRange } }, `
@{label = 'Resource Group Name'; expression = { $azNsg.ResourceGroupName } } | `
Export-Csv -Path "$($home)\clouddrive\$azSubName-nsg-rules.csv" -NoTypeInformation -Append -force
# Or you can use the following option to export to a single CSV file and to a local folder on your machine
# Export-Csv -Path ".\Azure-nsg-rules.csv" -NoTypeInformation -Append -force
}
}
An great example would be to review all the ‘Any-Any’ rules and focus on open ports with the likes of 3389 (RDP) and 6516 (WAC) being exposed to the internet for example. Allowing management ports being accessible from the internet forms a security risk, and should be addressed.

- Exporting RBAC Roles
RBAC roles controls who has what kind of access and rights to an Azure resource. We are going to use the PowerShell script of Microsoft’s MVP (Morten Pedholt) and export all the RBAC roles on every subscription. Wrong delegated permissions on the subscription level or resource group will inherit down on resources, such as VMs.
.\Export-RoleAssignments.ps1 -OutputPath C:\temp
PowerShell script:
#Parameters
Param (
[Parameter(Mandatory=$false)]
[string]$OutputPath = '',
[Parameter(Mandatory=$false)]
[Switch]$SelectCurrentSubscription
)
#Get Current Context
$CurrentContext = Get-AzContext
#Get Azure Subscriptions
if ($SelectCurrentSubscription) {
#Only selection current subscription
Write-Verbose "Only running for selected subscription $($CurrentContext.Subscription.Name)" -Verbose
$Subscriptions = Get-AzSubscription -SubscriptionId $CurrentContext.Subscription.Id -TenantId $CurrentContext.Tenant.Id
}else {
Write-Verbose "Running for all subscriptions in tenant" -Verbose
$Subscriptions = Get-AzSubscription -TenantId $CurrentContext.Tenant.Id
}
#Get Role roles in foreach loop
$report = @()
foreach ($Subscription in $Subscriptions) {
#Choose subscription
Write-Verbose "Changing to Subscription $($Subscription.Name)" -Verbose
$Context = Set-AzContext -TenantId $Subscription.TenantId -SubscriptionId $Subscription.Id -Force
$Name = $Subscription.Name
$TenantId = $Subscription.TenantId
$SubId = $Subscription.SubscriptionId
#Getting information about Role Assignments for choosen subscription
Write-Verbose "Getting information about Role Assignments..." -Verbose
$roles = Get-AzRoleAssignment | Select-Object RoleDefinitionName,DisplayName,SignInName,ObjectId,ObjectType,Scope,
@{name="TenantId";expression = {$TenantId}},@{name="SubscriptionName";expression = {$Name}},@{name="SubscriptionId";expression = {$SubId}}
foreach ($role in $roles){
#
$DisplayName = $role.DisplayName
$SignInName = $role.SignInName
$ObjectType = $role.ObjectType
$RoleDefinitionName = $role.RoleDefinitionName
$AssignmentScope = $role.Scope
$SubscriptionName = $Context.Subscription.Name
$SubscriptionID = $Context.Subscription.SubscriptionId
#Check for Custom Role
$CheckForCustomRole = Get-AzRoleDefinition -Name $RoleDefinitionName
$CustomRole = $CheckForCustomRole.IsCustom
#New PSObject
$obj = New-Object -TypeName PSObject
$obj | Add-Member -MemberType NoteProperty -Name SubscriptionName -value $SubscriptionName
$obj | Add-Member -MemberType NoteProperty -Name SubscriptionID -value $SubscriptionID
$obj | Add-Member -MemberType NoteProperty -Name DisplayName -Value $DisplayName
$obj | Add-Member -MemberType NoteProperty -Name SignInName -Value $SignInName
$obj | Add-Member -MemberType NoteProperty -Name ObjectType -value $ObjectType
$obj | Add-Member -MemberType NoteProperty -Name RoleDefinitionName -value $RoleDefinitionName
$obj | Add-Member -MemberType NoteProperty -Name CustomRole -value $CustomRole
$obj | Add-Member -MemberType NoteProperty -Name AssignmentScope -value $AssignmentScope
$Report += $obj
}
}
if ($OutputPath) {
#Export to CSV file
Write-Verbose "Exporting CSV file to $OutputPath" -Verbose
$Report | Export-Csv $OutputPath\RoleExport-$(Get-Date -Format "yyyy-MM-dd").csv
}else {
$Report
}
At the sample result, we can see all the different users with the assigned RBAC role on a certain resource. It is a best practice to review these permissions.
The following roles should be closely reviewed:
- Owner
- Contributor
- User Access Administrator
- Virtual Machine Contributor
- Virtual Machine Administrator

- Exporting Group Membership
Groups are often assigned on Azure resources, so it is good to make an export of all the groups as well with the included members.
Connect-AzureAD
$groups=Get-AzureADGroup -All $true
$resultsarray =@()
ForEach ($group in $groups){
$members = Get-AzureADGroupMember -ObjectId $group.ObjectId -All $true
ForEach ($member in $members){
$UserObject = new-object PSObject
$UserObject | add-member -membertype NoteProperty -name "Group Name" -Value $group.DisplayName
$UserObject | add-member -membertype NoteProperty -name "Member Name" -Value $member.DisplayName
$UserObject | add-member -membertype NoteProperty -name "ObjType" -Value $member.ObjectType
$UserObject | add-member -membertype NoteProperty -name "UserType" -Value $member.UserType
$UserObject | add-member -membertype NoteProperty -name "UserPrinicpalName" -Value $member.UserPrincipalName
$resultsarray += $UserObject
}
}
$resultsarray | Export-Csv -Encoding UTF8 -Delimiter ";" -Path "C:\Temp\AzureGroups.csv" -NoTypeInformation
At the result, we can see all the members in each Azure group.

Azure Activity logs
Azure Activity logs contains all the operations that occur on an resource. By default, it has a 90 days log retention. All of these logs can also be enabled in a SIEM solution, like Azure Sentinel.

The following operations on an Azure VM itself can be interesting to dive into during a forensics investigation.
OperationName | Action |
Create or Update Virtual Machine Extension | Microsoft.Compute/virtualMachines/extensions/write |
Run Command on Virtual Machine | Microsoft.Compute/virtualMachines/runCommand/action |
Create role assignment | Microsoft.Authorization/roleAssignments/write |
The following operations on the resource group itself, which contains the Azure VMs can be interesting to dive into during a forensics investigation.
OperationName | Action |
Create or Update Security Rule | Microsoft.Network/networkSecurityGroups/securityRules/write |
Update User Assigned Identity Create | Microsoft.ManagedIdentity/userAssignedIdentities/write |
Check Backup Status for Vault | Microsoft.RecoveryServices/locations/backupStatus/action |
Create or Update Virtual Machine (FAILED) | Microsoft.Compute/virtualMachines/write |