ownerlens 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +209 -0
  3. package/bin/ownerlens.js +92 -0
  4. package/dist/assets/index-B9aAYpVl.css +1 -0
  5. package/dist/assets/index-BcwLk2bx.js +10 -0
  6. package/dist/index.html +13 -0
  7. package/package.json +73 -0
  8. package/src/App.tsx +18 -0
  9. package/src/components/azure/AzureComponent.test.tsx +625 -0
  10. package/src/components/azure/AzureComponent.tsx +189 -0
  11. package/src/components/azure/AzureRbacComponent.tsx +104 -0
  12. package/src/components/azure/ClosableAzureTab.tsx +42 -0
  13. package/src/components/azure/EntraPermissionsComponent.tsx +194 -0
  14. package/src/components/azure/ManagedIdentityComponent.test.tsx +324 -0
  15. package/src/components/azure/ManagedIdentityComponent.tsx +141 -0
  16. package/src/components/azure/ResourceGroupComponent.tsx +157 -0
  17. package/src/components/azure/ServicePrincipalComponent.test.tsx +457 -0
  18. package/src/components/azure/ServicePrincipalComponent.tsx +155 -0
  19. package/src/components/azure/ServicePrincipalFieldRenderers.tsx +140 -0
  20. package/src/components/azure/ZtaComponent.test.tsx +267 -0
  21. package/src/components/azure/ZtaComponent.tsx +276 -0
  22. package/src/components/azure/ZtaRemediationBadge.tsx +70 -0
  23. package/src/components/azure/api.ts +216 -0
  24. package/src/components/azure/azureReportConfig.ts +247 -0
  25. package/src/core/azure/azureRbac.ts +70 -0
  26. package/src/core/azure/entra/index.ts +1 -0
  27. package/src/core/azure/entra/managedIdentity.ts +21 -0
  28. package/src/core/azure/entra/servicePrincipal.ts +34 -0
  29. package/src/core/azure/entra/types.ts +56 -0
  30. package/src/core/azure/identityEnrichment.ts +65 -0
  31. package/src/core/azure/resources.ts +141 -0
  32. package/src/core/azure/ztaReport.ts +58 -0
  33. package/src/core/config.ts +39 -0
  34. package/src/core/ownership/OwnershipTarget.ts +32 -0
  35. package/src/core/ownership/resolveOwner.ts +5 -0
  36. package/src/core/ownership/types.ts +14 -0
  37. package/src/core/risk/types.ts +1 -0
  38. package/src/core/runtime/index.ts +1 -0
  39. package/src/core/runtime/localSnapshotFiles.ts +74 -0
  40. package/src/core/runtime/rest.ts +61 -0
  41. package/src/lib/searchFilterUtils.ts +17 -0
  42. package/src/lib/utils.ts +48 -0
  43. package/src/main.tsx +10 -0
  44. package/src/providers/azure/identities/azureIdentityTypes.ts +1 -0
  45. package/src/providers/azure/identities/buildAzureManagedIdentityAssignmentIndex.test.ts +32 -0
  46. package/src/providers/azure/identities/buildAzureManagedIdentityAssignmentIndex.ts +35 -0
  47. package/src/providers/azure/identities/userAssignedIdentityAssignments.ts +52 -0
  48. package/src/providers/azure/inputTransferObject/entra/EntraAppRoleAssignment.ts +10 -0
  49. package/src/providers/azure/inputTransferObject/entra/EntraApplication.ts +27 -0
  50. package/src/providers/azure/inputTransferObject/entra/EntraOAuth2PermissionGrant.ts +8 -0
  51. package/src/providers/azure/inputTransferObject/entra/EntraServicePrincipal.ts +43 -0
  52. package/src/providers/azure/inputTransferObject/entra/EntraSnapshot.ts +13 -0
  53. package/src/providers/azure/inputTransferObject/entra/EntraSnapshotMeta.ts +12 -0
  54. package/src/providers/azure/inputTransferObject/resources/AzureActivityLog.ts +1 -0
  55. package/src/providers/azure/inputTransferObject/resources/AzureResource.ts +1 -0
  56. package/src/providers/azure/inputTransferObject/resources/AzureResourceGroup.ts +1 -0
  57. package/src/providers/azure/inputTransferObject/resources/AzureRoleAssignment.ts +1 -0
  58. package/src/providers/azure/inputTransferObject/resources/AzureSnapshot.ts +1 -0
  59. package/src/providers/azure/inputTransferObject/resources/AzureSnapshotMeta.ts +1 -0
  60. package/src/providers/azure/inputTransferObject/resources/AzureSubscription.ts +1 -0
  61. package/src/providers/azure/inputTransferObject/resources/AzureUserAssignedManagedIdentity.ts +1 -0
  62. package/src/providers/azure/ownership/azureActivityOwnershipEvidence.ts +60 -0
  63. package/src/providers/azure/ownership/azureOwnerReportTypes.ts +13 -0
  64. package/src/providers/azure/ownership/azureOwnershipConfig.ts +21 -0
  65. package/src/providers/azure/ownership/azureOwnershipTypes.ts +46 -0
  66. package/src/providers/azure/ownership/buildAzureOwnershipReport.test.ts +99 -0
  67. package/src/providers/azure/ownership/buildAzureOwnershipReport.ts +90 -0
  68. package/src/providers/azure/ownership/buildAzureOwnershipTargets.test.ts +87 -0
  69. package/src/providers/azure/ownership/buildAzureOwnershipTargets.ts +42 -0
  70. package/src/providers/azure/ownership/resolveAzureOwner.ts +146 -0
  71. package/src/providers/azure/runtime/DisabledEvidenceStore.ts +34 -0
  72. package/src/providers/azure/runtime/EnrichmentService.ts +35 -0
  73. package/src/providers/azure/runtime/LocalReportRuntime.test.ts +2318 -0
  74. package/src/providers/azure/runtime/LocalReportRuntime.ts +302 -0
  75. package/src/providers/azure/runtime/RuntimeHost.ts +60 -0
  76. package/src/providers/azure/runtime/SnapshotImporter.ts +44 -0
  77. package/src/providers/azure/runtime/enrichment/azureIdentityEnrichment.ts +523 -0
  78. package/src/providers/azure/runtime/enrichment/azureScopeClassifier.ts +30 -0
  79. package/src/providers/azure/runtime/enrichment/evaluateAzureRoleAssignmentRisk.ts +88 -0
  80. package/src/providers/azure/runtime/entra/EntraCollectionQueryService.ts +307 -0
  81. package/src/providers/azure/runtime/entra/LocalEntraReportRuntime.ts +227 -0
  82. package/src/providers/azure/runtime/entra/appRoleAssignmentsTable.ts +52 -0
  83. package/src/providers/azure/runtime/entra/applicationsTable.ts +175 -0
  84. package/src/providers/azure/runtime/entra/entraServicePrincipalMapper.ts +63 -0
  85. package/src/providers/azure/runtime/entra/localReportRuntimeRest.ts +41 -0
  86. package/src/providers/azure/runtime/entra/oauth2PermissionGrantsTable.ts +48 -0
  87. package/src/providers/azure/runtime/entra/principalProjection.ts +173 -0
  88. package/src/providers/azure/runtime/entra/servicePrincipalsTable.ts +149 -0
  89. package/src/providers/azure/runtime/entra/snapshotMetadataTable.ts +18 -0
  90. package/src/providers/azure/runtime/entra/snapshotStore.ts +102 -0
  91. package/src/providers/azure/runtime/localReportCollections.ts +101 -0
  92. package/src/providers/azure/runtime/localReportRuntimeRest.ts +71 -0
  93. package/src/providers/azure/runtime/resources/AzureResourcesCollectionQueryService.ts +145 -0
  94. package/src/providers/azure/runtime/resources/LocalAzureResourcesReportRuntime.ts +114 -0
  95. package/src/providers/azure/runtime/resources/disabledOwnerEvidenceTable.ts +60 -0
  96. package/src/providers/azure/runtime/resources/localReportRuntimeRest.ts +81 -0
  97. package/src/providers/azure/runtime/resources/resourceGroupOwnership.ts +90 -0
  98. package/src/providers/azure/runtime/resources/snapshotMetadataTable.ts +19 -0
  99. package/src/providers/azure/runtime/resources/snapshotStore.ts +128 -0
  100. package/src/providers/azure/runtime/resources/tables.ts +441 -0
  101. package/src/providers/azure/runtime/runtimeRestQuery.ts +46 -0
  102. package/src/providers/azure/runtime/runtimeSqlSchema.ts +357 -0
  103. package/src/providers/azure/runtime/zta/Discovery.ts +141 -0
  104. package/src/providers/azure/runtime/zta/LocalZeroTrustAssessmentReportRuntime.ts +86 -0
  105. package/src/providers/azure/runtime/zta/ZeroTrustAssessmentQueryService.ts +124 -0
  106. package/src/providers/azure/runtime/zta/localReportRuntimeRest.ts +15 -0
  107. package/src/providers/azure/runtime/zta/snapshotMetadataTable.ts +77 -0
  108. package/src/providers/azure/runtime/zta/snapshotStore.ts +112 -0
  109. package/src/providers/azure/runtime/zta/tables.ts +361 -0
  110. package/src/providers/azure/runtime/zta/types.ts +7 -0
  111. package/src/providers/azure/runtime/zta/ztaReportMapper.ts +12 -0
  112. package/src/report/applyCollectionControls.ts +289 -0
  113. package/src/report/buildCollectionColumns.tsx +38 -0
  114. package/src/report/components/ConfidenceBadge.tsx +10 -0
  115. package/src/report/components/EvidenceList.test.ts +25 -0
  116. package/src/report/components/EvidenceList.tsx +52 -0
  117. package/src/report/components/GenericTable.tsx +373 -0
  118. package/src/report/components/PermissionRiskBadge.tsx +19 -0
  119. package/src/report/components/reportTableControls.test.ts +175 -0
  120. package/src/report/components/reportTableControls.tsx +483 -0
  121. package/src/report/components/ui/badge.tsx +35 -0
  122. package/src/report/components/ui/button.tsx +38 -0
  123. package/src/report/components/ui/card.tsx +23 -0
  124. package/src/report/components/ui/input.tsx +15 -0
  125. package/src/report/components/ui/table.tsx +44 -0
  126. package/src/report/components/ui/tabs.tsx +29 -0
  127. package/src/report/export/csv.ts +34 -0
  128. package/src/report/ownerManualPrecheck.test.ts +137 -0
  129. package/src/report/ownerManualPrecheck.ts +132 -0
  130. package/src/report/reportArchitecture.test.ts +125 -0
  131. package/src/report/reportTypes.ts +54 -0
  132. package/src/report/reportValueRenderers.tsx +54 -0
  133. package/src/report/runtimeCollectionQuery.ts +23 -0
  134. package/src/report/types.ts +14 -0
  135. package/src/styles.css +43 -0
  136. package/tools/README.md +108 -0
  137. package/tools/azure-activity-check.ps1 +164 -0
  138. package/tools/collect-azure.ps1 +54 -0
  139. package/tools/collect-entra.ps1 +47 -0
  140. package/tools/collect-scripts.test.ts +22 -0
  141. package/tools/prepare-entra-snapshot.ps1 +403 -0
  142. package/tools/prepare-entra-snapshot.test.ts +14 -0
  143. package/tools/prepare-resource-snapshot.ps1 +345 -0
  144. package/vite.config.ts +23 -0
