Public/Export-IntuneAccessData.ps1
|
function Export-IntuneAccessData { <# .SYNOPSIS Exports IntuneAccess evidence as JSON or a set of CSV files. .PARAMETER InputObject An IntuneAccess administrator result, comparison or Scoped permissions result. .PARAMETER Path A JSON file path or a CSV destination directory. .PARAMETER Format JSON writes the complete evidence object. CSV writes flattened administrator datasets. .PARAMETER Force Replaces existing output files. .EXAMPLE $access | Export-IntuneAccessData -Path './access-snapshot.json' -Format Json .EXAMPLE $access | Export-IntuneAccessData -Path './access-csv' -Format Csv #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory, ValueFromPipeline)] [object] $InputObject, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory)] [ValidateSet('Json', 'Csv')] [string] $Format, [switch] $Force ) process { $supportedTypes = @( 'IntuneAccess.AdminAccess', 'IntuneAccess.AdminAccessComparison', 'IntuneAccess.ScopedPermissionImpact' ) if (@($InputObject.PSObject.TypeNames | Where-Object { $_ -in $supportedTypes }).Count -eq 0) { throw 'InputObject must be an IntuneAccess administrator, comparison or Scoped permissions result.' } $resolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($Path) if ($Format -eq 'Json') { if ([IO.Path]::GetExtension($resolvedPath) -ne '.json') { throw 'JSON output requires a .json file extension.' } if ((Test-Path -LiteralPath $resolvedPath) -and -not $Force) { throw "The export already exists: $resolvedPath. Use -Force to replace it." } $parent = Split-Path -Parent $resolvedPath if (-not [string]::IsNullOrWhiteSpace($parent) -and -not (Test-Path -LiteralPath $parent)) { $null = New-Item -ItemType Directory -Path $parent -Force } if ($PSCmdlet.ShouldProcess($resolvedPath, 'Write IntuneAccess JSON evidence')) { $envelope = [ordered] @{ Schema = 'https://controlaltdeletetechbits.github.io/intune-access/schemas/evidence-1.0.json' SchemaVersion = '1.0' DataType = @($InputObject.PSObject.TypeNames | Where-Object { $_ -like 'IntuneAccess.*' })[0] ToolVersion = $script:IntuneAccessVersion ExportedAt = [DateTimeOffset]::Now Data = $InputObject } $envelope | ConvertTo-Json -Depth 30 | Set-Content -LiteralPath $resolvedPath -Encoding utf8NoBOM return Get-Item -LiteralPath $resolvedPath } return } if ('IntuneAccess.AdminAccess' -notin $InputObject.PSObject.TypeNames) { throw 'CSV export currently accepts only a result returned by Get-IntuneAdminAccess.' } if ((Test-Path -LiteralPath $resolvedPath) -and @(Get-ChildItem -LiteralPath $resolvedPath -File -ErrorAction SilentlyContinue).Count -gt 0 -and -not $Force) { throw "The CSV destination contains files: $resolvedPath. Use -Force to replace the IntuneAccess datasets." } if (-not (Test-Path -LiteralPath $resolvedPath)) { $null = New-Item -ItemType Directory -Path $resolvedPath -Force } if (-not $PSCmdlet.ShouldProcess($resolvedPath, 'Write IntuneAccess CSV evidence datasets')) { return } $datasets = [ordered] @{} $datasets['summary.csv'] = @([PSCustomObject] @{ UserId = $InputObject.User.Id UserPrincipalName = $InputObject.User.UserPrincipalName TenantId = $InputObject.Tenant.Id TenantName = $InputObject.Tenant.DisplayName RoleAssignmentCount = @($InputObject.RoleAssignments).Count PermissionCount = @($InputObject.EffectivePermissions).Count WarningCount = @($InputObject.Warnings).Count GeneratedAt = $InputObject.GeneratedAt ToolVersion = $InputObject.ToolVersion }) $datasets['role-assignments.csv'] = @($InputObject.RoleAssignments | ForEach-Object { [PSCustomObject] @{ Id = $_.Id Name = $_.Name Applicability = $_.Applicability RoleDefinitionId = $_.RoleDefinition.Id RoleDefinitionName = $_.RoleDefinition.DisplayName IsBuiltIn = $_.RoleDefinition.IsBuiltIn AdminGroupIds = @($_.RawIds.AdminGroupIds) -join ';' ScopeGroupIds = @($_.RawIds.ScopeGroupIds) -join ';' ScopeTagIds = @($_.RawIds.ScopeTagIds) -join ';' Permissions = @($_.Permissions) -join ';' } }) $datasets['effective-permissions.csv'] = @($InputObject.EffectivePermissions | ForEach-Object { [PSCustomObject] @{ RawAction = $_.RawAction Resource = $_.Resource Operation = $_.Operation State = $_.State SourceCount = $_.SourceCount IsDuplicate = $_.IsDuplicate GrantingAssignmentIds = @($_.GrantedBy.RoleAssignmentId) -join ';' GrantingAssignmentNames = @($_.GrantedBy.RoleAssignmentName) -join ';' } }) foreach ($groupSet in @( [PSCustomObject] @{ File = 'admin-groups.csv'; Values = @($InputObject.AdminGroups) } [PSCustomObject] @{ File = 'scope-groups.csv'; Values = @($InputObject.ScopeGroups) } [PSCustomObject] @{ File = 'scope-tags.csv'; Values = @($InputObject.ScopeTags) } )) { $datasets[$groupSet.File] = @($groupSet.Values | ForEach-Object { [PSCustomObject] @{ Id = $_.Id; DisplayName = $_.DisplayName; ResolutionState = (Get-IntuneAccessProperty $_ 'ResolutionState' 'Resolved') } }) } $datasets['warnings.csv'] = @($InputObject.Warnings | ForEach-Object { [PSCustomObject] @{ Warning = $_ } }) $datasets['evidence.csv'] = @($InputObject.Evidence | ForEach-Object { [PSCustomObject] @{ UserId = $_.UserId UserPrincipalName = $_.UserPrincipalName AdminGroupId = $_.AdminGroupId AdminGroupName = $_.AdminGroupName MembershipType = $_.MembershipType RoleAssignmentId = $_.RoleAssignmentId RoleAssignmentName = $_.RoleAssignmentName RoleDefinitionId = $_.RoleDefinitionId RoleDefinitionName = $_.RoleDefinitionName Applicability = $_.Applicability } }) $datasetHeaders = @{ 'summary.csv' = @('UserId', 'UserPrincipalName', 'TenantId', 'TenantName', 'RoleAssignmentCount', 'PermissionCount', 'WarningCount', 'GeneratedAt', 'ToolVersion') 'role-assignments.csv' = @('Id', 'Name', 'Applicability', 'RoleDefinitionId', 'RoleDefinitionName', 'IsBuiltIn', 'AdminGroupIds', 'ScopeGroupIds', 'ScopeTagIds', 'Permissions') 'effective-permissions.csv' = @('RawAction', 'Resource', 'Operation', 'State', 'SourceCount', 'IsDuplicate', 'GrantingAssignmentIds', 'GrantingAssignmentNames') 'admin-groups.csv' = @('Id', 'DisplayName', 'ResolutionState') 'scope-groups.csv' = @('Id', 'DisplayName', 'ResolutionState') 'scope-tags.csv' = @('Id', 'DisplayName', 'ResolutionState') 'warnings.csv' = @('Warning') 'evidence.csv' = @('UserId', 'UserPrincipalName', 'AdminGroupId', 'AdminGroupName', 'MembershipType', 'RoleAssignmentId', 'RoleAssignmentName', 'RoleDefinitionId', 'RoleDefinitionName', 'Applicability') } $written = [System.Collections.Generic.List[IO.FileInfo]]::new() foreach ($dataset in $datasets.GetEnumerator()) { $filePath = Join-Path $resolvedPath $dataset.Key $rows = @($dataset.Value) if ($rows.Count -gt 0) { $rows | Export-Csv -LiteralPath $filePath -NoTypeInformation -Encoding utf8 -Force } else { $emptyRow = [ordered] @{} foreach ($header in $datasetHeaders[$dataset.Key]) { $emptyRow[$header] = $null } $headerLine = @([PSCustomObject] $emptyRow | ConvertTo-Csv -NoTypeInformation)[0] Set-Content -LiteralPath $filePath -Value $headerLine -Encoding utf8NoBOM } $written.Add((Get-Item -LiteralPath $filePath)) } return $written.ToArray() } } |