
When you work in Azure, there are many moments where you do not need a full script, a full automation account, or a polished tool. You just need to query something quickly, validate a configuration, find risk, export data, or clean up technical debt. That is where PowerShell shines. With the Az PowerShell modules and Azure Resource Graph, a single well-built one-liner can save time, reduce manual portal work, and help Azure administrators get answers fast.
Below are 24 advanced but practical Azure PowerShell one-liners you can use as inspiration or directly adapt for your environment.
Prerequisites: Most examples assume you are already connected with Connect-AzAccount and have the needed Az modules installed. Some examples also use Azure Resource Graph, so you may need Search-AzGraph.
1. Export all orphaned managed disks across all subscriptions
Find unattached managed disks that are still costing money.
1
Search-AzGraph -Query "Resources | where type =~ 'microsoft.compute/disks' | where managedBy == '' or isnull(managedBy) | project subscriptionId, resourceGroup, name, location, diskSizeGB=tostring(properties.diskSizeGB), sku=tostring(sku.name), timeCreated=tostring(properties.timeCreated)" -First 5000 | Export-Csv .\orphaned-managed-disks.csv -NoTypeInformation
2. Export unused public IP addresses
Useful for cleanup, cost reduction, and exposure review.
1
Search-AzGraph -Query "Resources | where type =~ 'microsoft.network/publicipaddresses' | where isnull(properties.ipConfiguration) and isnull(properties.natGateway) | project subscriptionId, resourceGroup, name, location, sku=tostring(sku.name), ipAddress=tostring(properties.ipAddress), allocation=tostring(properties.publicIPAllocationMethod)" -First 5000 | Export-Csv .\unused-public-ips.csv -NoTypeInformation
3. Find NSG rules exposing risky management ports to the internet
Checks for broad inbound access to common admin ports.
1
Search-AzGraph -Query "Resources | where type =~ 'microsoft.network/networksecuritygroups' | mv-expand rule=properties.securityRules | where tostring(rule.properties.direction) == 'Inbound' and tostring(rule.properties.access) == 'Allow' and (tostring(rule.properties.sourceAddressPrefix) == '*' or tostring(rule.properties.sourceAddressPrefix) == '0.0.0.0/0' or tostring(rule.properties.sourceAddressPrefix) == 'Internet') and (tostring(rule.properties.destinationPortRange) in ('22','3389','5985','5986') or tostring(rule.properties.destinationPortRanges) has_any ('22','3389','5985','5986')) | project subscriptionId, resourceGroup, nsg=name, ruleName=tostring(rule.name), priority=tostring(rule.properties.priority), port=tostring(rule.properties.destinationPortRange), source=tostring(rule.properties.sourceAddressPrefix)" -First 5000 | Export-Csv .\nsg-risky-admin-ports.csv -NoTypeInformation
4. Export resources missing required tags
Very handy for governance reviews.
1
Search-AzGraph -Query "Resources | where isnull(tags['Owner']) or isnull(tags['Environment']) or isnull(tags['CostCenter']) | project subscriptionId, resourceGroup, name, type, location, Owner=tostring(tags.Owner), Environment=tostring(tags.Environment), CostCenter=tostring(tags.CostCenter)" -First 5000 | Export-Csv .\resources-missing-tags.csv -NoTypeInformation
5. Find VMs that are stopped but still allocated
These often continue generating compute cost.
1
Get-AzVM -Status | Where-Object { $_.PowerState -eq 'VM stopped' } | Select-Object Name, ResourceGroupName, Location, @{N='VMSize';E={$_.HardwareProfile.VmSize}} | Export-Csv .\stopped-but-allocated-vms.csv -NoTypeInformation
6. Export direct privileged RBAC assignments
Filters for the most sensitive built-in roles.
1
Get-AzRoleAssignment | Where-Object { $_.RoleDefinitionName -in @('Owner','Contributor','User Access Administrator') -and $_.Scope -match '^/subscriptions/' } | Select-Object DisplayName, SignInName, ObjectType, RoleDefinitionName, Scope | Export-Csv .\privileged-rbac-assignments.csv -NoTypeInformation
7. Find identities with Owner access across multiple subscriptions
Helpful during privilege reviews.
1
Get-AzRoleAssignment | Where-Object { $_.RoleDefinitionName -eq 'Owner' -and $_.Scope -match '^/subscriptions/[^/]+$' } | Group-Object ObjectId | Where-Object { $_.Count -gt 1 } | ForEach-Object { $_.Group | Select-Object DisplayName, SignInName, ObjectType, Scope, RoleDefinitionName } | Export-Csv .\owner-on-multiple-subscriptions.csv -NoTypeInformation
8. Find app registrations with secrets expiring in the next 30 days
Very useful for identity hygiene.
1
Get-AzADApplication | ForEach-Object { $app=$_; $_.PasswordCredentials | Where-Object { $_.EndDateTime -lt (Get-Date).AddDays(30) } | Select-Object @{N='AppDisplayName';E={$app.DisplayName}}, @{N='AppId';E={$app.AppId}}, StartDateTime, EndDateTime } | Export-Csv .\app-secrets-expiring-soon.csv -NoTypeInformation
9. Find applications with certificates expiring in the next 30 days
The certificate equivalent of the previous check.
1
Get-AzADApplication | ForEach-Object { $app=$_; $_.KeyCredentials | Where-Object { $_.EndDateTime -lt (Get-Date).AddDays(30) } | Select-Object @{N='AppDisplayName';E={$app.DisplayName}}, @{N='AppId';E={$app.AppId}}, Type, StartDateTime, EndDateTime } | Export-Csv .\app-certificates-expiring-soon.csv -NoTypeInformation
10. Export storage accounts with public access enabled
Good for security posture reviews.
1
Search-AzGraph -Query "Resources | where type =~ 'microsoft.storage/storageaccounts' | project subscriptionId, resourceGroup, name, location, publicNetworkAccess=tostring(properties.publicNetworkAccess), allowBlobPublicAccess=tostring(properties.allowBlobPublicAccess), minimumTls=tostring(properties.minimumTlsVersion)" -First 5000 | Where-Object { $_.publicNetworkAccess -eq 'Enabled' -or $_.allowBlobPublicAccess -eq 'true' } | Export-Csv .\storage-public-exposure.csv -NoTypeInformation
11. Find Key Vaults without purge protection enabled
A simple but very important validation.
| Search-AzGraph -Query “Resources | where type =~ ‘microsoft.keyvault/vaults’ | project subscriptionId, resourceGroup, name, location, enablePurgeProtection=tostring(properties.enablePurgeProtection), enableSoftDelete=tostring(properties.enableSoftDelete), publicNetworkAccess=tostring(properties.publicNetworkAccess)” -First 5000 | Where-Object { $_.enablePurgeProtection -ne ‘true’ } | Export-Csv .\keyvaults-without-purge-protection.csv -NoTypeInformation |
12. Find Azure VMs without backup protection
A very practical operational report.
1
Get-AzVM | Where-Object { -not (Get-AzRecoveryServicesVault | ForEach-Object { Set-AzRecoveryServicesVaultContext -Vault $_; Get-AzRecoveryServicesBackupItem -WorkloadType AzureVM -ErrorAction SilentlyContinue } | Where-Object { $_.ContainerName -match $_.Name }) } | Select-Object Name, ResourceGroupName, Location | Export-Csv .\vms-without-backup.csv -NoTypeInformation
13. Find SQL servers with permissive firewall rules
Looks for wide-open or overly broad network access.
1
Get-AzSqlServer | ForEach-Object { $s=$_; Get-AzSqlServerFirewallRule -ResourceGroupName $s.ResourceGroupName -ServerName $s.ServerName | Where-Object { $_.StartIpAddress -eq '0.0.0.0' -or $_.StartIpAddress -eq '0.0.0.0' -and $_.EndIpAddress -eq '255.255.255.255' } | Select-Object @{N='ServerName';E={$s.ServerName}}, ResourceGroupName, FirewallRuleName, StartIpAddress, EndIpAddress } | Export-Csv .\sql-open-firewall-rules.csv -NoTypeInformation
14. Find production resource groups without delete locks
Useful for governance hardening.
1
Get-AzResourceGroup | Where-Object { $_.ResourceGroupName -match 'prod|production' } | Where-Object { -not (Get-AzResourceLock -ResourceGroupName $_.ResourceGroupName -AtScope 2 -ErrorAction SilentlyContinue) } | Select-Object ResourceGroupName, Location | Export-Csv .\prod-rgs-without-delete-lock.csv -NoTypeInformation
15. Export failed VM extensions
Very useful after migrations or large deployments.
1
Get-AzVM -Status | ForEach-Object { $vm=$_; $_.Extensions | Where-Object { $_.Statuses.Code -match 'ProvisioningState/failed' } | Select-Object @{N='VMName';E={$vm.Name}}, @{N='ResourceGroupName';E={$vm.ResourceGroupName}}, Name, ProvisioningState, Type, Publisher } | Export-Csv .\failed-vm-extensions.csv -NoTypeInformation
16. Build a cleanup report for unused Azure resources
Combines a few common cleanup candidates.
1
@((Search-AzGraph -Query "Resources | where type =~ 'microsoft.network/networkinterfaces' | where isnull(properties.virtualMachine) | project type, subscriptionId, resourceGroup, name, location" -First 5000),(Search-AzGraph -Query "Resources | where type =~ 'microsoft.compute/snapshots' | project type, subscriptionId, resourceGroup, name, location" -First 5000),(Search-AzGraph -Query "resourcecontainers | where type =~ 'microsoft.resources/subscriptions/resourcegroups' | project type, subscriptionId, resourceGroup=name, location" -First 5000 | Where-Object { -not (Get-AzResource -ResourceGroupName $_.resourceGroup -ErrorAction SilentlyContinue) })) | ForEach-Object { $_ } | Export-Csv .\azure-cleanup-report.csv -NoTypeInformation
17. Find VNet peerings that are not fully connected
Useful in hub-and-spoke environments.
1
Get-AzVirtualNetwork | ForEach-Object { $vnet=$_; $_.VirtualNetworkPeerings | Where-Object { $_.PeeringState -ne 'Connected' } | Select-Object @{N='VNet';E={$vnet.Name}}, @{N='ResourceGroupName';E={$vnet.ResourceGroupName}}, Name, PeeringState, RemoteVirtualNetwork } | Export-Csv .\broken-vnet-peerings.csv -NoTypeInformation
18. Export private endpoints with DNS review context
Helpful for troubleshooting private connectivity.
1
Get-AzPrivateEndpoint | Select-Object Name, ResourceGroupName, Location, @{N='SubnetId';E={$_.Subnet.Id}}, @{N='PrivateLinkServiceConnections';E={($_.PrivateLinkServiceConnections.PrivateLinkServiceId -join ';')}} | Export-Csv .\private-endpoints-review.csv -NoTypeInformation
19. Find PaaS resources that still allow public network access
Cross-service exposure review in a single pass.
1
Search-AzGraph -Query "Resources | where type in~ ('microsoft.storage/storageaccounts','microsoft.sql/servers','microsoft.keyvault/vaults','microsoft.web/sites') | project subscriptionId, resourceGroup, name, type, location, publicNetworkAccess=tostring(properties.publicNetworkAccess)" -First 5000 | Where-Object { $_.publicNetworkAccess -eq 'Enabled' } | Export-Csv .\public-network-access-enabled.csv -NoTypeInformation
20. Export Azure Policy exemptions
Great for governance tracking and audits.
1
Get-AzPolicyExemption | Select-Object Name, DisplayName, ExemptionCategory, Scope, PolicyAssignmentId, ExpiresOn, Metadata | Export-Csv .\policy-exemptions.csv -NoTypeInformation
21. Summarize non-compliant Azure Policy states by assignment
Useful for dashboard-style reporting.
1
Get-AzPolicyState -Filter "ComplianceState eq 'NonCompliant'" | Group-Object PolicyAssignmentName | Select-Object Name, Count | Sort-Object Count -Descending | Export-Csv .\policy-noncompliance-summary.csv -NoTypeInformation
22. Find resources deployed outside approved Azure regions
Useful for governance and residency checks.
1
$allowed=@('eastus2','centralus','westeurope'); Search-AzGraph -Query "Resources | project subscriptionId, resourceGroup, name, type, location" -First 5000 | Where-Object { $_.location -and $_.location.ToLower() -notin $allowed } | Export-Csv .\resources-outside-approved-regions.csv -NoTypeInformation
23. Find resources using disallowed VM sizes from a CSV file
Good example of mixing one-liners with input files.
1
$bad=(Import-Csv .\disallowed-vm-sizes.csv).Name; Get-AzVM | Where-Object { $_.HardwareProfile.VmSize -in $bad } | Select-Object Name, ResourceGroupName, Location, @{N='VMSize';E={$_.HardwareProfile.VmSize}} | Export-Csv .\vms-with-disallowed-sizes.csv -NoTypeInformation
Example input file:
1
2
3
4
Name
Standard_A1_v2
Standard_D2_v2
Standard_DS1_v2
24. Export actionable Azure Advisor recommendations
A simple way to turn Advisor into a backlog.
1
Get-AzAdvisorRecommendation | Where-Object { $_.Category -in @('Cost','HighAvailability','Security','OperationalExcellence','Performance') } | Select-Object Category, Impact, ShortDescription, ResourceId, RecommendationTypeId | Export-Csv .\advisor-recommendations.csv -NoTypeInformation
Conclusion
PowerShell is still one of the fastest ways to interrogate Azure, validate governance, find misconfigurations, and export useful operational data without building full solutions every time. A good one-liner can save minutes during troubleshooting, hours during reviews, and sometimes even real money by surfacing waste or risk early.
The nice thing is that these are not just “cool tricks.” They are the kind of commands Azure administrators can adapt into daily checks, scheduled jobs, governance reports, or incident response shortcuts.
Thanks for reading and keep clouding around.
Vukasin Terzic