top of page
Search

Azure Architecture: First 15 Commands to Run the Moment You Get Access

  • 23 minutes ago
  • 8 min read

You just got Azure access mid-incident. The clock is running, the client is watching, and you need to move fast without missing anything. This is not the time to figure out what to look at — that thinking should already be done.


Fifteen commands, organized in the exact sequence you should run them, with a clear explanation of what each one tells you and what red flags to look for. Bookmark it. Run it on every Azure engagement.


All commands work in Azure Cloud Shell (Bash or PowerShell) and in any local terminal with the Azure CLI installed. No special permissions beyond read access are required for most of these — though Owner-level access is recommended for a complete picture.

Phase 1: Environmental Baseline — Know What You Are Walking Into

These first four commands take under two minutes and give you a complete picture of the environment before you touch anything else. Never skip this phase.


Command 1 — Confirm Your Access Scope

The very first thing to verify is which subscriptions you can actually see. Clients often say 'you have full access' when they mean full access to one subscription. This shows you exactly what your credentials can reach:

az account list --output table
What to look for:

how many subscriptions exist, which one is currently selected (marked as IsDefault), and whether any subscriptions are missing that your client said would be in scope. If subscriptions are missing, stop and get access before proceeding.




Command 2 — Switch to the Right Subscription

If there are multiple subscriptions, select the one relevant to your investigation before running any further commands:

az account set --subscription "Subscription Name Here"

Repeat the remaining commands for each subscription in scope. Role assignments, resources, and logs do not automatically span subscriptions — you have to check each one individually.



Command 3 — Map the Resource Groups

Resource groups are the logical folders that contain everything in a subscription. This command gives you an instant map of the environment:

az group list --output table
What to look for:

resource group names that do not match what your client described, groups in unexpected regions, or groups with generic names like 'rg-temp' or 'test-rg' that nobody can explain. Attackers who deploy their own infrastructure always create a resource group to hold it.




Command 4 — Full Resource Inventory

Every virtual machine, storage account, database, network interface, and resource in the subscription — listed in one command:

az resource list --query "[].{Name:name, RG:resourceGroup, Type:type, Location:location, State:provisioningState}" --output table

This command shows you Name, Resource Group, Type, Location, and Provisioning State. What to look for:

  • VMs with names you do not recognize — use Command 12 further below to get their sizes and running status in full detail

  • Storage accounts with generic or random-looking names — potential attacker staging areas for data exfiltration

  • Resources in regions your client does not normally operate in — the Location column makes this easy to spot

  • Resources in resource groups that were not on your client's environment map — cross-reference with Command 3 output


💡 Investigator Note: az resource list does not show creation time by default. To find when a specific resource was created, query the Activity Log — covered in detail in the Azure Logging articles in this series.
az monitor activity-log list --start-time 2026-04-01T00:00:00Z --query "[?contains(operationName.value,'write')].{Time:eventTimestamp, Name:name, Resource:resourceId, Caller:caller, Operation:operationName.value}" --output table




Phase 2: Identity and Access — Who Can Do What

Once you know what exists in the environment, you need to know who has access to it. This phase maps the full access picture and is where you will most commonly find attacker persistence.


Command 5 — Find All Owner-Level Access

Owner is the most dangerous role in Azure. An Owner can do anything — create resources, delete resources, grant access to others, and remove logs. Finding every Owner across the subscription is non-negotiable:

az role assignment list --all --query "[?roleDefinitionName=='Owner']" --output table
What to look for:

any account your client cannot immediately identify. Service principals with Owner access. Guest accounts with Owner access. Accounts that were granted Owner access around the incident timeframe.

💡 Investigator Note: Every Owner in this list is a potential persistence mechanism if the attacker gained one of these accounts. Cross-reference each one against your known user list.



Command 6 — Find All Contributor Access

Contributor is the second most powerful role — full create and delete access without the ability to grant access to others. Still critical to enumerate:

az role assignment list --all --query "[?roleDefinitionName=='Contributor']" --output table
What to look for:

same as above. Any unrecognized account or service principal with Contributor access can create and destroy resources — enough capability for most attacker objectives.



Command 7 — Full Role Assignment List

Get every role assignment across all resource groups and resources in the subscription — not just subscription-level assignments:

az role assignment list --all --output table

The --all flag is what makes this comprehensive. Without it you only see top-level assignments and miss any access granted at the resource group or individual resource level. This is the complete picture.


Command 8 — Enumerate a Specific User's Access

When you have identified a compromised account, enumerate exactly what it has access to across the entire subscription:

az role assignment list --all --assignee user@yourtenant.onmicrosoft.com --output table

Replace the email with the actual compromised account UPN. Repeat this for every subscription in scope — access granted in one subscription does not appear when querying another.



Phase 3: Threat Hunting — Looking for What the Attacker Left Behind

Beyond investigating the original compromised account, experienced attackers always leave something behind. These commands hunt for the most common persistence mechanisms used in Azure environments.


Command 9 — List All Guest Accounts

Guest accounts are the most overlooked persistence mechanism in Azure. They can be added using any external email address — a personal Gmail, Outlook, or any other external identity. They do not appear in normal HR user lists and survive password resets on internal accounts:


az ad user list --query "[?userType=='Guest'].{Name:displayName, Email:userPrincipalName, Created:createdDateTime}" --output table
What to look for:

any guest account added around the incident timeframe. Any guest account your client cannot explain. Guest accounts with Owner or Contributor role assignments from Command 5 or 6.


Command 10 — List All Service Principals

Service principals are application identities. Attackers register rogue applications and grant them permissions because service principals are invisible to most user audits, are not affected by password resets, and persist independently of any user account:


