Collect-PurviewConfiguration.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)) {
        Write-Host "Installing module: $ModuleName..." -ForegroundColor Yellow
        Install-Module -Name $ModuleName -Force -ErrorAction Stop
    }
    Import-Module -Name $ModuleName -ErrorAction Stop
}

# 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"
Write-Log -IsInfo -InfoMessage "Log File Path : $LogFile" -LogFile $LogFile -ErrorAction:SilentlyContinue
        

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 "Connecting to Microsoft Compliance Center..." -ForegroundColor Yellow
    try {
        # Prompt for credentials
        $userName = Read-Host -Prompt 'Enter your User Principal Name (UPN)'
        # Connect to the Compliance Center using UserPrincipalName
        Connect-IPPSSession -UserPrincipalName $userName -ErrorAction SilentlyContinue -WarningAction SilentlyContinue

        $InfoMessage = "[SUCCESS] Connection established successfully!"
        Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue
        Write-Host $InfoMessage -ForegroundColor Green
    } catch {
        Write-Host "[ERROR] Connection failed: $_" -ForegroundColor Red
        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 and deduplicate
            $PublishedImmutableIds = @(
                $Collection["GetLabelPolicy"] |
                ForEach-Object { $_.ScopedLabels } |
                Where-Object { $_ -ne $null } |
                ForEach-Object { [string]$_ } |
                Select-Object -Unique
            )

            # Create a mapping of ImmutableId to Policy details
            $ImmutableIdToPolicyMap = @{}
            foreach ($Policy in $Collection["GetLabelPolicy"]) {
                if ($Policy.ScopedLabels) {
                    foreach ($ScopedLabel in $Policy.ScopedLabels) {
                        $ScopedLabelId = [string]$ScopedLabel
                        if (-not $ImmutableIdToPolicyMap.ContainsKey($ScopedLabelId)) {
                            $ImmutableIdToPolicyMap[$ScopedLabelId] = @()
                        }
                        # Add policy identifier (prefer Guid, fallback to Name)
                        $PolicyId = if ($Policy.Guid) { $Policy.Guid } else { $Policy.Name }
                        $ImmutableIdToPolicyMap[$ScopedLabelId] += $PolicyId
                    }
                }
            }

            # Add Published attribute to each label in GetLabel
            foreach ($Label in $Collection["GetLabel"]) {
                # Ensure ImmutableId is a string
                $ImmutableId = [string]$Label.ImmutableId

                # 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
                }

                # Add the PublishedPolicy property dynamically if it doesn't exist
                if (-not ($Label.PSObject.Properties | Where-Object { $_.Name -eq "PublishedPolicy" })) {
                    $Label | Add-Member -MemberType NoteProperty -Name PublishedPolicy -Value $null -Force
                }

                # Check if ImmutableId is in PublishedImmutableIds
                if ($null -ne $ImmutableId -and $ImmutableId -ne "") {
                    if ($PublishedImmutableIds -contains $ImmutableId) {
                        $Label.Published = $true
                        # Set the PublishedPolicy to the policy(ies) that contain this label
                        if ($ImmutableIdToPolicyMap.ContainsKey($ImmutableId)) {
                            $PolicyIds = $ImmutableIdToPolicyMap[$ImmutableId]
                            $Label.PublishedPolicy = if ($PolicyIds.Count -eq 1) { $PolicyIds[0] } else { $PolicyIds -join ", " }
                        }
                    } else {
                        $Label.Published = $false
                        $Label.PublishedPolicy = $null
                    }
                } else {
                    $Label.Published = $false
                    $Label.PublishedPolicy = $null
                }
            }
            
        }

        $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 

        # 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
}

# Main script execution
Write-Host "Starting Purview Configuration Data Collection..." -ForegroundColor Green

# 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: 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_${TenantId}_$(Get-Date -Format 'yyyyMMddHHmmss').json"
$RunLogFile = Join-Path -Path $OutputDir -ChildPath "file_runlog.txt"

# Step 4: Write raw data report to JSON file

# First, preprocess the entire collection to ensure it's ready for JSON serialization
Write-Host "Generating OptimizedReport.json..." -ForegroundColor Yellow
$Collection = Convert-ObjectForJson -InputObject $Collection

