Posts 24 Advanced but Useful Azure PowerShell One-Liners for Administrators
Post
Cancel

24 Advanced but Useful Azure PowerShell One-Liners for Administrators

Essential KQL Queries for Azure with and without Log Analytics

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 “Resourceswhere type =~ ‘microsoft.keyvault/vaults’project subscriptionId, resourceGroup, name, location, enablePurgeProtection=tostring(properties.enablePurgeProtection), enableSoftDelete=tostring(properties.enableSoftDelete), publicNetworkAccess=tostring(properties.publicNetworkAccess)” -First 5000Where-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

This post is licensed under CC BY 4.0