This is an updated version Damon Coursey's technique for querying the Activity logs. This follows how activity logs are saved in Log Analytics workspace. It also uses the max_arg aggregator to work for multiple VMs instead of one, and provides some additional fields for uptime in hours or days.
Note you will need to export the Activity logs for each subscription into a Log Analytics workspace, per this guide: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log?tabs=powershell#send-to-log-analytics-workspace
Configure DaysOfLogsToCheck, and Max_Uptime. Note this will only help you track events for as long as Activity logs are stored in your workspace (default 90 days).
// Uses Activity logs to report VMs with assumed uptime longer than X days (days since last 'start' action).
// Activity Log export must be configured for a relevant subscription, to save activity logs to a log workspace.
// Ref: https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log?tabs=powershell#send-to-log-analytics-workspace
let DaysOfLogsToCheck = ago(30d);
let Max_Uptime = totimespan(3d); // Specify max uptime here, in hours (h) or days (d).
let MaxUptimeDate = ago(Max_Uptime);
let Now = now();
// At a high level: we pull all VM start OR stop(deallocate) events, then summarize to pull the most recent event for each host, then filter only the 'start' events.
// We do this to avoid double-counting a system that was stopped and started multiple times in the period.
// Some includes and excludes are also added per variables above, for tailored reporting on hosts and groups.
AzureActivity
| where TimeGenerated > DaysOfLogsToCheck
and ResourceProviderValue == "MICROSOFT.COMPUTE"
and Properties_d['activityStatusValue'] == "Start"
and Properties_d['message'] in ("Microsoft.Compute/virtualMachines/start/action", "Microsoft.Compute/virtualMachines/deallocate/action")
and TimeGenerated <= MaxUptimeDate
| extend
Uptime_Days = datetime_diff('day', Now, TimeGenerated),
Uptime_Hours = datetime_diff('hour', Now, TimeGenerated),
MaxUptime=Max_Uptime
| project
TimeGenerated,
ResourceProviderValue,
ResourceGroup=Properties_d['resourceGroup'],
Resource=Properties_d['resource'],
Action=Properties_d['message'],
Uptime_Days,
Uptime_Hours,
MaxUptime
| summarize arg_max(TimeGenerated, *) by tostring(Resource) //Pull only the latest event for this resource, whether stop or start
| where Action == "Microsoft.Compute/virtualMachines/start/action" // Now only show the "start" actions