# Now, try to convert to JSON with maximum depth
try {
    # Try with depth 10 first (much more manageable file size)
    $Collection | ConvertTo-Json -Depth 10 | Out-File -FilePath $OutputFile -Encoding UTF8
    Write-Host "[SUCCESS] OptimizedReport.json generated successfully!" -ForegroundColor Green
    
    # Log the generated file
    $LogEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - OptimizedReport: $(Split-Path -Leaf $OutputFile)"
    Add-Content -Path $RunLogFile -Value $LogEntry
    
} catch {
    Write-Host "[ERROR] JSON conversion failed with depth 10: $_" -ForegroundColor Red
    try {
        # Try with depth 5 as fallback
        Write-Host "Trying with reduced depth (5)..." -ForegroundColor Yellow
        $Collection | ConvertTo-Json -Depth 5 | Out-File -FilePath $OutputFile -Encoding UTF8
        Write-Host "[SUCCESS] OptimizedReport.json generated successfully with reduced depth!" -ForegroundColor Green
        
        # Log the generated file
        $LogEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - OptimizedReport: $(Split-Path -Leaf $OutputFile)"
        Add-Content -Path $RunLogFile -Value $LogEntry
        
    } catch {
        Write-Host "[ERROR] JSON conversion failed with depth 5: $_" -ForegroundColor Red
        Write-Host "Creating minimal JSON for Excel processing..." -ForegroundColor Yellow
        # Create a minimal JSON for Excel processing
        $MinimalCollection = @{
            TenantDetails = $Collection["TenantDetails"]
            GetLabel = $Collection["GetLabel"]
            GetLabelPolicy = $Collection["GetLabelPolicy"]
            GetAutoSensitivityLabelPolicy = $Collection["GetAutoSensitivityLabelPolicy"]
            GetDlpCompliancePolicy = $Collection["GetDlpCompliancePolicy"]
            GetDlpComplianceRule = $Collection["GetDlpComplianceRule"]
            GetComplianceTag = $Collection["GetComplianceTag"]
            InsiderRiskManagement = $Collection["InsiderRiskManagement"]
        }
        $MinimalCollection | ConvertTo-Json -Depth 3 | Out-File -FilePath $OutputFile -Encoding UTF8
        Write-Host "[SUCCESS] Minimal OptimizedReport.json generated for Excel processing!" -ForegroundColor Green
        
        # Log the generated file
        $LogEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - OptimizedReport (Minimal): $(Split-Path -Leaf $OutputFile)"
        Add-Content -Path $RunLogFile -Value $LogEntry
    }
}

Write-Host "[SUCCESS] Data collection complete!" -ForegroundColor Green
Write-Host " OptimizedReport.json: $OutputFile" -ForegroundColor Gray

# 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 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.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", "PublishedPolicy")
        $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 = "[SUCCESS] Excel file generated successfully!"
        Write-Log -IsInfo -InfoMessage $InfoMessage -LogFile $LogFile -ErrorAction:SilentlyContinue
        
    } 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
}


$OutputExcelFile = Join-Path -Path $OutputExcelDir -ChildPath "OptimizedReport_${TenantId}_$(Get-Date -Format 'yyyyMMddHHmmss').xlsx"

# 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
    }
} else {
    # Fallback tenant details
    $TenantDetails = @{
        TenantId          = "Unknown"
        Organization      = "Unknown"
        UserPrincipalName = "Unknown"
        Timestamp         = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss")
    }
}

# Generate the Excel file from the processed JSON
if (Test-Path -Path $OriginalJsonFile) {
    GenerateExcelFromJSON -JsonFilePath $OriginalJsonFile -OutputExcelPath $OutputExcelFile -TenantDetails $TenantDetails
    
    Write-Host "[SUCCESS] Excel report generated!" -ForegroundColor Green
    Write-Host " Excel file: $OutputExcelFile" -ForegroundColor Gray
    
    # Log the generated Excel file
    $LogEntry = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Excel Report: $(Split-Path -Leaf $OutputExcelFile)"
    Add-Content -Path $RunLogFile -Value $LogEntry
    
} else {
    Write-Host "[ERROR] JSON file not found. Cannot generate Excel report." -ForegroundColor Red
    Write-Host " Expected JSON file: $OriginalJsonFile" -ForegroundColor Gray
}