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.

This image has an empty alt attribute; its file name is image-39.png

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 --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
  • 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 --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.

A script used to export all NSGs rules in all your Azure Subscriptions

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.

Created   : 04-January-2021
Updated   : 16-September-2021
Version   : 3.1
Author    : Charbel Nemnom
Twitter   : @CharbelNemnom
Blog      :
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 = { $'/')[-1] } },
            @{label = 'Source Port Range'; expression = { $_.SourcePortRange } }, Access, Priority, Direction, `
            @{label = 'Destination'; expression = { $_.DestinationAddressPrefix } }, `
            @{label = 'Destination Application Security Group'; expression = { $'/')[-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:

Param (
    [string]$OutputPath = '',
#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 {

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.

$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.

Create or Update Virtual Machine ExtensionMicrosoft.Compute/virtualMachines/extensions/write
Run Command on Virtual MachineMicrosoft.Compute/virtualMachines/runCommand/action
Create role assignmentMicrosoft.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.

Create or Update Security RuleMicrosoft.Network/networkSecurityGroups/securityRules/write
Update User Assigned Identity CreateMicrosoft.ManagedIdentity/userAssignedIdentities/write
Check Backup Status for VaultMicrosoft.RecoveryServices/locations/backupStatus/action
Create or Update Virtual Machine (FAILED)Microsoft.Compute/virtualMachines/write

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s