Scripts/SimplifiedPurviewConfigAnalyserReport.ps1
# Optimized script to collect relevant data and output it into a concise JSON file and then Covert it to Excel. # Function to establish a connection to the Microsoft Compliance Center # Ensure required modules are installed and imported function Write-Log { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [Switch]$IsError = $false, [Parameter(Mandatory = $false)] [Switch]$IsWarn = $false, [Parameter(Mandatory = $false)] [Switch]$IsInfo = $false, [Parameter(Mandatory = $false)] [Switch]$MachineInfo = $false, [Parameter(Mandatory = $false)] [Switch]$StopInfo = $false, [Parameter(Mandatory = $false)] [string]$ErrorMessage, [Parameter(Mandatory = $false)] [System.Collections.ArrayList]$WarnMessage, [Parameter(Mandatory = $false)] [string]$InfoMessage, [Parameter(Mandatory = $false)] [string]$StackTraceInfo, [String]$LogFile ) if ($MachineInfo) { $ComputerInfoObj = Get-ComputerInfo $CompName = $ComputerInfoObj.CsName $OSName = $ComputerInfoObj.OsName $OSVersion = $ComputerInfoObj.OsVersion $PowerShellVersion = $PSVersionTable.PSVersion try { "********************************************************************************************" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "Logging Started" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "Start time: $(Get-Date)" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "Computer Name: $CompName" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "Operating System Name: $OSName" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "Operating System Version: $OSVersion" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "PowerShell Version: $PowerShellVersion" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "********************************************************************************************" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue } catch { Write-Host "$(Get-Date) The local machine information cannot be logged." -ForegroundColor:Yellow } } if ($StopInfo) { try { "********************************************************************************************" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "Logging Ended" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "End time: $(Get-Date)" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "********************************************************************************************" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue if ($($global:ErrorOccurred) -eq $true) { Write-Host "Warning:$(Get-Date) The report generated may have reduced information due to errors in running the tool. These errors may occur due to multiple reasons. Please refer documentation for more details." -ForegroundColor:Yellow } } catch { Write-Host "$(Get-Date) The finishing time information cannot be logged." -ForegroundColor:Yellow } } #Error if ($IsError) { if ($($global:ErrorOccurred) -eq $false) { $global:ErrorOccurred = $true } $Log_content = "$(Get-Date) ERROR: $ErrorMessage" try { $Log_content | Out-File $LogFile -Append -ErrorAction:SilentlyContinue "TRACE: $StackTraceInfo" | Out-File $LogFile -Append -ErrorAction:SilentlyContinue } catch { Write-Host "$(Get-Date) An error event cannot be logged." -ForegroundColor:Yellow } } #Warning if ($IsWarn) { foreach ($Warnmsg in $WarnMessage) { $Log_content = "$(Get-Date) WARN: $Warnmsg" try { $Log_content | Out-File $LogFile -Append -ErrorAction:SilentlyContinue } catch { Write-Host "$(Get-Date) A warning event cannot be logged." -ForegroundColor:Yellow } } } #General if ($IsInfo) { $Log_content = "$(Get-Date) INFO: $InfoMessage" try { $Log_content | Out-File $LogFile -Append -ErrorAction:SilentlyContinue } catch { Write-Host "$(Get-Date) A general event cannot be logged." -ForegroundColor:Yellow } } } function EnsureModule { param ( [Parameter(Mandatory = $true)] [string]$ModuleName ) if (-not (Get-Module -ListAvailable -Name $ModuleName)) { $InfoMessage = "Module $ModuleName is not installed. Installing..." Write-Host "$(Get-Date) $InfoMessage" Install-Module -Name $ModuleName -Force -ErrorAction Stop } Import-Module -Name $ModuleName -ErrorAction Stop $InfoMessage = "Module $ModuleName is imported successfully." Write-Host "$(Get-Date) $InfoMessage" } # Import required modules EnsureModule -ModuleName ImportExcel EnsureModule -ModuleName ExchangeOnlineManagement $LogDirectory = "$env:LOCALAPPDATA\Microsoft\PurviewConfigAnalyser\Logs" if (-not (Test-Path -Path $LogDirectory)) { New-Item -ItemType Directory -Path $LogDirectory -Force | Out-Null } $FileName = "PurviewConfigAnalyser-$(Get-Date -Format 'yyyyMMddHHmmss').log" $LogFile = "$LogDirectory\$FileName" $InfoMessage = "Log File Path : $LogFile" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage $InfoMessage = "Main Directory Path : $env:LOCALAPPDATA\Microsoft\PurviewConfigAnalyser" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage function EnsureComplianceCenterConnection { param ( [string]$UserPrincipalName ) try { # Check if the session is still valid $Session = Get-PSSession | Where-Object { $_.Name -eq "ComplianceCenter" } if ($Session -and $Session.State -eq "Opened") { Write-Host "Compliance Center session is active." -ForegroundColor Green } else { Write-Host "Compliance Center session is expired or not established. Reconnecting..." -ForegroundColor Yellow Connect-IPPSSession -UserPrincipalName $UserPrincipalName -ErrorAction Stop Write-Host "Reconnected to Compliance Center successfully!" -ForegroundColor Green } } catch { Write-Host "Error reconnecting to Compliance Center: $_" -ForegroundColor Red exit 1 } } function Connect-ToComplianceCenter { Write-Host "Establishing connection to Microsoft Compliance Center..." try { # Prompt for credentials $userName = Read-Host -Prompt 'Enter your User Principal Name (UPN)' #EnsureComplianceCenterConnection -UserPrincipalName $userName # Connect to the Compliance Center using UserPrincipalName Connect-IPPSSession -UserPrincipalName $userName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue $InfoMessage = "Connection established successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage } catch { Write-Host "Error establishing connection: $_" exit 1 } } function Convert-ObjectForJson { param ( [Parameter(ValueFromPipeline = $true)] $InputObject ) process { if ($null -eq $InputObject) { return $null } if ($InputObject -is [hashtable] -or $InputObject -is [System.Collections.IDictionary]) { $newHash = [ordered]@{} foreach ($key in $InputObject.Keys) { $stringKey = [string]$key $newHash[$stringKey] = Convert-ObjectForJson -InputObject $InputObject[$key] } return [PSCustomObject]$newHash } if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]) { $newList = [System.Collections.ArrayList]::new() foreach ($item in $InputObject) { $null = $newList.Add((Convert-ObjectForJson -InputObject $item)) } return $newList } if ($InputObject -is [PSCustomObject]) { $newObj = [ordered]@{} foreach ($prop in $InputObject.PSObject.Properties) { $propName = $prop.Name $propValue = $prop.Value # Targeted fix for arrays that get truncated if ($propName -in @("Labels", "ScopedLabels") -and $propValue -is [System.Collections.IEnumerable] -and $propValue -isnot [string]) { $newObj[$propName] = @($propValue | ForEach-Object { "$_" }) } # Preserve LabelActions, which are often pre-formatted JSON strings elseif ($propName -in @("LabelActions", "Settings", "LocaleSettings") -and $propValue -is [System.Collections.IEnumerable] -and $propValue -isnot [string]) { $newObj[$propName] = $propValue # Preserve original structure } else { $newObj[$propName] = Convert-ObjectForJson -InputObject $propValue } } return [PSCustomObject]$newObj } # For all other types, return as is return $InputObject } } # Function to filter and optimize data function Optimize-Data { param ( [Parameter(Mandatory = $true)] [array]$Data, [array]$Fields ) $OptimizedData = @() foreach ($Item in $Data) { $FilteredItem = @{} foreach ($Field in $Fields) { if ($Item.PSObject.Properties[$Field]) { $FilteredItem[$Field] = $Item.$Field } } $OptimizedData += [PSCustomObject]$FilteredItem } return $OptimizedData } # Function: Get-InformationProtectionSettings Function Get-InformationProtectionSettings { Param( $Collection, [string]$LogFile ) try { [System.Collections.ArrayList]$WarnMessage = @() $Collection["GetLabel"] = Get-Label -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage $Collection["GetLabelPolicy"] = Get-LabelPolicy -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage # Add Published attribute to GetLabel data if ($Collection["GetLabel"] -ne "Error" -and $Collection["GetLabelPolicy"] -ne "Error") { # Extract all ImmutableIds from ScopedLabels in GetLabelPolicy # Extract all ImmutableIds from ScopedLabels in GetLabelPolicy and deduplicate $PublishedImmutableIds = @( $Collection["GetLabelPolicy"] | ForEach-Object { $_.ScopedLabels } | Where-Object { $_ -ne $null } | ForEach-Object { [string]$_ } | Select-Object -Unique ) # Debug: Log the deduplicated ImmutableIds Write-Host "Published ImmutableIds: $($PublishedImmutableIds -join ', ')" -ForegroundColor Cyan # Add Published attribute to each label in GetLabel foreach ($Label in $Collection["GetLabel"]) { Write-Host "Processing Label: $($Label.DisplayName)" -ForegroundColor Yellow # Ensure ImmutableId is a string $ImmutableId = [string]$Label.ImmutableId Write-Host "ImmutableId (as string): $ImmutableId" -ForegroundColor Cyan # Add the Published property dynamically if it doesn't exist if (-not ($Label.PSObject.Properties | Where-Object { $_.Name -eq "Published" })) { $Label | Add-Member -MemberType NoteProperty -Name Published -Value $false -Force } # Check if ImmutableId is in PublishedImmutableIds if ($null -ne $ImmutableId -and $ImmutableId -ne "") { if ($PublishedImmutableIds -contains $ImmutableId) { Write-Host "Match Found: $ImmutableId is in PublishedImmutableIds" -ForegroundColor Green $Label.Published = $true } else { Write-Host "No Match: $ImmutableId is NOT in PublishedImmutableIds" -ForegroundColor Red $Label.Published = $false } } else { Write-Host "Warning: Label $($Label.DisplayName) has a null or empty ImmutableId." -ForegroundColor Red $Label.Published = $false } } } $InfoMessage = "Get-InformationProtectionSettings - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage Write-Log -IsWarn -WarnMessage $WarnMessage -LogFile $LogFile -ErrorAction:SilentlyContinue } catch { $Collection["GetLabel"] = "Error" $Collection["GetLabelPolicy"] = "Error" Write-Host "Error:$(Get-Date) There was an issue in fetching Information Protection information. Please try running the tool again after some time." -ForegroundColor:Red $ErrorMessage = $_.ToString() $StackTraceInfo = $_.ScriptStackTrace Write-Log -IsError -ErrorMessage $ErrorMessage -StackTraceInfo $StackTraceInfo -LogFile $LogFile -ErrorAction:SilentlyContinue } try { [System.Collections.ArrayList]$WarnMessage = @() $Collection["GetAutoSensitivityLabelPolicy"] = Get-AutoSensitivityLabelPolicy -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage -ForceValidate $InfoMessage = "GetAutoSensitivityLabelPolicy - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage Write-Log -IsWarn -WarnMessage $WarnMessage -LogFile $LogFile -ErrorAction:SilentlyContinue } catch { $Collection["GetAutoSensitivityLabelPolicy"] = "Error" Write-Host "Error:$(Get-Date) There was an issue in fetching AutoSensitivity Label Policy information. Please try running the tool again after some time." -ForegroundColor:Red $ErrorMessage = $_.ToString() $StackTraceInfo = $_.ScriptStackTrace Write-Log -IsError -ErrorMessage $ErrorMessage -StackTraceInfo $StackTraceInfo -LogFile $LogFile -ErrorAction:SilentlyContinue } Return $Collection } # Function to fetch Tenant ID, Tenant Name, and Current Timestamp function Get-TenantDetails { param ( $Collection, [string]$LogFile ) try { # Fetch all tenant details using Get-ConnectionInformation [System.Collections.ArrayList]$WarnMessage = @() $OrgConfig = Get-ConnectionInformation -ErrorAction Stop # Debug: Check the output of Get-ConnectionInformation #Write-Host "OrgConfig Output:" -ForegroundColor Yellow #Write-Host ($OrgConfig | Out-String) # Filter records where TokenStatus is Active $ActiveRecords = $OrgConfig | Where-Object { $_.TokenStatus -eq "Active" } # Check if there are any active records if ($ActiveRecords.Count -eq 0) { Write-Host "No active records found in OrgConfig!" -ForegroundColor Red $Collection["TenantDetails"] = "No Active Records" } else { # Select required attributes from the active records $SelectedRecord = $ActiveRecords | Select-Object -First 1 TenantID, Organization, State, UserPrincipalName # Create a collection with Tenant ID, Tenant Name, and Timestamp $Collection["TenantDetails"] = @{ TenantId = $SelectedRecord.TenantID Organization = $SelectedRecord.Organization Timestamp = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss") State = $SelectedRecord.State UserPrincipalName = $SelectedRecord.UserPrincipalName } $InfoMessage = "Tenant Details fetched successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage } Write-Log -IsWarn -WarnMessage $WarnMessage -LogFile $LogFile -ErrorAction SilentlyContinue } catch { $Collection["TenantDetails"] = "Error" Write-Host "Error: $(Get-Date) There was an issue in fetching Tenant Details. Please try running the tool again after some time." -ForegroundColor Red $ErrorMessage = $_.ToString() $StackTraceInfo = $_.ScriptStackTrace Write-Log -IsError -ErrorMessage $ErrorMessage -StackTraceInfo $StackTraceInfo -LogFile $LogFile -ErrorAction:SilentlyContinue } return $Collection } # Get DLP settings Function Get-DataLossPreventionSettings { Param( $Collection, [string]$LogFile ) try { [System.Collections.ArrayList]$WarnMessage = @() $Collection["GetDlpComplianceRule"] = Get-DlpComplianceRule -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage $Collection["GetDLPCustomSIT"] = Get-DlpSensitiveInformationType -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage | Where-Object { $_.Publisher -ne "Microsoft Corporation" } $Collection["GetDlpCompliancePolicy"] = Get-DlpCompliancePolicy -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage -ForceValidate $InfoMessage = "GetDlpCompliancePolicy - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage Write-Log -IsWarn -WarnMessage $WarnMessage -LogFile $LogFile -ErrorAction:SilentlyContinue } catch { $Collection["GetDlpComplianceRule"] = "Error" $Collection["GetDLPCustomSIT"] = "Error" $Collection["GetDlpCompliancePolicy"] = "Error" Write-Host "Error:$(Get-Date) There was an issue in fetching Data Loss Prevention information. Please try running the tool again after some time." -ForegroundColor:Red $ErrorMessage = $_.ToString() $StackTraceInfo = $_.ScriptStackTrace Write-Log -IsError -ErrorMessage $ErrorMessage -StackTraceInfo $StackTraceInfo -LogFile $LogFile -ErrorAction:SilentlyContinue } Return $Collection } Function Get-RetentionCompliance { Param( $Collection, [string]$LogFile ) try { [System.Collections.ArrayList]$WarnMessage = @() $Collection["GetRetentionCompliancePolicy"] = Get-RetentionCompliancePolicy -DistributionDetail -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage $InfoMessage = "GetRetentionCompliancePolicy - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage $Collection["GetRetentionComplianceRule"] = Get-RetentionComplianceRule -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage $Collection["GetComplianceTag"] = Get-ComplianceTag -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage $InfoMessage = "GetComplianceTag - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage Write-Log -IsWarn -WarnMessage $WarnMessage -LogFile $LogFile -ErrorAction:SilentlyContinue } catch { $Collection["GetRetentionCompliancePolicy"] = "Error" $Collection["GetRetentionComplianceRule"] = "Error" $Collection["GetComplianceTag"] = "Error" Write-Host "Error:$(Get-Date) There was an issue in fetching Retention Compliance information. Please try running the tool again after some time." -ForegroundColor:Red $ErrorMessage = $_.ToString() $StackTraceInfo = $_.ScriptStackTrace Write-Log -IsError -ErrorMessage $ErrorMessage -StackTraceInfo $StackTraceInfo -LogFile $LogFile -ErrorAction:SilentlyContinue } Return $Collection } # Function: Get-InsiderRiskManagementSettings function Get-InsiderRiskManagementSettings { param ( [hashtable]$Collection ) try { $Collection["InsiderRiskManagement"] = Get-InsiderRiskPolicy -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage $InfoMessage = "InsiderRiskManagement - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage #$IRMPolicies = Get-InsiderRiskPolicy -ErrorAction:SilentlyContinue -WarningVariable +WarnMessage #$Collection["InsiderRiskManagement"] = Optimize-Data -Data $IRMPolicies -Fields @("Name", "Description", "Enabled", "Scope", "Conditions", "Actions") Write-Log -IsWarn -WarnMessage $WarnMessage -LogFile $LogFile -ErrorAction:SilentlyContinue } catch { $Collection["InsiderRiskManagement"]= "Error" Write-Host "Error:$(Get-Date) There was an issue in fetching Insider Risk Management information. Please try running the tool again after some time." -ForegroundColor:Red $ErrorMessage = $_.ToString() $StackTraceInfo = $_.ScriptStackTrace Write-Log -IsError -ErrorMessage $ErrorMessage -StackTraceInfo $StackTraceInfo -LogFile $LogFile -ErrorAction:SilentlyContinue } return $Collection } # Function to evaluate sensitivity labels function EvaluateSensitivityLabels { param ( [Parameter(Mandatory = $true)] [array]$Labels ) # Initialize the result collection $LabelTests = @() foreach ($Label in $Labels) { # Skip labels that are not published if (-not $Label.Published) { Write-Host "Skipping Label: $($Label.DisplayName) as it is not published." -ForegroundColor Yellow continue } $LabelName = $Label.DisplayName $IsPublished = $Label.Published -eq $true $TestResult = @{ LabelName = $LabelName Published = $IsPublished TestUnofficial = "Fail" TestOfficial = "Fail" TestOfficialSensitive = "Fail" } # Check for exact matches if ($LabelName -eq "Unofficial") { $TestResult.TestUnofficial = "Pass" } if ($LabelName -eq "Official") { $TestResult.TestOfficial = "Pass" } if ($LabelName -eq "Official Sensitive") { $TestResult.TestOfficialSensitive = "Pass" } # Check for partial matches if ($LabelName -like "*Unofficial*" -and $TestResult.TestUnofficial -ne "Pass") { $TestResult.TestUnofficial = "Partial Pass" } if ($LabelName -like "*Official*" -and $LabelName -notlike "*Sensitive*" -and $LabelName -notlike "*Unofficial*" -and $TestResult.TestOfficial -ne "Pass") { $TestResult.TestOfficial = "Partial Pass" } if ($LabelName -like "*Official*Sensitive*" -and $TestResult.TestOfficialSensitive -ne "Pass") { $TestResult.TestOfficialSensitive = "Partial Pass" } # Add the test result to the collection $LabelTests += $TestResult } $InfoMessage = "Evaluation of Sensitivity Labels - Completed successfully!" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage return $LabelTests } # Function to evaluate DLP policies against configuration function EvaluateDlpPolicies { param ( [Parameter(Mandatory = $true)] [array]$ConfiguredDlpPolicies, [Parameter(Mandatory = $true)] [array]$DiscoveredDlpPolicies ) $DlpPolicyResults = @() foreach ($configPolicy in $ConfiguredDlpPolicies) { # Look for matching policy by DisplayName $match = $DiscoveredDlpPolicies | Where-Object { $_.DisplayName -eq $configPolicy.DisplayName -or $_.Name -eq $configPolicy.DisplayName } $result = @{ DisplayName = $configPolicy.DisplayName Description = $configPolicy.Description Expected = @{ Enabled = $configPolicy.Enabled RuleCount = if ($configPolicy.Rules) { $configPolicy.Rules.Count } else { 0 } Analytics = $configPolicy.Analytics } Actual = @{ Found = $false Enabled = $false RuleCount = 0 Mode = "Unknown" } Met = $false Issues = @() } if ($match) { $result.Actual.Found = $true $result.Actual.Enabled = $match.Enabled $result.Actual.Mode = $match.Mode # Check if policy meets requirements if ($match.Enabled -eq $configPolicy.Enabled) { $result.Met = $true } else { $result.Issues += "Policy enabled status mismatch" } } else { $result.Issues += "Policy not found in tenant" } $DlpPolicyResults += $result } return $DlpPolicyResults } # Function to evaluate Auto-labeling policies function EvaluateAutoLabelingPolicies { param ( [Parameter(Mandatory = $true)] [array]$DiscoveredAutoLabelPolicies ) $AutoLabelResults = @{ EmailTransportEnabled = $false RecommendationsEnabled = $false SharePointOneDriveEnabled = $false PoliciesFound = $DiscoveredAutoLabelPolicies.Count Details = @() } foreach ($policy in $DiscoveredAutoLabelPolicies) { $policyDetail = @{ Name = $policy.Name Mode = $policy.Mode Workload = $policy.Workload Enabled = $policy.Mode -eq "Enable" } # Check for email transport auto-labeling if ($policy.Workload -like "*Exchange*" -and $policy.Mode -eq "Enable") { $AutoLabelResults.EmailTransportEnabled = $true } # Check for SharePoint/OneDrive auto-labeling if (($policy.Workload -like "*SharePoint*" -or $policy.Workload -like "*OneDrive*") -and $policy.Mode -eq "Enable") { $AutoLabelResults.SharePointOneDriveEnabled = $true } # Check for recommendations (typically TestWithoutNotifications or TestWithNotifications mode) if ($policy.Mode -like "*Test*" -or $policy.Mode -eq "TestWithNotifications") { $AutoLabelResults.RecommendationsEnabled = $true } $AutoLabelResults.Details += $policyDetail } return $AutoLabelResults } # Function to evaluate Insider Risk Management function EvaluateInsiderRiskManagement { param ( [Parameter(Mandatory = $true)] $InsiderRiskData ) $IRMResults = @{ Enabled = $false AnalyticsEnabled = $false PoliciesConfigured = $false ClassifiedItemPolicies = $false AdaptiveProtection = $false Issues = @() } if ($InsiderRiskData -eq "Error") { $IRMResults.Issues += "Insider Risk Management not accessible or configured" return $IRMResults } if ($InsiderRiskData -and $InsiderRiskData.Count -gt 0) { $IRMResults.Enabled = $true $IRMResults.PoliciesConfigured = $true foreach ($policy in $InsiderRiskData) { if ($policy.Enabled) { $IRMResults.AnalyticsEnabled = $true } # Check for policies targeting classified items if ($policy.Name -like "*classified*" -or $policy.Description -like "*classified*") { $IRMResults.ClassifiedItemPolicies = $true } } } else { $IRMResults.Issues += "No IRM policies found" } return $IRMResults } # Main script execution Write-Host "Starting Simplified Purview Configuration Report Generation..." # Step 1: Establish connection to the Compliance Center Connect-ToComplianceCenter # Step 2: Collect data $Collection = @{} $Collection = Get-InformationProtectionSettings -Collection $Collection -LogFile $LogFile $Collection = Get-RetentionCompliance -Collection $Collection $Collection = Get-DataLossPreventionSettings -Collection $Collection -LogFile $LogFile $Collection = Get-InsiderRiskManagementSettings -Collection $Collection -LogFile $LogFile $Collection = Get-TenantDetails -Collection $Collection -LogFile $LogFile $TenantId = $Collection["TenantDetails"]["TenantId"] -replace "[^a-zA-Z0-9]", "" # Remove special characters # Step 3: Load configuration file and evaluate policies $configPath = Join-Path -Path $PSScriptRoot -ChildPath "..\examples\Config_sample.json" if (-not (Test-Path $configPath)) { Write-Host "Warning: Config_sample.json not found at $configPath. Evaluation will be limited." -ForegroundColor Yellow $config = @() } else { $config = Get-Content $configPath | ConvertFrom-Json } # Step 4: Evaluate sensitivity labels # if ($Collection["GetLabel"] -ne "Error") { # $SensitivityLabelTests = EvaluateSensitivityLabels -Labels $Collection["GetLabel"] # $Collection["SensitivityLabelTests"] = $SensitivityLabelTests # } # Step 5: Evaluate maturity model compliance for each model in config # ... (Maturity Model Evaluation logic removed) # Add maturity model evaluation results to collection # $Collection["MaturityModelEvaluation"] = $MaturityModelResults # Step 6: Output directory and file $OutputDir = Join-Path -Path $PSScriptRoot -ChildPath "..\output" if (-not (Test-Path -Path $OutputDir)) { New-Item -ItemType Directory -Path $OutputDir | Out-Null } $OutputFile = Join-Path -Path $OutputDir -ChildPath "OptimizedReport.json" # Step 7: Generate SensitivityLabelReport.json with DLP evaluation # ... (SensitivityLabelReport generation logic removed) # Step 8: Write raw data report to JSON file # First, preprocess the entire collection to ensure it's ready for JSON serialization Write-Host "Preprocessing the collection for JSON export..." -ForegroundColor Cyan $Collection = Convert-ObjectForJson -InputObject $Collection # Now, try to convert to JSON with maximum depth try { $Collection | ConvertTo-Json -Depth 200 | Out-File -FilePath $OutputFile -Encoding UTF8 Write-Host "JSON generation successful with complete data preservation!" -ForegroundColor Green } catch { Write-Host "Conversion to JSON failed even after preprocessing: $_" -ForegroundColor Red Write-Host "Saving raw collection to XML for debugging." -ForegroundColor Yellow $Collection | Export-Clixml -Path "$OutputDir\CollectionRaw_AfterPreProcessing.xml" } Write-Host "Optimized Purview Configuration Report generated successfully at: $OutputFile" # Import the required module for Excel manipulation if (-not (Get-Module -ListAvailable -Name ImportExcel)) { Install-Module -Name ImportExcel -Force -Scope CurrentUser } Import-Module -Name ImportExcel -ErrorAction Stop # Function to extract specific columns from JSON data function ExtractColumns { param ( [Parameter(Mandatory = $true)] [array]$Data, [Parameter(Mandatory = $true)] [array]$Columns ) $ExtractedData = @() foreach ($Item in $Data) { $Row = @{} foreach ($Column in $Columns) { if ($Column -eq "LabelActions_Type" -and $Item.LabelActions) { # Special handling for LabelActions_Type $LabelActions = $Item.LabelActions | ForEach-Object { ($_ | ConvertFrom-Json).Type } $Row[$Column] = ($LabelActions -join ", ") } else { $Row[$Column] = $Item.$Column } } $ExtractedData += [PSCustomObject]$Row } return $ExtractedData } # Function to generate an Excel file from a JSON file # Function to preprocess JSON and handle duplicate keys function PreprocessJsonFile { param ( [Parameter(Mandatory = $true)] [string]$JsonFilePath, # Path to the JSON file [Parameter(Mandatory = $true)] [string]$OutputFilePath # Path to save the processed JSON file ) try { # Read the JSON file as a raw string $RawJson = Get-Content -Path $JsonFilePath -Raw # Handle duplicate keys by renaming 'Value' to 'Value_duplicate' $ProcessedJson = $RawJson -replace '"Value":', '"Value_duplicate":' # Save the processed JSON to a new file Set-Content -Path $OutputFilePath -Value $ProcessedJson -Encoding UTF8 Write-Host "JSON file preprocessed successfully and saved to: $OutputFilePath" -ForegroundColor Green } catch { Write-Host "Error preprocessing JSON file: $_" -ForegroundColor Red } } # Function to extract specific columns from JSON data and add TenantDetails function ExtractColumns { param ( [Parameter(Mandatory = $true)] [array]$Data, [Parameter(Mandatory = $true)] [array]$Columns, [Parameter(Mandatory = $true)] [hashtable]$TenantDetails ) $ExtractedData = @() foreach ($Item in $Data) { $Row = @{} # Add TenantDetails to each row foreach ($TenantKey in $TenantDetails.Keys) { $Row[$TenantKey] = $TenantDetails[$TenantKey] } # Add specified columns foreach ($Column in $Columns) { if ($Column -eq "LabelActions_Type" -and $Item.LabelActions) { # Special handling for LabelActions_Type $LabelActions = $Item.LabelActions | ForEach-Object { ($_ | ConvertFrom-Json).Type } $Row[$Column] = ($LabelActions -join ", ") } else { $Row[$Column] = $Item.$Column } } $ExtractedData += [PSCustomObject]$Row } return $ExtractedData } # Function to dynamically extract LabelActions and create a new tab function ExtractLabelActions { param ( [Parameter(Mandatory = $true)] [array]$LabelActions, # The LabelActions array to process [Parameter(Mandatory = $true)] [hashtable]$TenantDetails, # Tenant details to add to each row [Parameter(Mandatory = $true)] [string]$LabelName, # Label Name to add to each row [Parameter(Mandatory = $false)] [string]$ParentLabelName = "N/A", # Parent Label Name to add to each row [Parameter(Mandatory = $true)] [string]$ImmutableId # Immutable ID to add to each row ) $ExtractedData = @() foreach ($Action in $LabelActions) { try { # Parse the LabelAction JSON string $ParsedAction = $Action | ConvertFrom-Json # Extract Type and SubType $Type = $ParsedAction.Type $SubType = $ParsedAction.SubType # Process Settings array foreach ($Setting in $ParsedAction.Settings) { $Row = @{ # Add Tenant Details TenantId = $TenantDetails["TenantId"] Organization = $TenantDetails["Organization"] UserPrincipalName = $TenantDetails["UserPrincipalName"] Timestamp = $TenantDetails["Timestamp"] # Add Label Details LabelName = $LabelName ImmutableId = $ImmutableId ParentLabelName = $ParentLabelName # Add LabelActions Details Type = $Type SubType = $SubType SettingsKey = $Setting.Key SettingsValue = $Setting.Value SettingsValueIdentity = $null SettingsValueRights = $null } # If the Value contains JSON (e.g., rightsdefinitions), parse it further if ($Setting.Value -is [string] -and $Setting.Value.StartsWith("[") -and $Setting.Value.EndsWith("]")) { $ParsedValue = $Setting.Value | ConvertFrom-Json foreach ($Item in $ParsedValue) { $Row.SettingsValueIdentity = $Item.Identity $Row.SettingsValueRights = $Item.Rights $ExtractedData += [PSCustomObject]$Row } } else { # Add the row as-is if no further parsing is needed $ExtractedData += [PSCustomObject]$Row } } } catch { Write-Host "Error processing LabelAction: $_" -ForegroundColor Red } } return $ExtractedData } # Function to generate an Excel file from a JSON file function GenerateExcelFromJSON { param ( [Parameter(Mandatory = $true)] [string]$JsonFilePath, # Path to the JSON file [Parameter(Mandatory = $true)] [string]$OutputExcelPath, # Path to save the generated Excel file [Parameter(Mandatory = $true)] [hashtable]$TenantDetails # Tenant details to add to all tabs ) # Read and parse the JSON file try { $JsonData = Get-Content -Path $JsonFilePath -Raw | ConvertFrom-Json } catch { Write-Host "Error: Unable to read or parse the JSON file. $_" -ForegroundColor Red return } # Initialize an array to store Excel sheet data $ExcelSheets = @() # Process each section based on the specified columns if ($JsonData.TenantDetails) { $Columns = @("TenantId", "Organization", "UserPrincipalName", "Timestamp") $ExcelSheets += @{ Name = "TenantDetails" Data = ExtractColumns -Data @($JsonData.TenantDetails) -Columns $Columns -TenantDetails $TenantDetails } } if ($JsonData.SensitivityLabelTests) { $Columns = @("LabelName", "TestUnofficial", "TestOfficialSensitive", "TestOfficial") $ExcelSheets += @{ Name = "SensitivityLabelTests" Data = ExtractColumns -Data $JsonData.SensitivityLabelTests -Columns $Columns -TenantDetails $TenantDetails } } if ($JsonData.GetLabelPolicy) { # Flatten the GetLabelPolicy collection $FlattenedData = $JsonData.GetLabelPolicy | ForEach-Object { $FlattenedRow = @{} $_.PSObject.Properties | ForEach-Object { $FlattenedRow[$_.Name] = $_.Value -join ", " # Flatten arrays into comma-separated strings } [PSCustomObject]$FlattenedRow } $ExcelSheets += @{ Name = "GetLabelPolicy" Data = $FlattenedData } } if ($JsonData.GetLabel) { $Columns = @("ImmutableId", "Name", "DisplayName", "Priority", "ParentId", "ParentLabelDisplayName", "IsParent", "Tooltip", "ContentType", "Workload", "IsValid", "CreatedBy", "LastModifiedBy", "WhenCreated", "WhenCreatedUTC", "WhenChangedUTC", "OrganizationId", "LabelActions_Type", "Policy", "Published") $ExcelSheets += @{ Name = "GetLabel" Data = ExtractColumns -Data $JsonData.GetLabel -Columns $Columns -TenantDetails $TenantDetails } # Extract LabelActions and create a new tab $LabelActionsData = @() foreach ($Label in $JsonData.GetLabel) { if ($Label.LabelActions) { $ParentLabelName = if ([string]::IsNullOrWhiteSpace($Label.ParentLabelDisplayName)) { "N/A" } else { $Label.ParentLabelDisplayName } $LabelActionsData += ExtractLabelActions -LabelActions $Label.LabelActions ` -TenantDetails $TenantDetails ` -LabelName $Label.DisplayName ` -ParentLabelName $ParentLabelName ` -ImmutableId $Label.ImmutableId } } if ($LabelActionsData.Count -gt 0) { $ExcelSheets += @{ Name = "LabelActions" Data = $LabelActionsData } } } if ($JsonData.GetAutoSensitivityLabelPolicy) { $Columns = @("Type", "Name", "Guid", "LabelDisplayName", "ApplySensitivityLabel", "OverwriteLabel", "Mode", "Comment", "Workload", "CreatedBy", "LastModifiedBy", "ModificationTimeUtc", "CreationTimeUtc") $ExcelSheets += @{ Name = "GetAutoSensitivityLabelPolicy" Data = ExtractColumns -Data $JsonData.GetAutoSensitivityLabelPolicy -Columns $Columns -TenantDetails $TenantDetails } } if ($JsonData.GetDlpCompliancePolicy) { $Columns = @("Name", "Guid", "DisplayName", "Type", "PolicyCategory", "IsSimulationPolicy", "SimulationStatus", "AutoEnableAfter", "Workload", "Comment", "Enabled", "CreationTimeUtc", "ModificationTimeUtc", "Mode") $ExcelSheets += @{ Name = "GetDlpCompliancePolicy" Data = ExtractColumns -Data $JsonData.GetDlpCompliancePolicy -Columns $Columns -TenantDetails $TenantDetails } } if ($JsonData.GetDlpComplianceRule) { $Columns = @("Name", "Guid", "DisplayName", "RulePriority", "Comment", "Enabled", "SensitiveInformationType", "AccessScopeIs", "BlockAccess", "BlockAccessScope", "NotifyUser", "NotifyPolicyTipDisplayOption", "EncryptionRMSTemplate", "CreationTimeUtc", "ModificationTimeUtc") $ExcelSheets += @{ Name = "GetDlpComplianceRule" Data = ExtractColumns -Data $JsonData.GetDlpComplianceRule -Columns $Columns -TenantDetails $TenantDetails } } if ($JsonData.GetComplianceTag) { $Columns = @("Guid", "Name", "RetentionAction", "RetentionType", "AutoApprovalPeriod", "IsRecordLabel", "HasRetentionAction", "ComplianceTagType", "Workload", "Policy", "CreatedBy", "LastModifiedBy") $ExcelSheets += @{ Name = "GetComplianceTag" Data = ExtractColumns -Data $JsonData.GetComplianceTag -Columns $Columns -TenantDetails $TenantDetails } } if ($JsonData.InsiderRiskManagement) { $Columns = @("Name", "Enabled", "Description", "Actions", "Scope", "Conditions") $ExcelSheets += @{ Name = "InsiderRiskManagement" Data = ExtractColumns -Data $JsonData.InsiderRiskManagement -Columns $Columns -TenantDetails $TenantDetails } } # Generate the Excel file try { $ExcelSheets | ForEach-Object { Export-Excel -Path $OutputExcelPath -WorksheetName $_.Name -AutoSize -ClearSheet -InputObject $_.Data } $InfoMessage = "Excel file generated successfully at: $OutputExcelPath" Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue Write-Host $InfoMessage } catch { Write-Host "Error: Unable to generate the Excel file. $_" -ForegroundColor Red } } # Example usage $OriginalJsonFile = $OutputFile $OutputExcelDir = Join-Path -Path $PSScriptRoot -ChildPath "..\output" if (-not (Test-Path -Path $OutputExcelDir)) { New-Item -ItemType Directory -Path $OutputExcelDir | Out-Null } $ProcessedJsonFile = Join-Path -Path $OutputDir -ChildPath "Preprocessed_OptimizedReport_${TenantId}_$(Get-Date -Format 'yyyyMMddHHmmss').JSON" $OutputExcelFile = Join-Path -Path $OutputExcelDir -ChildPath "OptimizedReport_${TenantId}_$(Get-Date -Format 'yyyyMMddHHmmss').xlsx" # Preprocess the JSON file to handle duplicate keys #PreprocessJsonFile -JsonFilePath $OriginalJsonFile -OutputFilePath $ProcessedJsonFile # Extract TenantDetails for adding to all tabs $JsonForExcel = Get-Content -Path $OriginalJsonFile -Raw | ConvertFrom-Json if ($JsonForExcel.TenantDetails -and $JsonForExcel.TenantDetails.TenantId -ne "Unknown") { $TenantDetails = @{ TenantId = $JsonForExcel.TenantDetails.TenantId Organization = $JsonForExcel.TenantDetails.Organization UserPrincipalName = $JsonForExcel.TenantDetails.UserPrincipalName Timestamp = $JsonForExcel.TenantDetails.Timestamp } Write-Host "TenantDetails extracted successfully from JSON file for Excel generation." -ForegroundColor Green } else { # Fallback tenant details $TenantDetails = @{ TenantId = "Unknown" Organization = "Unknown" UserPrincipalName = "Unknown" Timestamp = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss") } Write-Host "Warning: TenantDetails not available, using fallback values for Excel generation." -ForegroundColor Yellow } # Generate the Excel file from the processed JSON GenerateExcelFromJSON -JsonFilePath $OriginalJsonFile -OutputExcelPath $OutputExcelFile -TenantDetails $TenantDetails # Delete the preprocessed JSON file #Remove-Item -Path $ProcessedJsonFile -Force #Write-Host "Preprocessed JSON file deleted: $ProcessedJsonFile" -ForegroundColor Green |