@@ -0,0 +1,108 @@
1
+ # OwnerLens Tools
2
+
3
+ PowerShell scripts in this directory create the JSON snapshot files consumed by the OwnerLens app.
4
+
5
+ ## Core Files
6
+
7
+ - `prepare-resource-snapshot.ps1` creates the Azure resource snapshot used by the app. It exports subscriptions, resource groups, resources, managed identities, role assignments, and optional Azure Monitor activity logs.
8
+ - `prepare-entra-snapshot.ps1` creates the Entra snapshot used by the app. It exports service principals, application registrations, and groups so ownership and identity relationships can be resolved.
9
+
10
+ Run these commands from the repository root so the default output paths write into `.\data`.
11
+
12
+ ## Prerequisites
13
+
14
+ - PowerShell 7 or Windows PowerShell
15
+ - Azure PowerShell modules:
16
+
17
+ ```powershell
18
+ Install-Module Az -Scope CurrentUser
19
+ Install-Module Az.ManagedServiceIdentity -Scope CurrentUser
20
+ Install-Module Microsoft.Graph -Scope CurrentUser
21
+ ```
22
+
23
+ If `Invoke-AzRestMethod` is missing, update `Az.Accounts`:
24
+
25
+ ```powershell
26
+ Update-Module Az.Accounts
27
+ ```
28
+
29
+ ## Sign In
30
+
31
+ Sign in to Azure before creating the resource snapshot:
32
+
33
+ ```powershell
34
+ Connect-AzAccount
35
+ ```
36
+
37
+ Sign in to Microsoft Graph before creating the Entra snapshot:
38
+
39
+ ```powershell
40
+ Connect-MgGraph -TenantId "<tenant-id>" -Scopes "Application.Read.All","Group.Read.All","Directory.Read.All"
41
+ ```
42
+
43
+ ## Create Snapshots
44
+
45
+ Create the Azure resource snapshot:
46
+
47
+ ```powershell
48
+ .\tools\collect-azure.ps1
49
+ ```
50
+
51
+ By default this writes `.\data\snapshot.json`, using the current Azure subscription and the last 90 days of activity logs.
52
+
53
+ Common resource snapshot options:
54
+
55
+ ```powershell
56
+ .\tools\collect-azure.ps1 -SubscriptionIds "sub-id-1,sub-id-2"
57
+ .\tools\collect-azure.ps1 -OutputPath ".\data\snapshot-prod.json"
58
+ .\tools\collect-azure.ps1 -ActivityDays 30 -MaxActivityRecords 5000
59
+ .\tools\collect-azure.ps1 -SkipAuditLogsExport
60
+ .\tools\collect-azure.ps1 -ExpandResourceProperties
61
+ ```
62
+
63
+ Resource property expansion is disabled by default because OwnerLens reads the standard resource fields plus identity data from the resource list response. Use `-ExpandResourceProperties` only when debugging or when you need Azure's additional expanded metadata in a raw snapshot.
64
+
65
+ Create the Entra snapshot:
66
+
67
+ ```powershell
68
+ .\tools\collect-entra.ps1
69
+ ```
70
+
71
+ By default this writes `.\data\entra-snapshot.json`.
72
+
73
+ Common Entra snapshot option:
74
+
75
+ ```powershell
76
+ .\tools\collect-entra.ps1 -TenantId "<tenant-id>"
77
+ .\tools\collect-entra.ps1 -OutputPath ".\data\entra-snapshot-prod.json"
78
+ ```
79
+
80
+ After both files exist, start the app with `npm run dev` and refresh the browser.
81
+
82
+ The same collectors are available through npm scripts:
83
+
84
+ ```bash
85
+ npm run collect:azure -- -SubscriptionIds "sub-id-1,sub-id-2"
86
+ npm run collect:entra -- -TenantId "<tenant-id>"
87
+ ```
88
+
89
+ After publishing the package, the equivalent `npx` commands are:
90
+
91
+ ```bash
92
+ npx ownerlens collect:azure -SubscriptionIds "sub-id-1,sub-id-2"
93
+ npx ownerlens collect:entra -TenantId "<tenant-id>"
94
+ ```
95
+
96
+ ## Scripts
97
+
98
+ - `collect-azure.ps1` signs in when needed, then calls `prepare-resource-snapshot.ps1`.
99
+ - `collect-entra.ps1` signs in when needed, then calls `prepare-entra-snapshot.ps1`.
100
+ - `prepare-resource-snapshot.ps1` exports Azure subscriptions, resource groups, resources, user-assigned managed identities, role assignments, and optional Azure Monitor activity logs.
101
+ - `prepare-entra-snapshot.ps1` exports Entra service principals, application registrations, and groups.
102
+ - `azure-activity-check.ps1` is a helper loaded by `prepare-resource-snapshot.ps1`; it is not usually run directly.
103
+
104
+ ## Notes
105
+
106
+ - Snapshot files can contain tenant, subscription, resource, identity, application registration, group, credential metadata, and activity-log metadata. Review them before sharing.
107
+ - The app discovers files in `.\data` whose names end with `snapshot.json`, such as `snapshot.json`, `entra-snapshot.json`, or `snapshot-prod.json`.
108
+ - If scripts fail with a missing connection error, run the relevant sign-in command again and retry.
@@ -0,0 +1,164 @@
1
+ if (-not $script:AzureActivityLogCache) {
2
+ $script:AzureActivityLogCache = @{}
3
+ }
4
+
5
+ function Get-ActivityLogValue {
6
+ param(
7
+ [object]$Object,
8
+ [string]$PropertyName
9
+ )
10
+
11
+ if (-not $Object) {
12
+ return $null
13
+ }
14
+
15
+ $property = $Object.PSObject.Properties[$PropertyName]
16
+ if (-not $property) {
17
+ return $null
18
+ }
19
+
20
+ return $property.Value
21
+ }
22
+
23
+ function Get-ActivityLogField {
24
+ param(
25
+ [object]$Entry,
26
+ [string]$PropertyName
27
+ )
28
+
29
+ $value = Get-ActivityLogValue $Entry $PropertyName
30
+ if ($null -ne $value) {
31
+ return $value
32
+ }
33
+
34
+ $properties = Get-ActivityLogValue $Entry "properties"
35
+ return Get-ActivityLogValue $properties $PropertyName
36
+ }
37
+
38
+ function Get-ActivityLogClaim {
39
+ param(
40
+ [object]$Claims,
41
+ [string[]]$ClaimNames
42
+ )
43
+
44
+ if (-not $Claims) {
45
+ return $null
46
+ }
47
+
48
+ foreach ($claimName in $ClaimNames) {
49
+ $value = Get-ActivityLogValue $Claims $claimName
50
+ if ($null -ne $value -and -not [string]::IsNullOrWhiteSpace([string]$value)) {
51
+ return $value
52
+ }
53
+ }
54
+
55
+ return $null
56
+ }
57
+
58
+ function Get-ActivityLogCacheKey {
59
+ param(
60
+ [string]$SubscriptionId,
61
+ [datetime]$StartTime,
62
+ [int]$MaxRecord
63
+ )
64
+
65
+ $normalizedStartTime = $StartTime.ToUniversalTime().ToString("o")
66
+ return "$SubscriptionId|$normalizedStartTime|$MaxRecord"
67
+ }
68
+
69
+ function Get-AzureMonitorActivityLogs {
70
+ param(
71
+ [string]$SubscriptionId,
72
+ [datetime]$StartTime,
73
+ [int]$MaxRecord
74
+ )
75
+
76
+ $logs = [System.Collections.Generic.List[object]]::new()
77
+ if ($MaxRecord -le 0) {
78
+ return $logs
79
+ }
80
+
81
+ $cacheKey = Get-ActivityLogCacheKey -SubscriptionId $SubscriptionId -StartTime $StartTime -MaxRecord $MaxRecord
82
+ if ($script:AzureActivityLogCache.ContainsKey($cacheKey)) {
83
+ return $script:AzureActivityLogCache[$cacheKey]
84
+ }
85
+
86
+ $endTime = Get-Date
87
+ $filter = "eventTimestamp ge '$($StartTime.ToUniversalTime().ToString("o"))' and eventTimestamp le '$($endTime.ToUniversalTime().ToString("o"))'"
88
+ $encodedFilter = [Uri]::EscapeDataString($filter)
89
+ $requestPath = "/subscriptions/$SubscriptionId/providers/microsoft.insights/eventtypes/management/values?api-version=2015-04-01&`$filter=$encodedFilter"
90
+
91
+ while ($requestPath -and $logs.Count -lt $MaxRecord) {
92
+ if ($requestPath -match "^https?://") {
93
+ $response = Invoke-AzRestMethod -Method GET -Uri $requestPath
94
+ } else {
95
+ $response = Invoke-AzRestMethod -Method GET -Path $requestPath
96
+ }
97
+
98
+ $content = $response.Content | ConvertFrom-Json
99
+ foreach ($entry in @($content.value)) {
100
+ if ($logs.Count -ge $MaxRecord) {
101
+ break
102
+ }
103
+
104
+ $operationName = Get-ActivityLogField $entry "operationName"
105
+ $status = Get-ActivityLogField $entry "status"
106
+ $subStatus = Get-ActivityLogField $entry "subStatus"
107
+ $category = Get-ActivityLogField $entry "category"
108
+ $resourceProviderName = Get-ActivityLogField $entry "resourceProviderName"
109
+ $resourceType = Get-ActivityLogField $entry "resourceType"
110
+ $authorization = Get-ActivityLogField $entry "authorization"
111
+ $claims = Get-ActivityLogField $entry "claims"
112
+
113
+ $logs.Add([pscustomobject]@{
114
+ eventTimestamp = Get-ActivityLogField $entry "eventTimestamp"
115
+ submissionTimestamp = Get-ActivityLogField $entry "submissionTimestamp"
116
+ caller = Get-ActivityLogField $entry "caller"
117
+ callerUserPrincipalName = Get-ActivityLogClaim $claims @(
118
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
119
+ "unique_name",
120
+ "upn",
121
+ "preferred_username"
122
+ )
123
+ callerName = Get-ActivityLogClaim $claims @(
124
+ "name",
125
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"
126
+ )
127
+ callerEmail = Get-ActivityLogClaim $claims @(
128
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
129
+ "email",
130
+ "emails",
131
+ "preferred_username"
132
+ )
133
+ callerObjectId = Get-ActivityLogClaim $claims @(
134
+ "http://schemas.microsoft.com/identity/claims/objectidentifier",
135
+ "oid",
136
+ "objectidentifier"
137
+ )
138
+ callerIdentityType = Get-ActivityLogClaim $claims @("idtyp")
139
+ callerAppId = Get-ActivityLogClaim $claims @("appid", "azp")
140
+ callerIpAddress = Get-ActivityLogClaim $claims @("ipaddr")
141
+ callerTenantId = Get-ActivityLogClaim $claims @(
142
+ "http://schemas.microsoft.com/identity/claims/tenantid",
143
+ "tid"
144
+ )
145
+ operationName = Get-ActivityLogValue $operationName "localizedValue"
146
+ operationNameValue = Get-ActivityLogValue $operationName "value"
147
+ status = Get-ActivityLogValue $status "localizedValue"
148
+ subStatus = Get-ActivityLogValue $subStatus "localizedValue"
149
+ category = Get-ActivityLogValue $category "localizedValue"
150
+ resourceGroupName = Get-ActivityLogField $entry "resourceGroupName"
151
+ resourceId = Get-ActivityLogField $entry "resourceId"
152
+ resourceProviderName = Get-ActivityLogValue $resourceProviderName "localizedValue"
153
+ resourceType = Get-ActivityLogValue $resourceType "localizedValue"
154
+ authorizationAction = Get-ActivityLogValue $authorization "action"
155
+ authorizationScope = Get-ActivityLogValue $authorization "scope"
156
+ }) | Out-Null
157
+ }
158
+
159
+ $requestPath = Get-ActivityLogValue $content "nextLink"
160
+ }
161
+
162
+ $script:AzureActivityLogCache[$cacheKey] = $logs
163
+ return $script:AzureActivityLogCache[$cacheKey]
164
+ }
@@ -0,0 +1,54 @@
1
+ param(
2
+ [string]$OutputDir = ".\data",
3
+ [string]$OutputPath = "",
4
+ [int]$ActivityDays = 90,
5
+ [int]$MaxActivityRecords = 10000,
6
+ [switch]$SkipAuditLogsExport,
7
+ [string]$SubscriptionIds = "",
8
+ [switch]$ExpandResourceProperties,
9
+ [switch]$SkipLogin
10
+ )
11
+
12
+ $ErrorActionPreference = "Stop"
13
+
14
+ function Write-CollectProgress {
15
+ param([string]$Message)
16
+
17
+ $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
18
+ Write-Host "[$timestamp] $Message"
19
+ }
20
+
21
+ $resolvedOutputPath = $OutputPath
22
+ if ([string]::IsNullOrWhiteSpace($resolvedOutputPath)) {
23
+ $resolvedOutputPath = Join-Path $OutputDir "snapshot.json"
24
+ }
25
+
26
+ if (-not (Get-Command Get-AzContext -ErrorAction SilentlyContinue)) {
27
+ throw "Az PowerShell module missing. Install: Install-Module Az -Scope CurrentUser"
28
+ }
29
+
30
+ $context = Get-AzContext
31
+ if (-not $SkipLogin -and -not $context) {
32
+ Write-CollectProgress "Azure context not found. Starting Connect-AzAccount."
33
+ Connect-AzAccount | Out-Null
34
+ }
35
+
36
+ Write-CollectProgress "Collecting Azure resource snapshot"
37
+ Write-CollectProgress "Output path: $resolvedOutputPath"
38
+
39
+ $prepareParams = @{
40
+ OutputPath = $resolvedOutputPath
41
+ ActivityDays = $ActivityDays
42
+ MaxActivityRecords = $MaxActivityRecords
43
+ SubscriptionIds = $SubscriptionIds
44
+ }
45
+
46
+ if ($SkipAuditLogsExport) {
47
+ $prepareParams.SkipAuditLogsExport = $true
48
+ }
49
+
50
+ if ($ExpandResourceProperties) {
51
+ $prepareParams.ExpandResourceProperties = $true
52
+ }
53
+
54
+ & "$PSScriptRoot\prepare-resource-snapshot.ps1" @prepareParams
@@ -0,0 +1,47 @@
1
+ param(
2
+ [string]$OutputDir = ".\data",
3
+ [string]$OutputPath = "",
4
+ [string]$TenantId = "",
5
+ [string[]]$Scopes = @("Application.Read.All", "Group.Read.All", "Directory.Read.All"),
6
+ [switch]$SkipLogin
7
+ )
8
+
9
+ $ErrorActionPreference = "Stop"
10
+
11
+ function Write-CollectProgress {
12
+ param([string]$Message)
13
+
14
+ $timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
15
+ Write-Host "[$timestamp] $Message"
16
+ }
17
+
18
+ $resolvedOutputPath = $OutputPath
19
+ if ([string]::IsNullOrWhiteSpace($resolvedOutputPath)) {
20
+ $resolvedOutputPath = Join-Path $OutputDir "entra-snapshot.json"
21
+ }
22
+
23
+ try {
24
+ Import-Module Microsoft.Graph.Authentication -ErrorAction Stop
25
+ } catch {
26
+ throw "Microsoft Graph PowerShell module missing: Microsoft.Graph.Authentication. Install: Install-Module Microsoft.Graph -Scope CurrentUser"
27
+ }
28
+
29
+ $context = Get-MgContext
30
+ if (-not $SkipLogin -and -not $context) {
31
+ Write-CollectProgress "Microsoft Graph context not found. Starting Connect-MgGraph."
32
+
33
+ $connectParams = @{
34
+ Scopes = $Scopes
35
+ }
36
+
37
+ if (-not [string]::IsNullOrWhiteSpace($TenantId)) {
38
+ $connectParams.TenantId = $TenantId
39
+ }
40
+
41
+ Connect-MgGraph @connectParams | Out-Null
42
+ }
43
+
44
+ Write-CollectProgress "Collecting Microsoft Entra snapshot"
45
+ Write-CollectProgress "Output path: $resolvedOutputPath"
46
+
47
+ & "$PSScriptRoot\prepare-entra-snapshot.ps1" -OutputPath $resolvedOutputPath
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ const packageJson = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf8"));
5
+ const cli = readFileSync(join(process.cwd(), "bin/ownerlens.js"), "utf8");
6
+ const collectEntra = readFileSync(join(process.cwd(), "tools/collect-entra.ps1"), "utf8");
7
+ const collectAzure = readFileSync(join(process.cwd(), "tools/collect-azure.ps1"), "utf8");
8
+
9
+ test("package exposes OwnerLens collect commands through the npm bin", () => {
10
+ expect(packageJson.bin.ownerlens).toBe("./bin/ownerlens.js");
11
+ expect(packageJson.scripts["collect:entra"]).toBe("node ./bin/ownerlens.js collect:entra");
12
+ expect(packageJson.scripts["collect:azure"]).toBe("node ./bin/ownerlens.js collect:azure");
13
+ expect(cli).toContain('["collect:entra", "collect-entra.ps1"]');
14
+ expect(cli).toContain('["collect:azure", "collect-azure.ps1"]');
15
+ });
16
+
17
+ test("collect wrappers delegate to the snapshot exporters used by the runtime", () => {
18
+ expect(collectEntra).toContain("prepare-entra-snapshot.ps1");
19
+ expect(collectEntra).toContain('Join-Path $OutputDir "entra-snapshot.json"');
20
+ expect(collectAzure).toContain("prepare-resource-snapshot.ps1");
21
+ expect(collectAzure).toContain('Join-Path $OutputDir "snapshot.json"');
22
+ });