az ad sp list --all --query "[].{Name:displayName, AppId:appId, Type:servicePrincipalType}" --output table
What to look for:

service principals with generic or suspicious names, any service principal created around the incident timeframe, and cross-reference against your role assignment results to see which ones have elevated permissions.


💡 Investigator Note: A service principal with Owner or Contributor access that your client cannot explain is a critical finding. It means an attacker has persistent access that survives any user account remediation.


Command 11 — Find Recently Created User Accounts

If the attacker created a new internal user account as a backdoor, it will appear in the directory sorted by creation date. This command surfaces it:


az ad user list --query "sort_by([?createdDateTime!=null].{Name:displayName, Email:userPrincipalName, Created:createdDateTime}, &Created)" --output table

Scroll to the most recently created accounts. Any account created during or just before the incident window that your client cannot explain should be treated as an attacker-created backdoor until proven otherwise.




Phase 4: Network and Infrastructure — Understand the Attack Surface

These commands map the network and compute infrastructure, helping you understand what was exposed to the internet and what paths an attacker could have used.


Command 12 — List All Virtual Machines with Full Detail

Get a focused list of every VM with its current power state, size, and location. This is where you check for high-compute sizes used for crypto mining:


az vm list --show-details --query "[].{Name:name, Status:powerState, Size:hardwareProfile.vmSize, RG:resourceGroup, Location:location}" --output table
What to look for:

VMs in a stopped/deallocated state that should be running, high-compute sizes like Standard_NC, Standard_F, or Standard_H series that are expensive and commonly used for crypto mining, VMs in unexpected resource groups or regions, and any VM your client did not provision.




Command 13 — List All Public IP Addresses

Every public IP address in the subscription represents a potential entry point. This command lists all of them:

az network public-ip list --query "[].{Name:name, IP:ipAddress, Associated:ipConfiguration.id, RG:resourceGroup}" --output table
What to look for:

public IPs not associated with any resource (could be leftover from a deleted VM), IPs in resource groups you do not recognize, and any IP associated with a resource the client did not provision.


Command 14 — List All Storage Accounts

Storage accounts are the most common data exfiltration destination in Azure. This command lists all of them with their public access configuration:

az storage account list --query "[].{Name:name, RG:resourceGroup, PublicAccess:allowBlobPublicAccess, Location:location}" --output table
What to look for:

any storage account where PublicAccess is true — this means blobs in that account may be publicly accessible without credentials. Any storage account your client did not create. Storage accounts in unexpected resource groups.

💡 Investigator Note: A storage account with public blob access enabled is a high-priority finding in any investigation. It could be the exfiltration destination or an accidentally exposed data store. Check it immediately.

Phase 5: Management Groups — The Top of the Hierarchy

Command 15 — Check for Management Groups

The final command checks whether management groups exist and, if they do, what structure they create. Policies and permissions at this level cascade down to every subscription beneath them:

az account management-group list --output table
What to look for:

the existence of management groups at all, the hierarchy they create, and whether any unexpected policies or permissions may have been applied at this level. If management groups exist, investigate what policies are assigned to them — these policies control logging, security settings, and access across the entire tenant.



Quick Reference: All 15 Commands in One Place

Copy and paste this block into your notes at the start of every Azure engagement:

# PHASE 1: ENVIRONMENTAL BASELINE
az account list --output table
az account set --subscription "Subscription Name"
az group list --output table
az resource list --query "[].{Name:name, RG:resourceGroup, Type:type, Location:location, State:provisioningState}" --output table

# PHASE 2: IDENTITY AND ACCESS
az role assignment list --all --query "[?roleDefinitionName=='Owner']" --output table
az role assignment list --all --query "[?roleDefinitionName=='Contributor']" --output table
az role assignment list --all --output table
az role assignment list --all --assignee user@tenant.onmicrosoft.com --output table

# PHASE 3: THREAT HUNTING
az ad user list --query "[?userType=='Guest'].{Name:displayName, Email:userPrincipalName, Created:createdDateTime}" --output table
az ad sp list --all --query "[].{Name:displayName, AppId:appId, Type:servicePrincipalType}" --output table
az ad user list --query "sort_by([].{Name:displayName, Email:userPrincipalName, Created:createdDateTime}, &Created)" --output table

# PHASE 4: NETWORK AND INFRASTRUCTURE
az vm list --show-details --query "[].{Name:name, Status:powerState, Size:hardwareProfile.vmSize, RG:resourceGroup, Location:location}" --output table
az network public-ip list --query "[].{Name:name, IP:ipAddress, Associated:ipConfiguration.id, RG:resourceGroup}" --output table
az storage account list --query "[].{Name:name, RG:resourceGroup, PublicAccess:allowBlobPublicAccess, Location:location}" --output table

# PHASE 5: MANAGEMENT GROUPS
az account management-group list --output table

These 15 commands are your Azure IR starting point — not your ending point. What you find here drives where you go next. Unexpected VMs lead you to activity logs. Suspicious storage accounts lead you to StorageRead logs. Guest accounts lead you to sign-in logs. Let the findings from this playbook direct your deeper investigation.

All five log source types, how to enable them, and how to query them are covered in the Azure Logging articles in this series.


--------------------------------------------------------Dean------------------------------------------------

Special Thanks


I would like to extend my heartfelt gratitude to one of my dearest friend, a Microsoft Certified Trainer, for her invaluable assistance in creating these articles. Without her support, this would not have been possible. Thank you so much for your time, expertise, and dedication!



 
 
 

Comments


Ready to discuss:

- Schedule a call for a consultation

- Message me via "Let's Chat" for quick questions

Let's connect!

Subscribe to our newsletter

Connect With Me:

  • LinkedIn
  • Medium

© 2023 by Cyberengage. All rights reserved.

bottom of page