EnhancedGraphAO.psm1

#Region '.\Private\Convert-RsaParametersToPem.ps1' -1

function Convert-RsaParametersToPem {
    param (
        [Parameter(Mandatory = $true)]
        [System.Security.Cryptography.RSAParameters]$rsaParameters
    )

    $builder = [System.Text.StringBuilder]::new()

    $builder.AppendLine("-----BEGIN RSA PRIVATE KEY-----") | Out-Null

    # Combine all RSA parameters and convert them to Base64
    $params = @(
        $rsaParameters.Modulus,
        $rsaParameters.Exponent,
        $rsaParameters.D,
        $rsaParameters.P,
        $rsaParameters.Q,
        $rsaParameters.DP,
        $rsaParameters.DQ,
        $rsaParameters.InverseQ
    )

    foreach ($param in $params) {
        $b64 = [System.Convert]::ToBase64String($param)
        $builder.AppendLine($b64) | Out-Null
    }

    $builder.AppendLine("-----END RSA PRIVATE KEY-----") | Out-Null

    return $builder.ToString()
}
#EndRegion '.\Private\Convert-RsaParametersToPem.ps1' 32
#Region '.\Private\Generate-JWTAssertion.ps1' -1

function Generate-JWTAssertion {
    param (
        [Parameter(Mandatory = $true)]
        [hashtable]$jwtHeader,
        [Parameter(Mandatory = $true)]
        [hashtable]$jwtPayload,
        [Parameter(Mandatory = $true)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$cert
    )

    $jwtHeaderJson = ($jwtHeader | ConvertTo-Json -Compress)
    $jwtPayloadJson = ($jwtPayload | ConvertTo-Json -Compress)
    $jwtHeaderEncoded = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($jwtHeaderJson)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
    $jwtPayloadEncoded = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($jwtPayloadJson)).TrimEnd('=').Replace('+', '-').Replace('/', '_')

    $dataToSign = "$jwtHeaderEncoded.$jwtPayloadEncoded"
    $sha256 = [Security.Cryptography.SHA256]::Create()
    $hash = $sha256.ComputeHash([Text.Encoding]::UTF8.GetBytes($dataToSign))

    $rsa = [Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($cert)
    $signature = [Convert]::ToBase64String($rsa.SignHash($hash, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignaturePadding]::Pkcs1)).TrimEnd('=').Replace('+', '-').Replace('/', '_')

    return "$dataToSign.$signature"
}
#EndRegion '.\Private\Generate-JWTAssertion.ps1' 25
#Region '.\Private\Get-UnixTime.ps1' -1

function Get-UnixTime {
    param (
        [Parameter(Mandatory = $true)]
        [int]$offsetMinutes
    )

    return [int]([DateTimeOffset]::UtcNow.ToUnixTimeSeconds() + ($offsetMinutes * 60))
}
#EndRegion '.\Private\Get-UnixTime.ps1' 9
#Region '.\Private\Send-TokenRequest.ps1' -1

function Send-TokenRequest {
    param (
        [Parameter(Mandatory = $true)]
        [string]$tokenEndpoint,
        [Parameter(Mandatory = $true)]
        [string]$clientId,
        [Parameter(Mandatory = $true)]
        [string]$clientAssertion
    )

    $body = @{
        client_id = $clientId
        scope = "https://graph.microsoft.com/.default"
        client_assertion = $clientAssertion
        client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
        grant_type = "client_credentials"
    }

    try {
        Write-EnhancedLog -Message "Sending request to token endpoint: $tokenEndpoint" -Level "INFO"
        $response = Invoke-RestMethod -Method Post -Uri $tokenEndpoint -ContentType "application/x-www-form-urlencoded" -Body $body
        Write-EnhancedLog -Message "Successfully obtained access token." -Level "INFO"
        return $response.access_token
    }
    catch {
        Write-EnhancedLog -Message "Error obtaining access token: $_"
        throw $_
    }
}
#EndRegion '.\Private\Send-TokenRequest.ps1' 30
#Region '.\Public\Add-KeyCredentialToApp.ps1' -1

# Associate certificate with App Registration
function Add-KeyCredentialToApp {
    param (
        [Parameter(Mandatory = $true)]
        [string]$AppId,

        [Parameter(Mandatory = $true)]
        [string]$CertPath
    )

    # Read the certificate file using the constructor
    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertPath)
    $certBytes = $cert.RawData
    $base64Cert = [System.Convert]::ToBase64String($certBytes)

    # Convert certificate dates to DateTime and adjust for time zone
    $startDate = [datetime]::Parse($cert.NotBefore.ToString("o"))
    $endDate = [datetime]::Parse($cert.NotAfter.ToString("o"))

    # Adjust the start and end dates to ensure they are valid and in UTC
    $startDate = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($startDate, [System.TimeZoneInfo]::Local.Id, 'UTC')
    $endDate = [System.TimeZoneInfo]::ConvertTimeBySystemTimeZoneId($endDate, [System.TimeZoneInfo]::Local.Id, 'UTC')

    # Adjust end date by subtracting one day to avoid potential end date issues
    $endDate = $endDate.AddDays(-1)

    # Prepare the key credential parameters
    $keyCredentialParams = @{
        CustomKeyIdentifier = [System.Convert]::FromBase64String([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($cert.Thumbprint.Substring(0, 32))))
        DisplayName = "GraphCert"
        EndDateTime = $endDate
        StartDateTime = $startDate
        KeyId = [Guid]::NewGuid().ToString()
        Type = "AsymmetricX509Cert"
        Usage = "Verify"
        Key = $certBytes
    }

    # Create the key credential object
    $keyCredential = [Microsoft.Graph.PowerShell.Models.MicrosoftGraphKeyCredential]::new()
    $keyCredential.CustomKeyIdentifier = $keyCredentialParams.CustomKeyIdentifier
    $keyCredential.DisplayName = $keyCredentialParams.DisplayName
    $keyCredential.EndDateTime = $keyCredentialParams.EndDateTime
    $keyCredential.StartDateTime = $keyCredentialParams.StartDateTime
    $keyCredential.KeyId = $keyCredentialParams.KeyId
    $keyCredential.Type = $keyCredentialParams.Type
    $keyCredential.Usage = $keyCredentialParams.Usage
    $keyCredential.Key = $keyCredentialParams.Key

    # Update the application with the new key credential
    try {
        Update-MgApplication -ApplicationId $AppId -KeyCredentials @($keyCredential)
        Write-Host "Key credential added successfully to the application."
    } catch {
        Write-Host "An error occurred: $_" -ForegroundColor Red
    }
}


#EndRegion '.\Public\Add-KeyCredentialToApp.ps1' 60
#Region '.\Public\Connect-GraphWithCert.ps1' -1

function Connect-GraphWithCert {
    param (
        [Parameter(Mandatory = $true)]
        [string]$tenantId,
        [Parameter(Mandatory = $true)]
        [string]$clientId,
        [Parameter(Mandatory = $true)]
        [string]$certPath,
        [Parameter(Mandatory = $true)]
        [string]$certPassword,
        [switch]$ConnectToIntune,
        [switch]$ConnectToTeams
    )

    try {
        # Log the certificate path
        Log-Params -Params @{certPath = $certPath}

        # Load the certificate from the PFX file
        $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($certPath, $certPassword)

        # Define the splat for Connect-MgGraph
        $GraphParams = @{
            ClientId    = $clientId
            TenantId    = $tenantId
            Certificate = $cert
        }

        # Log the parameters
        Log-Params -Params $GraphParams

        # Obtain access token (if needed separately)
        $accessToken = Get-MsGraphAccessTokenCert -tenantId $tenantId -clientId $clientId -certPath $certPath -certPassword $certPassword
        Log-Params -Params @{accessToken = $accessToken}

        # Connect to Microsoft Graph
        Write-EnhancedLog -message 'Calling Connect-MgGraph with client certificate file path and password' -Level 'INFO'
        Connect-MgGraph @GraphParams -NoWelcome

        # Conditional check for Intune connection
        if ($ConnectToIntune) {
            try {
                # Define the parameters for non-interactive connection to Intune
                $IntuneGraphconnectionParams = @{
                    ClientId    = $clientId
                    TenantId    = $tenantId
                    ClientCert  = $cert
                }

                # Log the connection attempt
                Write-EnhancedLog -Message "Calling Connect-MSIntuneGraph with connectionParams" -Level "WARNING"

                # Call the Connect-MSIntuneGraph function with splatted parameters
                $Session = Connect-MSIntuneGraph @IntuneGraphconnectionParams

                # Log the successful connection
                Write-EnhancedLog -Message "Connecting to Graph using Connect-MSIntuneGraph - done" -Level "INFO"
            } catch {
                Handle-Error -ErrorRecord $_
            }
        }

        # Conditional check for Teams connection
        if ($ConnectToTeams) {
            try {
                Write-EnhancedLog -Message "Connecting to Microsoft Teams" -Level "INFO"

                # Connect to Microsoft Teams using the certificate
                Connect-MicrosoftTeams -TenantId $tenantId -Certificate $cert -ApplicationId $clientId

                Write-EnhancedLog -Message "Connected to Microsoft Teams" -Level "INFO"
            } catch {
                Handle-Error -ErrorRecord $_
            }
        }

        return $accessToken
    } catch {
        Handle-Error -ErrorRecord $_
    }
}

#Note for Teams Connection you must add RBAC role like Teams Admin to the app as well in additon to the API permissions as mentioned below

# https://learn.microsoft.com/en-us/MicrosoftTeams/teams-powershell-application-authentication

# Setup Application-based authentication
# An initial onboarding is required for authentication using application objects. Application and service principal are used interchangeably, but an application is like a class object while a service principal is like an instance of the class. You can learn more about these objects at Application and service principal objects in Microsoft Entra ID.

# Sample steps for creating applications in Microsoft Entra ID are mentioned below. For detailed steps, refer to this article.

# 1- Register the application in Microsoft Entra ID.
# 2- Assign API permissions to the application.
# 2.1 For *-Cs cmdlets - the Microsoft Graph API permission needed is Organization.Read.All.
# 2.2 For Non *-Cs cmdlets - the Microsoft Graph API permissions needed are Organization.Read.All, User.Read.All, Group.ReadWrite.All, AppCatalog.ReadWrite.All, TeamSettings.ReadWrite.All, Channel.Delete.All, ChannelSettings.ReadWrite.All, ChannelMember.ReadWrite.All.
# 3. Generate a self-signed certificate.
# 4. Attach the certificate to the Microsoft Entra application.
# 5. Assign Microsoft Entra roles to the application. Refer to this Assign a role procedure, but search for the application instead of a user.
# The application needs to have the appropriate RBAC roles assigned. Because the apps are provisioned in Microsoft Entra ID, you can use any of the supported built-in roles.
#EndRegion '.\Public\Connect-GraphWithCert.ps1' 100
#Region '.\Public\Connect-ToMicrosoftGraphIfServerCore.ps1' -1

function Connect-ToMicrosoftGraphIfServerCore {
    param (
        [string[]]$Scopes
    )

    if (Is-ServerCore) {
        Write-Output "Running on Windows Server Core. Using device authentication for Microsoft Graph."
        Connect-MgGraph -Scopes $Scopes -Verbose -UseDeviceAuthentication
    } else {
        Write-Output "Not running on Windows Server Core. Using default authentication for Microsoft Graph."
        Connect-MgGraph -Scopes $Scopes -Verbose
    }
}
#EndRegion '.\Public\Connect-ToMicrosoftGraphIfServerCore.ps1' 14
#Region '.\Public\Convert-EntraDeviceIdToIntuneDeviceId.ps1' -1

function Convert-EntraDeviceIdToIntuneDeviceId {
    param (
        [Parameter(Mandatory = $true)]
        [string]$entraDeviceId,
        [hashtable]$headers
    )

    Write-EnhancedLog -Message "Converting Entra Device ID: $entraDeviceId to Intune Device ID" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)

    try {
        # Construct the Graph API URL to retrieve device details
        $graphApiUrl = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=azureADDeviceId eq '$entraDeviceId'"
        Write-Output "Constructed Graph API URL: $graphApiUrl"

        # Send the request
        $response = Invoke-WebRequest -Uri $graphApiUrl -Headers $headers -Method Get
        $data = ($response.Content | ConvertFrom-Json).value

        if ($data -and $data.Count -gt 0) {
            $intuneDeviceId = $data[0].id
            Write-EnhancedLog -Message "Converted Entra Device ID: $entraDeviceId to Intune Device ID: $intuneDeviceId" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green)
            return $intuneDeviceId
        } else {
            Write-EnhancedLog -Message "No Intune Device found for Entra Device ID: $entraDeviceId" -Level "WARN" -ForegroundColor ([ConsoleColor]::Yellow)
            return $null
        }
    } catch {
        Write-EnhancedLog -Message "Error converting Entra Device ID to Intune Device ID: $_" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
        return $null
    }
}

# # Example usage
# $headers = @{ Authorization = "Bearer your-access-token" }
# $entraDeviceId = "73e94a92-fc5a-45b6-bf6c-90ce8a353c44"

# $intuneDeviceId = Convert-EntraDeviceIdToIntuneDeviceId -entraDeviceId $entraDeviceId -Headers $headers
# Write-Output "Intune Device ID: $intuneDeviceId"
#EndRegion '.\Public\Convert-EntraDeviceIdToIntuneDeviceId.ps1' 39
#Region '.\Public\Create-AndVerifyServicePrincipal.ps1' -1

function Create-AndVerifyServicePrincipal {
    param (
        [Parameter(Mandatory = $true)]
        [string]$ClientId
    )

    try {
        Write-EnhancedLog -Message "Creating a new service principal for the application with Client ID: $ClientId" -Level "INFO"

        # Create a new service principal for the application
        New-MgServicePrincipal -AppId $ClientId

        Write-EnhancedLog -Message "Service principal created successfully." -Level "INFO"

        # Verify the creation
        $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$ClientId'"

        if ($null -eq $servicePrincipal) {
            Write-EnhancedLog -Message "Service principal not found after creation." -Level "ERROR"
            throw "Service principal not found after creation"
        }

        Write-EnhancedLog -Message "Service principal verified successfully." -Level "INFO"

        # Display the service principal details
        $servicePrincipal | Format-Table DisplayName, AppId, Id

        return $servicePrincipal

    } catch {
        Write-EnhancedLog -Message "An error occurred while creating or verifying the service principal." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}

# Example usage
# Create-AndVerifyServicePrincipal -ClientId "your-application-id"
#EndRegion '.\Public\Create-AndVerifyServicePrincipal.ps1' 39
#Region '.\Public\Create-AppRegistration.ps1' -1

function Create-AppRegistration {
    param (
        [string]$AppName,
        # [string]$PermsFile = "$PSScriptRoot\permissions.json"
        [string]$PermsFile
    )

    try {
        if (-Not (Test-Path $PermsFile)) {
            Write-EnhancedLog -Message "Permissions file not found: $PermsFile" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
            throw "Permissions file missing"
        }
    
        $permissions = Get-Content -Path $PermsFile -Raw | ConvertFrom-Json

        # Convert the JSON data to the required types
        $requiredResourceAccess = @()
        foreach ($perm in $permissions.permissions) {
            $resourceAccess = @()
            foreach ($access in $perm.ResourceAccess) {
                $resourceAccess += [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphResourceAccess]@{
                    Id   = [Guid]$access.Id
                    Type = $access.Type
                }
            }
            $requiredResourceAccess += [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphRequiredResourceAccess]@{
                ResourceAppId  = [Guid]$perm.ResourceAppId
                ResourceAccess = $resourceAccess
            }
        }

        # Connect to Graph interactively
        # Connect-MgGraph -Scopes "Application.ReadWrite.All"
    
        # Get tenant details
        $tenantDetails = Get-MgOrganization | Select-Object -First 1
    
        # Create the application
        $app = New-MgApplication -DisplayName $AppName -SignInAudience "AzureADMyOrg" -RequiredResourceAccess $requiredResourceAccess
    
        if ($null -eq $app) {
            Write-EnhancedLog -Message "App registration failed" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
            throw "App registration failed"
        }
    
        Write-EnhancedLog -Message "App registered successfully" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)
        return @{ App = $app; TenantDetails = $tenantDetails }
        
    }
    catch {
        Handle-Error -ErrorRecord $_
    }
}
#EndRegion '.\Public\Create-AppRegistration.ps1' 54
#Region '.\Public\Create-SelfSignedCert.ps1' -1

# function Create-SelfSignedCert {
# param (
# [string]$CertName,
# [string]$CertStoreLocation = "Cert:\CurrentUser\My",
# [string]$TenantName,
# [string]$AppId
# )

# $cert = New-SelfSignedCertificate -CertStoreLocation $CertStoreLocation `
# -Subject "CN=$CertName, O=$TenantName, OU=$AppId" `
# -KeyLength 2048 `
# -NotAfter (Get-Date).AddDays(30)

# if ($null -eq $cert) {
# Write-EnhancedLog -Message "Failed to create certificate" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
# throw "Certificate creation failed"
# }
# Write-EnhancedLog -Message "Certificate created successfully" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)
# return $cert
# }







# function Create-SelfSignedCert {
# param (
# [string]$CertName,
# [string]$CertStoreLocation = "Cert:\CurrentUser\My",
# [string]$TenantName,
# [string]$AppId,
# # [string]$OutputPath = "C:\Certificates",
# [string]$OutputPath,
# # [string]$PfxPassword = "YourPfxPassword"
# [string]$PfxPassword
# )

# try {
# # Create output directory if it doesn't exist
# if (-not (Test-Path -Path $OutputPath)) {
# New-Item -ItemType Directory -Path $OutputPath
# }

# # Define certificate subject details
# $subject = "CN=$CertName, O=$TenantName, OU=$AppId, L=City, S=State, C=US"

# # Generate the self-signed certificate
# $cert = New-SelfSignedCertificate -CertStoreLocation $CertStoreLocation `
# -Subject $subject `
# -KeyLength 2048 `
# -KeyExportPolicy Exportable `
# -NotAfter (Get-Date).AddDays(30) `
# -KeyUsage DigitalSignature, KeyEncipherment `
# -FriendlyName "$CertName for $TenantName"

# if ($null -eq $cert) {
# Write-EnhancedLog -Message "Failed to create certificate" -Level "ERROR"
# throw "Certificate creation failed"
# }

# Write-EnhancedLog -Message "Certificate created successfully" -Level "INFO"

# # Convert password to secure string
# $securePfxPassword = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText

# # Export the certificate to a PFX file
# $pfxFilePath = Join-Path -Path $OutputPath -ChildPath "$CertName-$TenantName-$AppId.pfx"
# Export-PfxCertificate -Cert $cert -FilePath $pfxFilePath -Password $securePfxPassword

# Write-EnhancedLog -Message "PFX file created successfully at $pfxFilePath" -Level "INFO"

# # Export the private key
# $privateKeyFilePath = Join-Path -Path $OutputPath -ChildPath "$CertName-$TenantName-$AppId.key"
# $privateKey = $cert.PrivateKey
# $privateKeyBytes = [System.Convert]::ToBase64String($privateKey.ExportCspBlob($true))
# Set-Content -Path $privateKeyFilePath -Value $privateKeyBytes

# Write-EnhancedLog -Message "Private key file created successfully at $privateKeyFilePath" -Level "INFO"

# return $cert

# } catch {
# Write-EnhancedLog -Message "An error occurred while creating the self-signed certificate" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# Example usage
# $cert = Create-SelfSignedCert -CertName "GraphCert" -TenantName "YourTenantName" -AppId "YourAppId" -OutputPath $OutputPath















# function Create-SelfSignedCert {
# param (
# [string]$CertName,
# [string]$CertStoreLocation = "Cert:\CurrentUser\My",
# [string]$TenantName,
# [string]$AppId,
# [string]$OutputPath,
# [string]$PfxPassword
# )

# try {
# # Create output directory if it doesn't exist
# if (-not (Test-Path -Path $OutputPath)) {
# New-Item -ItemType Directory -Path $OutputPath -Force
# }

# # Get the logged-in user for the Graph API session
# Write-EnhancedLog -Message "Fetching current user information from Microsoft Graph API." -Level "INFO"
# $currentUserResponse = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/me" -Method GET
# Write-EnhancedLog -Message "Response from Microsoft Graph API: $($currentUserResponse | ConvertTo-Json -Compress)" -Level "DEBUG"

# $currentUser = $currentUserResponse

# # $DBG

# # Define certificate subject details
# $subject = "CN=$CertName-$AppId, O=$TenantName, OU=$AppId, L=City, S=State, C=US"

# # Generate the self-signed certificate
# $certParams = @{
# CertStoreLocation = $CertStoreLocation
# Subject = $subject
# Issuer = "CN=$($currentUser.DisplayName)-$($currentUser.userPrincipalName)"
# KeyLength = 2048
# KeyExportPolicy = "Exportable"
# NotAfter = (Get-Date).AddDays(30)
# KeyUsage = "DigitalSignature, KeyEncipherment"
# FriendlyName = "$CertName-$AppId for $TenantName"
# }

# # Generate the self-signed certificate
# $cert = New-SelfSignedCertificate @certParams

# $DBG

# if ($null -eq $cert) {
# Write-EnhancedLog -Message "Failed to create certificate" -Level "ERROR"
# throw "Certificate creation failed"
# }

# Write-EnhancedLog -Message "Certificate created successfully" -Level "INFO"

# # Convert password to secure string
# $securePfxPassword = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText

# # Export the certificate to a PFX file
# # $pfxFilePath = Join-Path -Path "$OutputPath" -ChildPath "$CertName-$TenantName-$AppId.pfx"
# $pfxFilePath = $null
# # $pfxFilePath = Join-Path -Path "$OutputPath" -ChildPath "$CertName-$TenantName.pfx"
# $pfxFilePath = Join-Path -Path "$OutputPath" -ChildPath "$CertName.pfx"
# # $pfxFilePath = Join-Path -Path "$OutputPath" -ChildPath "1.pfx"

# # $pfxFilePath = "C:\Code\GraphAppwithCert\Graph\Information and Communications Technology Council_b5dae566-ad8f-44e1-9929-5669f1dbb343\c.pfx"

# # $DBG

# Export-PfxCertificate -Cert $cert -FilePath "$pfxFilePath" -Password $securePfxPassword

# Write-EnhancedLog -Message "Certificate $cert exported successfully to pfx file located in $pfxFilePath " -Level "INFO"

# $DBG

# Write-EnhancedLog -Message "PFX file created successfully at $pfxFilePath" -Level "INFO"

# # Export the private key
# # $privateKeyFilePath = Join-Path -Path "$OutputPath" -ChildPath "$CertName-$TenantName-$AppId.key"
# $privateKeyFilePath = Join-Path -Path "$OutputPath" -ChildPath "$CertName-$TenantName.key"
# $privateKey = $cert.PrivateKey

# $rsaParameters = $privateKey.ExportParameters($true)
# $privateKeyPem = Convert-RsaParametersToPem -rsaParameters $rsaParameters
# Set-Content -Path $privateKeyFilePath -Value $privateKeyPem

# Write-EnhancedLog -Message "Private key file created successfully at $privateKeyFilePath" -Level "INFO"

# return $cert

# }
# catch {
# Write-EnhancedLog -Message "An error occurred while creating the self-signed certificate" -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }



# # Example usage
# $certParams = @{
# CertName = "GraphCert"
# TenantName = "YourTenantName"
# AppId = "YourAppId"
# OutputPath = "C:\Certificates"
# PfxPassword = "YourPfxPassword"
# }
# $cert = Create-SelfSignedCert @certParams










# function Create-SelfSignedCert {
# param (
# [string]$CertName,
# [string]$CertStoreLocation = "Cert:\CurrentUser\My",
# [string]$TenantName,
# [string]$AppId,
# [string]$OutputPath,
# [string]$PfxPassword
# )

# try {
# # Get the logged-in user for the Graph API session
# Write-EnhancedLog -Message "Fetching current user information from Microsoft Graph API." -Level "INFO"
# $currentUserResponse = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/me" -Method GET
# Write-EnhancedLog -Message "Response from Microsoft Graph API: $($currentUserResponse | ConvertTo-Json -Compress)" -Level "DEBUG"

# $currentUser = $currentUserResponse

# # Create output directory if it doesn't exist
# if (-not (Test-Path -Path $OutputPath)) {
# New-Item -ItemType Directory -Path $OutputPath
# }

# # Define certificate subject details
# $subject = "CN=$CertName-$AppId, O=$TenantName, OU=$AppId, L=City, S=State, C=US"

# # Splat the parameters
# $certParams = @{
# CertStoreLocation = $CertStoreLocation
# Subject = $subject
# Issuer = "CN=$($currentUser.displayName)"
# KeyLength = 2048
# KeyExportPolicy = "Exportable"
# NotAfter = (Get-Date).AddDays(30)
# KeyUsage = @("DigitalSignature", "KeyEncipherment")
# FriendlyName = "$CertName-$AppId for $TenantName"
# }

# # Generate the self-signed certificate
# $cert = New-SelfSignedCertificate @certParams

# if ($null -eq $cert) {
# Write-EnhancedLog -Message "Failed to create certificate" -Level "ERROR"
# throw "Certificate creation failed"
# }

# Write-EnhancedLog -Message "Certificate created successfully" -Level "INFO"

# # Convert password to secure string
# $securePfxPassword = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText

# # Export the certificate to a PFX file
# $pfxFilePath = Join-Path -Path $OutputPath -ChildPath "$CertName-$TenantName-$AppId.pfx"
# Export-PfxCertificate -Cert $cert -FilePath $pfxFilePath -Password $securePfxPassword

# Write-EnhancedLog -Message "PFX file created successfully at $pfxFilePath" -Level "INFO"

# return $cert

# } catch {
# Write-EnhancedLog -Message "An error occurred while creating the self-signed certificate." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# Example usage
# $scopes = @("User.Read.All", "Application.ReadWrite.All", "Directory.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# $certParams = @{
# CertName = "GraphCert"
# TenantName = $tenantDetails.DisplayName
# AppId = $app.AppId
# OutputPath = $certexportDirectory
# PfxPassword = $certPassword
# }
# $cert = Create-SelfSignedCert @certParams






function Create-SelfSignedCert {
    param (
        [string]$CertName,
        [string]$CertStoreLocation = "Cert:\CurrentUser\My",
        [string]$TenantName,
        [string]$AppId,
        [string]$OutputPath,
        [string]$PfxPassword
    )

    try {
        # Get the logged-in user for the Graph API session
        Write-EnhancedLog -Message "Fetching current user information from Microsoft Graph API." -Level "INFO"
        $currentUserResponse = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/me" -Method GET
        Write-EnhancedLog -Message "Response from Microsoft Graph API: $($currentUserResponse | ConvertTo-Json -Compress)" -Level "DEBUG"

        $currentUser = $currentUserResponse

        # Create output directory if it doesn't exist
        if (-not (Test-Path -Path $OutputPath)) {
            New-Item -ItemType Directory -Path $OutputPath
        }

         # Define certificate subject details
        $subject = "CN=$CertName-$AppId, O=$TenantName, OU=$AppId, L=City, S=State, C=US"
        $Issuer  = "CN=$($currentUser.DisplayName)-$($currentUser.userPrincipalName)"

        # Splat the parameters
        $certParams = @{
            CertStoreLocation = $CertStoreLocation
            Subject           = $subject
            KeyLength         = 2048
            KeyExportPolicy   = "Exportable"
            NotAfter          = (Get-Date).AddDays(30)
            KeyUsage          = @("DigitalSignature", "KeyEncipherment")
            FriendlyName      = "$CertName-$AppId for $TenantName by $Issuer"
        }

        # Generate the self-signed certificate
        $cert = New-SelfSignedCertificate @certParams

        if ($null -eq $cert) {
            Write-EnhancedLog -Message "Failed to create certificate" -Level "ERROR"
            throw "Certificate creation failed"
        }

        Write-EnhancedLog -Message "Certificate created successfully" -Level "INFO"

        # Convert password to secure string
        $securePfxPassword = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText

        # Export the certificate to a PFX file
        $pfxFilePath = Join-Path -Path $OutputPath -ChildPath "$CertName-$AppId.pfx"
        Export-PfxCertificate -Cert $cert -FilePath $pfxFilePath -Password $securePfxPassword

        Write-EnhancedLog -Message "PFX file created successfully at $pfxFilePath" -Level "INFO"


        # $DBG

        return $cert

    } catch {
        Write-EnhancedLog -Message "An error occurred while creating the self-signed certificate." -Level "ERROR"
        Handle-Error -ErrorRecord $_ 
        throw $_
    }
}

# # Example usage
# $scopes = @("User.Read.All", "Application.ReadWrite.All", "Directory.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# $certParams = @{
# CertName = "GraphCert"
# TenantName = $tenantDetails.DisplayName
# AppId = $app.AppId
# OutputPath = $certexportDirectory
# PfxPassword = $certPassword
# }
# $cert = Create-SelfSignedCert @certParams






#EndRegion '.\Public\Create-SelfSignedCert.ps1' 395
#Region '.\Public\Create-SelfSignedCertOpenSSL.ps1' -1

# We'll create the following functions:

# Get-CurrentUser
# Generate-Certificate
# Convert-CertificateToPfx
# Import-PfxCertificateToStore
# Create-SelfSignedCertOpenSSL




function Get-CurrentUser {
    try {
        Write-EnhancedLog -Message "Fetching current user information from Microsoft Graph API." -Level "INFO"
        $currentUserResponse = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/me" -Method GET
        Write-EnhancedLog -Message "Response from Microsoft Graph API: $($currentUserResponse | ConvertTo-Json -Compress)" -Level "DEBUG"
        return $currentUserResponse
    } catch {
        Write-EnhancedLog -Message "An error occurred while fetching the current user information." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}

function Convert-CertificateToPfx {
    param (
        [string]$CertKeyPath,
        [string]$CertCrtPath,
        [string]$PfxPath,
        [string]$PfxPassword
    )

    try {
        $opensslPfxCmd = "openssl pkcs12 -export -out `"$PfxPath`" -inkey `"$CertKeyPath`" -in `"$CertCrtPath`" -passout pass:$PfxPassword"
        Write-EnhancedLog -Message "Running OpenSSL command to convert certificate to PFX format: $opensslPfxCmd" -Level "INFO"

        $processInfo = New-Object System.Diagnostics.ProcessStartInfo
        $processInfo.FileName = "/bin/bash"
        $processInfo.Arguments = "-c `"$opensslPfxCmd`""
        $processInfo.RedirectStandardOutput = $true
        $processInfo.RedirectStandardError = $true
        $processInfo.UseShellExecute = $false
        $processInfo.CreateNoWindow = $true

        $process = New-Object System.Diagnostics.Process
        $process.StartInfo = $processInfo
        $process.Start() | Out-Null

        $stdout = $process.StandardOutput.ReadToEnd()
        $stderr = $process.StandardError.ReadToEnd()

        $process.WaitForExit()

        Write-EnhancedLog -Message "Standard Output: $stdout" -Level "DEBUG"
        Write-EnhancedLog -Message "Standard Error: $stderr" -Level "DEBUG"

        if ($process.ExitCode -ne 0) {
            Write-EnhancedLog -Message "OpenSSL PFX command failed with exit code $($process.ExitCode)" -Level "ERROR"
            throw "PFX file creation failed"
        }

        Write-EnhancedLog -Message "PFX file created successfully at $PfxPath" -Level "INFO"
    } catch {
        Write-EnhancedLog -Message "An error occurred while converting the certificate to PFX format." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}



function Import-PfxCertificateToStore {
    param (
        [string]$PfxPath,
        [string]$PfxPassword,
        [string]$CertStoreLocation
    )

    try {
        $securePfxPassword = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText
        $cert = Import-PfxCertificate -FilePath $PfxPath -Password $securePfxPassword -CertStoreLocation $CertStoreLocation
        Write-EnhancedLog -Message "Certificate imported successfully into store location $CertStoreLocation" -Level "INFO"
        return $cert
    } catch {
        Write-EnhancedLog -Message "An error occurred while importing the PFX certificate to the store." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}



function Create-DummyCertWithOpenSSL {
    param (
        # [string]$OutputDir = "/workspaces/cert"
        [string]$OutputDir
    )

    try {
        # Ensure the output directory exists
        if (-not (Test-Path -Path $OutputDir)) {
            New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
            Write-EnhancedLog -Message "Created output directory: $OutputDir" -Level "INFO"
        } else {
            Write-EnhancedLog -Message "Output directory already exists: $OutputDir" -Level "INFO"
        }

        # Define a simple command to run OpenSSL
        $opensslCmd = "openssl req -x509 -nodes -days 1 -newkey rsa:2048 -keyout $OutputDir/dummy.key -out $OutputDir/dummy.crt -subj '/CN=DummyCert/O=DummyOrg/C=US'"
        Write-EnhancedLog -Message "Running OpenSSL command: $opensslCmd" -Level "INFO"

        # Use Start-Process to execute the command
        $startInfo = New-Object System.Diagnostics.ProcessStartInfo
        $startInfo.FileName = "/bin/bash"
        $startInfo.Arguments = "-c `"$opensslCmd`""
        $startInfo.RedirectStandardOutput = $true
        $startInfo.RedirectStandardError = $true
        $startInfo.UseShellExecute = $false
        $startInfo.CreateNoWindow = $true

        $process = New-Object System.Diagnostics.Process
        $process.StartInfo = $startInfo
        $process.Start() | Out-Null

        # Capture the output
        $stdout = $process.StandardOutput.ReadToEnd()
        $stderr = $process.StandardError.ReadToEnd()

        $process.WaitForExit()

        # Output the results
        Write-EnhancedLog -Message "Standard Output: $stdout" -Level "INFO"
        Write-EnhancedLog -Message "Standard Error: $stderr" -Level "INFO"

        if ($process.ExitCode -ne 0) {
            Write-EnhancedLog -Message "OpenSSL command failed with exit code $($process.ExitCode)" -Level "ERROR"
            throw "Certificate creation failed"
        } else {
            Write-EnhancedLog -Message "Certificate created successfully using OpenSSL" -Level "INFO"
        }
    } catch {
        Write-EnhancedLog -Message "An error occurred while generating the certificate." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}

# # Test the function
# Create-DummyCertWithOpenSSL

# $DBG


function Create-SelfSignedCertOpenSSL {
    param (
        [string]$CertName,
        [string]$CertStoreLocation = "Cert:\CurrentUser\My",
        [string]$TenantName,
        [string]$AppId,
        [string]$OutputPath,
        [string]$PfxPassword
    )

    try {
        $currentUser = Get-CurrentUser

        # Create output directory if it doesn't exist
        if (-not (Test-Path -Path $OutputPath)) {
            New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
        }

        Write-EnhancedLog -Message "calling Run-GenerateCertificateScript" -Level "INFO"
        $DBG
        # $certPaths = Run-GenerateCertificateScript -CertName $CertName -TenantName $TenantName -AppId $AppId -OutputPath $OutputPath -CurrentUser $currentUser -RealCertName $CertName -RealTenantName $TenantName -RealAppId $AppId

        Create-DummyCertWithOpenSSL -OutputDir $OutputPath

        Write-EnhancedLog -Message "Done calling Run-GenerateCertificateScript" -Level "INFO"

        $DBG

        $pfxFilePath = Join-Path -Path $OutputPath -ChildPath "$CertName-$AppId.pfx"

        Convert-CertificateToPfx -CertKeyPath $certPaths.KeyPath -CertCrtPath $certPaths.CrtPath -PfxPath $pfxFilePath -PfxPassword $PfxPassword

        if ($PSVersionTable.OS -match "Windows") {
            try {
                $securePfxPassword = ConvertTo-SecureString -String $PfxPassword -Force -AsPlainText
                $cert = Import-PfxCertificateToStore -FilePath $pfxFilePath -Password $securePfxPassword -CertStoreLocation $CertStoreLocation
                Write-EnhancedLog -Message "Certificate imported successfully into store location $CertStoreLocation" -Level "INFO"
            } catch {
                Write-EnhancedLog -Message "An error occurred while importing the PFX certificate to the store." -Level "ERROR"
                Handle-Error -ErrorRecord $_
                throw $_
            }
        } else {
            Write-EnhancedLog -Message "Running on a non-Windows OS, skipping the import of the PFX certificate to the store." -Level "INFO"
            $cert = $null
        }

        return $cert
    } catch {
        Write-EnhancedLog -Message "An error occurred while creating the self-signed certificate." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}
#EndRegion '.\Public\Create-SelfSignedCertOpenSSL.ps1' 208
#Region '.\Public\ExportCertificatetoCER.ps1' -1

    # # Define the function
    # function ExportCertificatetoCER {
    # param (
    # [Parameter(Mandatory = $true)]
    # [string]$CertThumbprint,

    # [Parameter(Mandatory = $true)]
    # [string]$ExportDirectory
    # )

    # try {
    # # Get the certificate from the current user's personal store
    # $cert = Get-Item -Path "Cert:\CurrentUser\My\$CertThumbprint"
        
    # # Ensure the export directory exists
    # if (-not (Test-Path -Path $ExportDirectory)) {
    # New-Item -ItemType Directory -Path $ExportDirectory -Force
    # }

    # # Dynamically create a file name using the certificate subject name and current timestamp
    # $timestamp = (Get-Date).ToString("yyyyMMddHHmmss")
    # $subjectName = $cert.SubjectName.Name -replace "[^a-zA-Z0-9]", "_"
    # $fileName = "${subjectName}_$timestamp"

    # # Set the export file path
    # $certPath = Join-Path -Path $ExportDirectory -ChildPath "$fileName.cer"
        
    # # Export the certificate to a file (DER encoded binary format with .cer extension)
    # $cert | Export-Certificate -FilePath $certPath -Type CERT -Force | Out-Null

    # # Output the export file path
    # Write-EnhancedLog -Message "Certificate exported to: $certPath"

    # # Return the export file path
    # return $certPath
    # }
    # catch {
    # Write-Host "Failed to export certificate: $_" -ForegroundColor Red
    # }
    # }


    function ExportCertificatetoCER {
        param (
            [Parameter(Mandatory = $true)]
            [string]$CertThumbprint,
    
            [Parameter(Mandatory = $true)]
            [string]$ExportDirectory,

            [Parameter(Mandatory = $true)]
            [string]$Certname
        )
    
        try {
            Write-EnhancedLog -Message "Starting certificate export process for thumbprint: $CertThumbprint" -Level "INFO"
            
            # Get the certificate from the current user's personal store
            $cert = Get-Item -Path "Cert:\CurrentUser\My\$CertThumbprint"
            if (-not $cert) {
                Write-EnhancedLog -Message "Certificate with thumbprint $CertThumbprint not found." -Level "ERROR"
                throw "Certificate with thumbprint $CertThumbprint not found."
            }
            Write-EnhancedLog -Message "Certificate with thumbprint $CertThumbprint found." -Level "INFO"
    
            # Ensure the export directory exists
            if (-not (Test-Path -Path $ExportDirectory)) {
                Write-EnhancedLog -Message "Export directory $ExportDirectory does not exist. Creating directory." -Level "INFO"
                New-Item -ItemType Directory -Path $ExportDirectory -Force
            }
            Write-EnhancedLog -Message "Using export directory: $ExportDirectory" -Level "INFO"
    
            # Dynamically create a file name using the certificate subject name and current timestamp
            $timestamp = (Get-Date).ToString("yyyyMMddHHmmss")
            # $subjectName = $cert.SubjectName.Name -replace "[^a-zA-Z0-9]", "_"
            # $fileName = "${subjectName}_$timestamp"
            $fileName = "${certname}_$timestamp"
    
            # Set the export file path
            $certPath = Join-Path -Path $ExportDirectory -ChildPath "$fileName.cer"
            Write-EnhancedLog -Message "Export file path set to: $certPath" -Level "INFO"
    
            # Export the certificate to a file (DER encoded binary format with .cer extension)

            # $DBG

            Export-Certificate -Cert $cert -FilePath $certPath -Type CERT -Force | Out-Null
            Write-EnhancedLog -Message "Certificate successfully exported to: $certPath" -Level "INFO"
    
            # Return the export file path
            return $certPath
        }
        catch {
            $errorMessage = $_.Exception.Message
            Write-EnhancedLog -Message "Failed to export certificate: $errorMessage" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }
    


#EndRegion '.\Public\ExportCertificatetoCER.ps1' 103
#Region '.\Public\Get-AppInfoFromJson.ps1' -1

function Get-AppInfoFromJson {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$jsonPath
    )

    Begin {
        Write-EnhancedLog -Message "Starting Get-AppInfoFromJson function" -Level "INFO"
        Log-Params -Params @{ jsonPath = $jsonPath }
    }

    Process {
        try {
            # Check if the file exists
            if (-Not (Test-Path -Path $jsonPath)) {
                Write-Error "The file at path '$jsonPath' does not exist."
                return
            }

            # Read the JSON content from the file
            Write-EnhancedLog -Message "Reading JSON content from file: $jsonPath" -Level "INFO"
            $jsonContent = Get-Content -Path $jsonPath -Raw

            # Check if the JSON content is empty
            if (-Not $jsonContent) {
                Write-EnhancedLog -Message "The JSON content is empty." -Level "ERROR"
                return
            }

            # Convert the JSON content to a PowerShell object
            Write-EnhancedLog -Message "Converting JSON content to PowerShell object" -Level "INFO"
            $appData = ConvertFrom-Json -InputObject $jsonContent

            # Check if the appData is empty or null
            if (-Not $appData) {
                Write-EnhancedLog -Message "The JSON content did not contain any data." -Level "ERROR"
                return
            }

            # Extract the required information
            Write-EnhancedLog -Message "Extracting required information from JSON data" -Level "INFO"
            $extractedData = $appData | ForEach-Object {
                [PSCustomObject]@{
                    Id              = $_.Id
                    DisplayName     = $_.DisplayName
                    AppId           = $_.AppId
                    SignInAudience  = $_.SignInAudience
                    PublisherDomain = $_.PublisherDomain
                }
            }

            # Return the extracted data
            return $extractedData
        } catch {
            Write-EnhancedLog -Message "An error occurred while processing the JSON content: $($_.Exception.Message)" -Level "ERROR"
            Handle-Error -ErrorRecord $_
        }
    }

    End {
        Write-EnhancedLog -Message "Exiting Get-AppInfoFromJson function" -Level "INFO"
    }
}
#EndRegion '.\Public\Get-AppInfoFromJson.ps1' 65
#Region '.\Public\Get-AppName.ps1' -1

# Function to read the application name from app.json and append a timestamp
function Get-AppName {
    param (
        [string]$AppJsonFile
    )

    if (-Not (Test-Path $AppJsonFile)) {
        Write-EnhancedLog -Message "App JSON file not found: $AppJsonFile" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
        throw "App JSON file missing"
    }

    $appConfig = Get-Content -Path $AppJsonFile | ConvertFrom-Json
    $baseAppName = $appConfig.AppName
    $timestamp = (Get-Date).ToString("yyyyMMddHHmmss")
    $uniqueAppName = "$baseAppName-$timestamp"

    Write-EnhancedLog -Message "Generated unique app name: $uniqueAppName" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)
    return $uniqueAppName
}
#EndRegion '.\Public\Get-AppName.ps1' 20
#Region '.\Public\Get-FriendlyNamesForPermissions-Archive.ps1' -1

#need to the test the following first

# function Get-FriendlyNamesForPermissions {
# param (
# [string]$tenantId,
# [string]$clientId,
# [string]$clientSecret,
# [string]$permissionsFile
# )

# # Function to get access token
# function Get-MsGraphAccessToken {
# param (
# [string]$tenantId,
# [string]$clientId,
# [string]$clientSecret
# )

# $body = @{
# grant_type = "client_credentials"
# client_id = $clientId
# client_secret = $clientSecret
# scope = "https://graph.microsoft.com/.default"
# }

# $response = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -ContentType "application/x-www-form-urlencoded" -Body $body
# return $response.access_token
# }

# # Load permissions from the JSON file
# if (Test-Path -Path $permissionsFile) {
# $permissions = Get-Content -Path $permissionsFile | ConvertFrom-Json
# }
# else {
# Write-Error "Permissions file not found: $permissionsFile"
# throw "Permissions file not found: $permissionsFile"
# }

# # Get access token
# $accessToken = Get-MsGraphAccessToken -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret

# # Create header for Graph API requests
# $headers = @{
# Authorization = "Bearer $accessToken"
# }

# # Translate IDs to friendly names
# foreach ($permission in $permissions) {
# $id = $permission.Id
# $url = "https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appRoles/id eq '$id' or oauth2PermissionScopes/id eq '$id'&$select=displayName"
# $response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
# $friendlyName = $response.value[0].displayName
# $permission | Add-Member -MemberType NoteProperty -Name FriendlyName -Value $friendlyName
# }

# return $permissions
# }

# # Example usage
# $tenantId = "your-tenant-id"
# $clientId = "your-client-id"
# $clientSecret = "your-client-secret"
# $permissionsFilePath = Join-Path -Path $PSScriptRoot -ChildPath "permissions.json"

# $friendlyPermissions = Get-FriendlyNamesForPermissions -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret -permissionsFile $permissionsFilePath
# $friendlyPermissions | Format-Table -AutoSize
#EndRegion '.\Public\Get-FriendlyNamesForPermissions-Archive.ps1' 67
#Region '.\Public\Get-FriendlyNamesForPermissions.ps1' -1

#need to the test the following first

function Get-FriendlyNamesForPermissions {
    param (
        [string]$tenantId,
        [string]$clientId,
        [string]$clientSecret,
        [string]$permissionsFile
    )

    # Function to get access token
    function Get-MsGraphAccessToken {
        param (
            [string]$tenantId,
            [string]$clientId,
            [string]$clientSecret
        )

        $body = @{
            grant_type    = "client_credentials"
            client_id     = $clientId
            client_secret = $clientSecret
            scope         = "https://graph.microsoft.com/.default"
        }

        $response = Invoke-RestMethod -Method Post -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -ContentType "application/x-www-form-urlencoded" -Body $body
        return $response.access_token
    }

    # Load permissions from the JSON file
    if (Test-Path -Path $permissionsFile) {
        $permissions = Get-Content -Path $permissionsFile | ConvertFrom-Json
    }
    else {
        Write-Error "Permissions file not found: $permissionsFile"
        throw "Permissions file not found: $permissionsFile"
    }

    # Get access token
    $accessToken = Get-MsGraphAccessToken -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret

    # Create header for Graph API requests
    $headers = @{
        Authorization = "Bearer $accessToken"
    }

    # Translate IDs to friendly names
    foreach ($permission in $permissions) {
        $id = $permission.Id
        $url = "https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appRoles/id eq '$id' or oauth2PermissionScopes/id eq '$id'&$select=displayName"
        $response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
        $friendlyName = $response.value[0].displayName
        $permission | Add-Member -MemberType NoteProperty -Name FriendlyName -Value $friendlyName
    }

    return $permissions
}

# # Example usage
# $tenantId = "your-tenant-id"
# $clientId = "your-client-id"
# $clientSecret = "your-client-secret"
# $permissionsFilePath = Join-Path -Path $PSScriptRoot -ChildPath "permissions.json"

# $friendlyPermissions = Get-FriendlyNamesForPermissions -tenantId $tenantId -clientId $clientId -clientSecret $clientSecret -permissionsFile $permissionsFilePath
# $friendlyPermissions | Format-Table -AutoSize
#EndRegion '.\Public\Get-FriendlyNamesForPermissions.ps1' 67
#Region '.\Public\Get-MsGraphAccessToken.ps1' -1

function Get-MsGraphAccessToken {
    param (
        [string]$tenantId,
        [string]$clientId,
        [string]$clientSecret
    )

    $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"
    $body = @{
        client_id     = $clientId
        scope         = "https://graph.microsoft.com/.default"
        client_secret = $clientSecret
        grant_type    = "client_credentials"
    }

    $httpClient = New-Object System.Net.Http.HttpClient
    $bodyString = ($body.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '&'

    try {
        $content = New-Object System.Net.Http.StringContent($bodyString, [System.Text.Encoding]::UTF8, "application/x-www-form-urlencoded")
        $response = $httpClient.PostAsync($tokenEndpoint, $content).Result

        if (-not $response.IsSuccessStatusCode) {
            Write-EnhancedLog -Message "HTTP request failed with status code: $($response.StatusCode)" -Level "ERROR"
            return $null
        }

        $responseContent = $response.Content.ReadAsStringAsync().Result
        $accessToken = (ConvertFrom-Json $responseContent).access_token

        if ($accessToken) {
            Write-EnhancedLog -Message "Access token retrieved successfully" -Level "INFO"
            return $accessToken
        }
        else {
            Write-EnhancedLog -Message "Failed to retrieve access token, response was successful but no token was found." -Level "ERROR"
            return $null
        }
    }
    catch {
        Write-EnhancedLog -Message "Failed to execute HTTP request or process results: $_" -Level "ERROR"
        return $null
    }
}
#EndRegion '.\Public\Get-MsGraphAccessToken.ps1' 45
#Region '.\Public\Get-MsGraphAccessTokenCert.ps1' -1

function Get-MsGraphAccessTokenCert {
    param (
        [Parameter(Mandatory = $true)]
        [string]$tenantId,
        [Parameter(Mandatory = $true)]
        [string]$clientId,
        [Parameter(Mandatory = $true)]
        [string]$certPath,
        [Parameter(Mandatory = $true)]
        [string]$certPassword
    )

    $tokenEndpoint = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"

    # Load the certificate
    $cert = Load-Certificate -certPath $certPath -certPassword $certPassword

    # Create JWT header
    $jwtHeader = @{
        alg = "RS256"
        typ = "JWT"
        x5t = [Convert]::ToBase64String($cert.GetCertHash())
    }

    $now = [System.DateTime]::UtcNow
    Write-EnhancedLog -Message "Current UTC Time: $now"

    # Get nbf and exp times
    $nbfTime = Get-UnixTime -offsetMinutes -5  # nbf is 5 minutes ago
    $expTime = Get-UnixTime -offsetMinutes 55  # exp is 55 minutes from now

    Write-EnhancedLog -Message "nbf (not before) time: $nbfTime"
    Write-EnhancedLog -Message "exp (expiration) time: $expTime"

    # Create JWT payload
    $jwtPayload = @{
        aud = $tokenEndpoint
        exp = $expTime
        iss = $clientId
        jti = [guid]::NewGuid().ToString()
        nbf = $nbfTime
        sub = $clientId
    }

    Write-EnhancedLog -Message "JWT Payload: $(ConvertTo-Json $jwtPayload -Compress)"

    # Generate JWT assertion
    $clientAssertion = Generate-JWTAssertion -jwtHeader $jwtHeader -jwtPayload $jwtPayload -cert $cert

    # Send token request
    return Send-TokenRequest -tokenEndpoint $tokenEndpoint -clientId $clientId -clientAssertion $clientAssertion
}


# # Example usage of Get-MsGraphAccessTokenCert
# $tenantId = "b5dae566-ad8f-44e1-9929-5669f1dbb343"
# $clientId = "8230c33e-ff30-419c-a1fc-4caf98f069c9"
# $certPath = "C:\Code\appgallery\Intune-Win32-Deployer\apps-winget-repo\PR4B_ExportVPNtoSPO-v1\PR4B-ExportVPNtoSPO-v2\graphcert.pfx"
# $certPassword = "somepassword"
# $accessToken = Get-MsGraphAccessTokenCert -tenantId $tenantId -clientId $clientId -certPath $certPath -certPassword $certPassword
# Write-Host "Access Token: $accessToken"
#EndRegion '.\Public\Get-MsGraphAccessTokenCert.ps1' 62
#Region '.\Public\Get-Secrets.ps1' -1

function Get-Secrets {
    <#
.SYNOPSIS
Loads secrets from a JSON file.
 
.DESCRIPTION
This function reads a JSON file containing secrets and returns an object with these secrets.
 
.PARAMETER SecretsPath
The path to the JSON file containing secrets. If not provided, the default is "secrets.json" in the same directory as the script.
 
.EXAMPLE
$secrets = Get-Secrets -SecretsPath "C:\Path\To\secrets.json"
 
This example loads secrets from the specified JSON file.
 
.NOTES
If the SecretsPath parameter is not provided, the function assumes the JSON file is named "secrets.json" and is located in the same directory as the script.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $false)]
        # [string]$SecretsPath = (Join-Path -Path $PSScriptRoot -ChildPath "secrets.json")
        [string]$SecretsPath
    )

    try {
        Write-EnhancedLog -Message "Attempting to load secrets from path: $SecretsPath" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)

        # Check if the secrets file exists
        if (-not (Test-Path -Path $SecretsPath)) {
            Write-EnhancedLog -Message "Secrets file not found at path: $SecretsPath" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
            throw "Secrets file not found at path: $SecretsPath"
        }

        # Load and parse the secrets file
        $secrets = Get-Content -Path $SecretsPath -Raw | ConvertFrom-Json
        Write-EnhancedLog -Message "Successfully loaded secrets from path: $SecretsPath" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green)
        
        return $secrets
    }
    catch {
        Write-EnhancedLog -Message "Error loading secrets from path: $SecretsPath. Error: $_" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
        throw $_
    }
}
#EndRegion '.\Public\Get-Secrets.ps1' 47
#Region '.\Public\Get-SignInLogs.ps1' -1

function Get-SignInLogs {
    param (
        [string]$url,
        [hashtable]$headers
    )

    $allLogs = @()

    while ($url) {
        try {
            Write-EnhancedLog -Message "Requesting URL: $url" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)
            # Make the API request
            $response = Invoke-WebRequest -Uri $url -Headers $headers -Method Get
            $data = ($response.Content | ConvertFrom-Json)

            # Collect the logs
            $allLogs += $data.value

            # Check for pagination
            $url = $data.'@odata.nextLink'
        } catch {
            Write-EnhancedLog -Message "Error: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
            break
        }
    }

    return $allLogs
}
#EndRegion '.\Public\Get-SignInLogs.ps1' 29
#Region '.\Public\Get-TenantDetails.ps1' -1

function Get-TenantDetails {
    try {
        # Retrieve the organization details
        $organization = Get-MgOrganization

        # Extract the required details
        $tenantName = $organization.DisplayName
        $tenantId = $organization.Id

        # Initialize tenantDomain
        $tenantDomain = $null

        # Search for a verified domain matching the onmicrosoft.com pattern
        foreach ($domain in $organization.VerifiedDomains) {
            if ($domain.Name -match '\.onmicrosoft\.com$') {
                $tenantDomain = $domain.Name
                break
            }
        }

        # Adjust the tenant domain if necessary
        if ($tenantDomain -match '\.mail\.onmicrosoft\.com$') {
            $tenantDomain = $tenantDomain -replace '\.mail\.onmicrosoft\.com$', '.onmicrosoft.com'
        }

        if ($null -eq $tenantDomain) {
            throw "No onmicrosoft.com domain found."
        }

        # Output tenant summary
        Write-EnhancedLog -Message "Tenant Name: $tenantName" -Level "INFO"
        Write-EnhancedLog -Message "Tenant ID: $tenantId" -Level "INFO"
        Write-EnhancedLog -Message "Tenant Domain: $tenantDomain" -Level "INFO"


        # Return the extracted details
        return @{
            TenantName = $tenantName
            TenantId = $tenantId
            TenantDomain = $tenantDomain
        }
    } catch {
        Handle-Error -ErrorRecord $_
        Write-EnhancedLog -Message "Failed to retrieve tenant details" -Level "ERROR"
        return $null
    }
}

# # Example usage
# $tenantDetails = Get-TenantDetails

# if ($null -ne $tenantDetails) {
# $tenantName = $tenantDetails.TenantName
# $tenantId = $tenantDetails.TenantId
# $tenantDomain = $tenantDetails.TenantDomain

# # Use the tenant details as needed
# Write-EnhancedLog -Message "Using Tenant Details outside the function" -Level "INFO"
# Write-EnhancedLog -Message "Tenant Name: $tenantName" -Level "INFO"
# Write-EnhancedLog -Message "Tenant ID: $tenantId" -Level "INFO"
# Write-EnhancedLog -Message "Tenant Domain: $tenantDomain" -Level "INFO"
# } else {
# Write-EnhancedLog -Message "Tenant details could not be retrieved." -Level "ERROR"
# }
#EndRegion '.\Public\Get-TenantDetails.ps1' 65
#Region '.\Public\Grant-AdminConsentToAllPermissions.ps1' -1

# using namespace System.Collections.Generic
# function Grant-AdminConsentToAllPermissions {
# param(
# [string]$AppDisplayName
# )

# $App = Get-MgApplication -Filter "DisplayName eq '$AppDisplayName'"

# $sp = Get-MgServicePrincipal -Filter "AppId eq '$($App.AppId)'"

# # $DBG

# foreach ($resourceAccess in $App.RequiredResourceAccess) {
# $resourceSp = Get-MgServicePrincipal -Filter "AppId eq '$($resourceAccess.ResourceAppId)'"
# if (!$resourceSp) {
# throw "Please cleanup permissions in the Azure portal for the app '$App.AppId', it contains permissions for removed App."
# }
# $scopesIdToValue = @{}
# $resourceSp.PublishedPermissionScopes | ForEach-Object { $scopesIdToValue[$_.Id] = $_.Value }
# [HashSet[string]]$requiredScopes = $resourceAccess.ResourceAccess | ForEach-Object { $scopesIdToValue[$_.Id] }
# $grant = Get-MgOauth2PermissionGrant -Filter "ClientId eq '$($sp.Id)' and ResourceId eq '$($resourceSp.Id)'"
# $newGrantRequired = $true
# if ($grant) {
# [HashSet[string]]$grantedScopes = $grant.Scope.Split(" ")
# if (!$requiredScopes.IsSubsetOf($grantedScopes)) {
# Write-Host "Revoking grant for '$($resourceSp.DisplayName)'"
# Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId $grant.Id
# }
# else {
# $newGrantRequired = $false
# }
# }
# if ($newGrantRequired) {

# $consentExpiry = ([datetime]::Now.AddYears(10))
# $scopesToGrant = $requiredScopes -join " "
# Write-Host "Issuing grant for '$($resourceSp.DisplayName)', scope = $scopesToGrant"
# New-MgOauth2PermissionGrant -ClientId $sp.Id -ConsentType "AllPrincipals" `
# -ResourceId $resourceSp.Id -Scope $scopesToGrant `
# -ExpiryTime $consentExpiry | Out-Null
# }
# }
# }


# # Grant-AdminConsentToAllPermissions -AppDisplayName 'GraphApp-Test001-20240618142134'
#EndRegion '.\Public\Grant-AdminConsentToAllPermissions.ps1' 47
#Region '.\Public\Grant-AdminConsentToApiPermissions.ps1' -1

# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# # [string]$AppId,
# [string]$clientId
# )

# try {



# Create-AndVerifyServicePrincipal -ClientId $clientId

# # $DBG


# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve and list all service principals for debugging
# # $allServicePrincipals = Get-MgServicePrincipal
# # Write-Output "All Service Principals:"
# # $allServicePrincipals | Format-Table DisplayName, AppId, Id

# # Retrieve the service principal for the application
# # $servicePrincipal = $allServicePrincipals | Where-Object { $_.AppId -eq $clientId }

# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# # $DBG

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Retrieve all API permissions (OAuth2PermissionGrants) for the service principal
# $apiPermissions = Get-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $servicePrincipalId

# # $DBG

# Write-EnhancedLog -Message "API permissions retrieved successfully." -Level "INFO"

# # Grant admin consent to each permission
# foreach ($permission in $apiPermissions) {
# Update-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $servicePrincipalId -Id $permission.Id -ConsentType "AllPrincipals"
# }

# Write-EnhancedLog -Message "Admin consent granted to all API permissions." -Level "INFO"
# }
# catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }








# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Retrieve all API permissions (OAuth2PermissionGrants) for the service principal
# $apiPermissions = Get-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $servicePrincipalId

# Write-EnhancedLog -Message "API permissions retrieved: $($apiPermissions.Count)" -Level "INFO"

# if ($apiPermissions.Count -eq 0) {
# Write-EnhancedLog -Message "No API permissions found for the service principal with ID: $servicePrincipalId" -Level "WARNING"
# }

# # Grant admin consent to each permission
# foreach ($permission in $apiPermissions) {
# Write-EnhancedLog -Message "Granting admin consent for permission ID: $($permission.Id) with scope: $($permission.Scope)" -Level "INFO"
# Update-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId $servicePrincipalId -Id $permission.Id -ConsentType "AllPrincipals"
# Write-EnhancedLog -Message "Admin consent granted for permission ID: $($permission.Id) with scope: $($permission.Scope)" -Level "INFO"
# }

        
# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# Example usage
# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"


# Example usage
# Grant-AdminConsentToApiPermissions -AppId "65a3ee49-f480-4cde-9d55-a4a952084bf7"













# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId,
# $headers
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Define the permissions to be granted
# $permissions = "User.Read.All Group.Read.All" # Replace with the required permissions

# # Retrieve the Microsoft Graph service principal
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
# throw "Microsoft Graph service principal not found"
# }

# $resourceId = $graphServicePrincipal.Id

# # Grant the permissions
# $body = @{
# clientId = $servicePrincipalId
# consentType = "AllPrincipals"
# resourceId = $resourceId
# scope = $permissions
# }

# # $headers = @{
# # "Authorization" = "Bearer $AccessToken"
# # "Content-Type" = "application/json"
# # }

# $response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" -Method POST -Headers $headers -Body ($body | ConvertTo-Json)

# Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# Example usage
# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"




# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Define the permissions to be granted
# $permissions = "User.Read.All Group.Read.All" # Replace with the required permissions

# # Retrieve the Microsoft Graph service principal
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
# throw "Microsoft Graph service principal not found"
# }

# $resourceId = $graphServicePrincipal.Id

# # Grant the permissions
# $body = @{
# clientId = $servicePrincipalId
# consentType = "AllPrincipals"
# resourceId = $resourceId
# scope = $permissions
# }

# $response = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json"

# $DBG

# Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# # Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"




# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Define the permissions to be granted (application permissions)
# $permissions = @("Directory.Read.All", "Directory.ReadWrite.All") # Replace with the required permissions

# # Retrieve the Microsoft Graph service principal
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id,appRoles

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
# throw "Microsoft Graph service principal not found"
# }

# $resourceId = $graphServicePrincipal.Id
# $appRoles = $graphServicePrincipal.AppRoles

# # Find the IDs of the required permissions
# $requiredRoles = $appRoles | Where-Object { $permissions -contains $_.Value } | Select-Object -ExpandProperty Id

# if ($requiredRoles.Count -eq 0) {
# Write-EnhancedLog -Message "No matching app roles found for the specified permissions." -Level "ERROR"
# throw "No matching app roles found"
# }

# Write-EnhancedLog -Message "App roles to be granted: $($requiredRoles -join ', ')" -Level "INFO"

# # Grant the app roles (application permissions)
# foreach ($roleId in $requiredRoles) {
# $body = @{
# principalId = $servicePrincipalId
# resourceId = $resourceId
# appRoleId = $roleId
# }

# $response = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$resourceId/appRoleAssignedTo" -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json"

            

# Write-EnhancedLog -Message "Granted app role with ID: $roleId" -Level "INFO"
# }

# $DBG

# Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"







# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId,
# [Parameter(Mandatory = $true)]
# [string]$SPPermissionsPath
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Load permissions from JSON file
# $permissionsFile = Join-Path -Path $SPPermissionsPath -ChildPath "SPPermissions.json"
# if (-not (Test-Path -Path $permissionsFile)) {
# Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
# throw "Permissions file not found"
# }

# $permissionsJson = Get-Content -Path $permissionsFile -Raw | ConvertFrom-Json
# $permissions = $permissionsJson.permissions | Where-Object { $_.granted -eq $true } | Select-Object -ExpandProperty name

# Write-EnhancedLog -Message "Permissions to be granted: $($permissions -join ', ')" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Retrieve the Microsoft Graph service principal
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id,appRoles

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
# throw "Microsoft Graph service principal not found"
# }

# $resourceId = $graphServicePrincipal.Id
# $appRoles = $graphServicePrincipal.AppRoles

# # Find the IDs of the required permissions
# $requiredRoles = $appRoles | Where-Object { $permissions -contains $_.Value } | Select-Object -ExpandProperty Id

# if ($requiredRoles.Count -eq 0) {
# Write-EnhancedLog -Message "No matching app roles found for the specified permissions." -Level "ERROR"
# throw "No matching app roles found"
# }

# Write-EnhancedLog -Message "App roles to be granted: $($requiredRoles -join ', ')" -Level "INFO"

# # Ensure the access token has the necessary scopes
# $context = Get-MgContext -ErrorAction Stop
# $token = $context.AuthContext.AccessToken
# $decodedToken = $token | ConvertFrom-Json -ErrorAction Stop
# $tokenScopes = $decodedToken.scp.Split(' ')

# if (-not $tokenScopes -contains "AppRoleAssignment.ReadWrite.All") {
# Write-EnhancedLog -Message "Access token does not contain the necessary scope: AppRoleAssignment.ReadWrite.All" -Level "ERROR"
# throw "Insufficient privileges"
# }

# # $DBG

# # Grant the app roles (application permissions)
# foreach ($roleId in $requiredRoles) {
# $body = @{
# principalId = $servicePrincipalId
# resourceId = $resourceId
# appRoleId = $roleId
# }

# $response = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$resourceId/appRoleAssignedTo" -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json"

# Write-EnhancedLog -Message "Granted app role with ID: $roleId" -Level "INFO"
# }

# Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All", "AppRoleAssignment.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"







# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Load permissions from JSON file
# $permissionsFile = Join-Path -Path $SPPermissionsPath -ChildPath "SPPermissions.json"
# if (-not (Test-Path -Path $permissionsFile)) {
# Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
# throw "Permissions file not found"
# }

# $permissionsJson = Get-Content -Path $permissionsFile -Raw | ConvertFrom-Json
# $permissions = $permissionsJson.permissions | Where-Object { $_.granted -eq $true } | Select-Object -ExpandProperty name

# Write-EnhancedLog -Message "Permissions to be granted: $($permissions -join ', ')" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Retrieve the Microsoft Graph service principal
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id,appRoles

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
# throw "Microsoft Graph service principal not found"
# }

# $resourceId = $graphServicePrincipal.Id
# $appRoles = $graphServicePrincipal.AppRoles

# # Find the IDs of the required permissions
# $requiredRoles = $appRoles | Where-Object { $permissions -contains $_.Value } | Select-Object -ExpandProperty Id

# if ($requiredRoles.Count -eq 0) {
# Write-EnhancedLog -Message "No matching app roles found for the specified permissions." -Level "ERROR"
# throw "No matching app roles found"
# }

# Write-EnhancedLog -Message "App roles to be granted: $($requiredRoles -join ', ')" -Level "INFO"

# # # Ensure the access token has the necessary scopes
# # $context = Get-MgContext -ErrorAction Stop
# # if ($null -eq $context) {
# # Write-EnhancedLog -Message "Microsoft Graph context is null." -Level "ERROR"
# # throw "Microsoft Graph context is null"
# # }

# # $token = $context.AuthContext.AccessToken
# # if ($null -eq $token) {
# # Write-EnhancedLog -Message "Access token is null." -Level "ERROR"
# # throw "Access token is null"
# # }

# # $decodedToken = $token | ConvertFrom-Json -ErrorAction Stop
# # $tokenScopes = $decodedToken.scp.Split(' ')

# # if (-not $tokenScopes -contains "AppRoleAssignment.ReadWrite.All") {
# # Write-EnhancedLog -Message "Access token does not contain the necessary scope: AppRoleAssignment.ReadWrite.All" -Level "ERROR"
# # throw "Insufficient privileges"
# # }

# # Grant the app roles (application permissions)
# foreach ($roleId in $requiredRoles) {
# $body = @{
# principalId = $servicePrincipalId
# resourceId = $resourceId
# appRoleId = $roleId
# }

# $response = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$resourceId/appRoleAssignedTo" -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json"

# Write-EnhancedLog -Message "Granted app role with ID: $roleId" -Level "INFO"
# }

# Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# # Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All", "AppRoleAssignment.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"








# function Grant-AdminConsentToApiPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$clientId,
# $SPPermissionsPath
# )

# try {
# Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Load permissions from JSON file
# $permissionsFile = Join-Path -Path $SPPermissionsPath -ChildPath "SPPermissions.json"
# if (-not (Test-Path -Path $permissionsFile)) {
# Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
# throw "Permissions file not found"
# }

# $permissionsJson = Get-Content -Path $permissionsFile -Raw | ConvertFrom-Json
# $permissions = $permissionsJson.permissions | Where-Object { $_.granted -eq $true } | Select-Object -ExpandProperty name

# Write-EnhancedLog -Message "Permissions to be granted: $($permissions -join ', ')" -Level "INFO"

# # Create and verify the service principal
# Create-AndVerifyServicePrincipal -ClientId $clientId

# Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

# # Retrieve the service principal for the application
# $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

# if ($null -eq $servicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

# # Retrieve the service principal ID
# $servicePrincipalId = $servicePrincipal.Id

# # Retrieve the Microsoft Graph service principal
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id,appRoles

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
# throw "Microsoft Graph service principal not found"
# }

# $resourceId = $graphServicePrincipal.Id
# $appRoles = $graphServicePrincipal.AppRoles

# # Find the IDs of the required permissions
# $requiredRoles = $appRoles | Where-Object { $permissions -contains $_.Value } | Select-Object -ExpandProperty Id

# if ($requiredRoles.Count -eq 0) {
# Write-EnhancedLog -Message "No matching app roles found for the specified permissions." -Level "ERROR"
# throw "No matching app roles found"
# }

# Write-EnhancedLog -Message "App roles to be granted: $($requiredRoles -join ', ')" -Level "INFO"

# # Grant the app roles (application permissions)
# foreach ($roleId in $requiredRoles) {
# $body = @{
# principalId = $servicePrincipalId
# resourceId = $resourceId
# appRoleId = $roleId
# }

# $response = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$resourceId/appRoleAssignedTo" -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json"

# Write-EnhancedLog -Message "Granted app role with ID: $roleId" -Level "INFO"
# }

# # $DBG

# Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# # Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All", "AppRoleAssignment.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"










function Grant-AdminConsentToApiPermissions {
    param (
        [Parameter(Mandatory = $true)]
        [string]$clientId,

        [Parameter(Mandatory = $true)]
        [string]$SPPermissionsPath
    )

    try {
        Write-EnhancedLog -Message "Starting the process to grant admin consent to API permissions for App ID: $clientId" -Level "INFO"

        # Load permissions from JSON file
        $permissionsFile = Join-Path -Path $SPPermissionsPath -ChildPath "SPPermissions.json"
        if (-not (Test-Path -Path $permissionsFile)) {
            Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
            throw "Permissions file not found"
        }

        $permissionsJson = Get-Content -Path $permissionsFile -Raw | ConvertFrom-Json
        $permissions = $permissionsJson.permissions | Where-Object { $_.granted -eq $true } | Select-Object -ExpandProperty name

        Write-EnhancedLog -Message "Permissions to be granted: $($permissions -join ', ')" -Level "INFO"

        # Create and verify the service principal
        Create-AndVerifyServicePrincipal -ClientId $clientId

        Write-EnhancedLog -Message "Granting admin consent to API permissions for App ID: $clientId" -Level "INFO"

        # Retrieve the service principal for the application
        $servicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$clientId'"

        if ($null -eq $servicePrincipal) {
            Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
            throw "Service principal not found"
        }

        Write-EnhancedLog -Message "Service principal for app ID: $clientId retrieved successfully." -Level "INFO"

        # Retrieve the service principal ID
        $servicePrincipalId = $servicePrincipal.Id

        # Retrieve the Microsoft Graph service principal
        $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id,appRoles

        if ($null -eq $graphServicePrincipal) {
            Write-EnhancedLog -Message "Microsoft Graph service principal not found." -Level "ERROR"
            throw "Microsoft Graph service principal not found"
        }

        $resourceId = $graphServicePrincipal.Id
        $appRoles = $graphServicePrincipal.AppRoles

        # Find the IDs of the required permissions
        $requiredRoles = $appRoles | Where-Object { $permissions -contains $_.Value } | Select-Object Id, Value

        if ($requiredRoles.Count -eq 0) {
            Write-EnhancedLog -Message "No matching app roles found for the specified permissions." -Level "ERROR"
            throw "No matching app roles found"
        }

        Write-EnhancedLog -Message "App roles to be granted: $($requiredRoles.Value -join ', ')" -Level "INFO"

        # Grant the app roles (application permissions)
        foreach ($role in $requiredRoles) {
            $body = @{
                principalId = $servicePrincipalId
                resourceId  = $resourceId
                appRoleId   = $role.Id
            }

            $response = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$resourceId/appRoleAssignedTo" -Method POST -Body ($body | ConvertTo-Json) -ContentType "application/json"

            Write-EnhancedLog -Message "Granted app role: $($role.Value) with ID: $($role.Id)" -Level "INFO"
        }
        # $DBG

        Write-EnhancedLog -Message "Admin consent granted successfully." -Level "INFO"
        return $response

    } catch {
        Write-EnhancedLog -Message "An error occurred while granting admin consent." -Level "ERROR"
        Handle-Error -ErrorRecord $_ 
        throw $_
    }
}

# # Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All", "AppRoleAssignment.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Grant-AdminConsentToApiPermissions -ClientId "your-application-id"








#EndRegion '.\Public\Grant-AdminConsentToApiPermissions.ps1' 800
#Region '.\Public\Grant-AdminConsentToDelegatedPermissions.ps1' -1

# function Grant-AdminConsentToDelegatedPermissions {
# param (
# [Parameter(Mandatory = $true)]
# [string]$AppId,
# [Parameter(Mandatory = $true)]
# [string]$Permissions,
# [Parameter(Mandatory = $true)]
# [string]$AccessToken
# )

# try {
# Write-EnhancedLog -Message "Granting tenant-wide admin consent to API permissions for App ID: $AppId" -Level "INFO"

# # Retrieve the service principal for Microsoft Graph
# $graphServicePrincipal = Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Select id,displayName,appId,oauth2PermissionScopes

# if ($null -eq $graphServicePrincipal) {
# Write-EnhancedLog -Message "Service principal for Microsoft Graph not found." -Level "ERROR"
# throw "Service principal for Microsoft Graph not found"
# }

# Write-EnhancedLog -Message "Microsoft Graph service principal retrieved successfully." -Level "INFO"

# # Retrieve the service principal for the client application
# $clientServicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$AppId'"

# if ($null -eq $clientServicePrincipal) {
# Write-EnhancedLog -Message "Service principal not found for the specified application ID." -Level "ERROR"
# throw "Service principal not found"
# }

# Write-EnhancedLog -Message "Service principal for client application retrieved successfully." -Level "INFO"

# # Grant the delegated permissions to the client enterprise application
# $body = @{
# clientId = $clientServicePrincipal.Id
# consentType = "AllPrincipals"
# resourceId = $graphServicePrincipal.Id
# scope = $Permissions
# }

# $headers = @{
# "Authorization" = "Bearer $AccessToken"
# "Content-Type" = "application/json"
# }

# $response = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" -Method POST -Headers $headers -Body ($body | ConvertTo-Json)

# Write-EnhancedLog -Message "Tenant-wide admin consent granted successfully." -Level "INFO"

# return $response

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting tenant-wide admin consent." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# # Example usage
# # $accessToken = "your-access-token"
# # Grant-AdminConsentToDelegatedPermissions -AppId "08216f27-1d3d-4a9f-9406-80f957e7fca6" -Permissions "User.Read.All Group.Read.All" -AccessToken $accessToken
#EndRegion '.\Public\Grant-AdminConsentToDelegatedPermissions.ps1' 63
#Region '.\Public\Grant-AdminConsentUsingAzCli.ps1' -1

# function Grant-AdminConsentUsingAzCli {
# param (
# [Parameter(Mandatory = $true)]
# [string]$AppId
# )

# try {
# Write-EnhancedLog -Message "Granting admin consent to Azure AD application with App ID: $AppId using Azure CLI" -Level "INFO"



# #First download and install Az CLI

# # $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; Remove-Item .\AzureCLI.msi


# # Run the following to fix the issue mentioned here https://github.com/Azure/azure-cli/issues/28997
# az account clear
# az config set core.enable_broker_on_windows=false

# # Now login (it will open up a web browser window to login as normal)
# #If you have an Azure Subscription
# # az login


# #If you DO NOT have an Azure Subscription
# az login --allow-no-subscriptions

# # $DBG

# # Execute the Azure CLI command to grant admin consent
# $azCliCommand = "az ad app permission admin-consent --id $AppId"
# $output = Invoke-Expression -Command $azCliCommand

# # $DBG

# if ($output -match "deprecated") {
# Write-EnhancedLog -Message "The 'admin-consent' command is deprecated. Use 'az ad app permission grant' instead." -Level "WARNING"
# }

# Write-EnhancedLog -Message "Admin consent granted successfully using Azure CLI." -Level "INFO"

# az logout

# return $output

# } catch {
# Write-EnhancedLog -Message "An error occurred while granting admin consent using Azure CLI." -Level "ERROR"
# Handle-Error -ErrorRecord $_
# throw $_
# }
# }

# # Example usage
# # Grant-AdminConsentUsingAzCli -AppId "your-application-id"



# #Readme
# # https://github.com/Azure/azure-cli/issues/28997

# # Solution: (After installing azure-cli-2.61.0-x64.msi the az command will become available through the PATH ENV Variable located in C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin now to fix the az login issue mentioned follow these steps carefully

# # Step 1: open CMD or PoweShell 5 or 7 (in VS Code, Terminal or normal Shell) AS Admin
# # Step 2: Run the following command az account clear (you will get nothing back)
# # Step 3: Run the following command az config set core.enable_broker_on_windows=false (you will get "A web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. Please continue the login in the web browser. If no web browser is available or if the web browser fails to open, use device code flow with az login --use-device-code.") and then a web login page will open
# # Step4: Once you login it will say in the webpage

# # You have logged into Microsoft Azure!
# # You can close this window, or we will redirect you to the Azure CLI documentation in 1 minute.



# #Example Output


# # C:\Users\Administrator>az account clear

# # C:\Users\Administrator>az login
# # Please select the account you want to log in with.
# # User cancelled the Accounts Control Operation.. Status: Response_Status.Status_UserCanceled, Error code: 0, Tag: 528315210
# # Please explicitly log in with:
# # az login

# # C:\Users\Administrator>az account clear

# # C:\Users\Administrator>az config set core.enable_broker_on_windows=false
# # Command group 'config' is experimental and under development. Reference and support levels: https://aka.ms/CLI_refstatus

# # C:\Users\Administrator>az login
# # A web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. Please continue the login in the web browser. If no web browser is available or if the web browser fails to open, use device code flow with `az login --use-device-code`.

# # Retrieving tenants and subscriptions for the selection...
# # The following tenants don't contain accessible subscriptions. Use `az login --allow-no-subscriptions` to have tenant level access.
# # 5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50 'CASN'
# # No subscriptions found for NovaAdmin_AOllivierre@casn.ca.

# # C:\Users\Administrator>






# # az login --allow-no-subscriptions in pwsh at 05:40:25
# # A web browser has been opened at https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize. Please continue the login in the web browser. If no web browser is available or if the web browser fails to open, use device code flow with `az login --use-device-code`.

# # Retrieving tenants and subscriptions for the selection...
# # The following tenants don't contain accessible subscriptions. Use `az login --allow-no-subscriptions` to have tenant level access.
# # 5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50 'CASN'

# # [Tenant and subscription selection]

# # No Subscription name Subscription ID Tenant
# # ----- ------------------------- ------------------------------------ ------------------------------------
# # [1] * N/A(tenant level account) 5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50 5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50

# # The default is marked with an *; the default tenant is '5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50' and subscription is 'N/A(tenant level account)' (5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50).

# # Select a subscription and tenant (Type a number or Enter for no changes): 1

# # Tenant: 5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50
# # Subscription: N/A(tenant level account) (5784dc53-9279-4dc4-8b3d-cf6e8d4f9c50)

# # [Announcements]
# # With the new Azure CLI login experience, you can select the subscription you want to use more easily. Learn more about it and its configuration at https://go.microsoft.com/fwlink/?linkid=2271236

# # If you encounter any problem, please open an issue at https://aka.ms/azclibug

# # [Warning] The login output has been updated. Please be aware that it no longer displays the full list of available subscriptions by default.
#EndRegion '.\Public\Grant-AdminConsentUsingAzCli.ps1' 131
#Region '.\Public\Import-CertificateIfNotExist.ps1' -1

function Import-CertificateIfNotExist {
    param (
        [Parameter(Mandatory = $true)]
        [string]$CertPath,
        
        [Parameter(Mandatory = $true)]
        [string]$CertPassword
    )

    try {
        Write-EnhancedLog -Message "Starting certificate import process." -Level "INFO"
        
        # Load the PFX file using the constructor
        $securePassword = ConvertTo-SecureString -String $CertPassword -AsPlainText -Force
        $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($CertPath, $securePassword, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet)

        # Check if the certificate already exists in the local machine store
        $store = New-Object System.Security.Cryptography.X509Certificates.X509Store("My", "LocalMachine")
        $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly)
        $existingCert = $store.Certificates | Where-Object { $_.Thumbprint -eq $cert.Thumbprint }
        $store.Close()

        if ($existingCert) {
            Write-EnhancedLog -Message "Certificate already exists in the local machine store." -Level "INFO"
        } else {
            Write-EnhancedLog -Message "Certificate does not exist in the local machine store. Importing certificate..." -Level "INFO"

            # Open the store with write access and add the certificate
            $store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)
            $store.Add($cert)
            $store.Close()

            Write-EnhancedLog -Message "Certificate imported successfully." -Level "INFO"
        }
    } catch {
        Write-EnhancedLog -Message "An error occurred during the certificate import process." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}

# Example usage
# $params = @{
# CertPath = "C:\Path\To\Your\Certificate.pfx"
# CertPassword = "YourPfxPassword"
# }

# Import-CertificateIfNotExist @params
#EndRegion '.\Public\Import-CertificateIfNotExist.ps1' 49
#Region '.\Public\Invoke-EnhancedGraphAORequest.ps1' -1

function Invoke-EnhancedGraphAORequest {
    param (
        [string]$Method,
        [string]$Uri,
        [string]$AccessToken,
        [string]$Body = $null
    )

    $httpClient = New-Object System.Net.Http.HttpClient
    $httpClient.DefaultRequestHeaders.Authorization = New-Object System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", $AccessToken)

    try {
        if ($Body) {
            $content = New-Object System.Net.Http.StringContent($Body, [System.Text.Encoding]::UTF8, "application/json")
        }

        $response = $null
        switch ($Method) {
            "GET" { $response = $httpClient.GetAsync($Uri).Result }
            "POST" { $response = $httpClient.PostAsync($Uri, $content).Result }
            "PATCH" { $response = $httpClient.PatchAsync($Uri, $content).Result }
            "DELETE" { $response = $httpClient.DeleteAsync($Uri).Result }
        }

        if (-not $response) {
            throw "No response received from the server."
        }

        $responseContent = $response.Content.ReadAsStringAsync().Result
        $responseStatus = $response.IsSuccessStatusCode

        # Define the directory for response JSON files
        $responseDir = Join-Path -Path $PSScriptRoot -ChildPath "responses"
        if (-not (Test-Path -Path $responseDir)) {
            New-Item -ItemType Directory -Path $responseDir
        }

        # Log the full request and response in JSON format
        $logEntry = @{
            RequestUri    = $Uri
            RequestMethod = $Method
            RequestBody   = $Body
            Response      = $responseContent
            IsSuccess     = $responseStatus
            TimeStamp     = Get-Date -Format "yyyyMMddHHmmssfff"
        }

        $logFile = Join-Path -Path $responseDir -ChildPath ("Response_$($logEntry.TimeStamp).json")
        $logEntry | ConvertTo-Json | Set-Content -Path $logFile

        Write-EnhancedLog -Message "Response logged to $logFile" -Level "INFO"

        if ($response.IsSuccessStatusCode) {
            Write-EnhancedLog -Message "Successfully executed $Method request to $Uri." -Level "INFO"
            return $responseContent
        }
        else {
            $errorContent = $responseContent
            Write-EnhancedLog -Message "HTTP request failed with status code: $($response.StatusCode). Error content: $errorContent" -Level "ERROR"
            return $null
        }
    }
    catch {
        Write-EnhancedLog -Message "Failed to execute $Method request to $Uri $_" -Level "ERROR"
        return $null
    }
    finally {
        $httpClient.Dispose()
    }
}
#EndRegion '.\Public\Invoke-EnhancedGraphAORequest.ps1' 71
#Region '.\Public\Load-Certificate.ps1' -1

function Load-Certificate {
    param (
        [Parameter(Mandatory = $true)]
        [string]$CertPath,

        [Parameter(Mandatory = $true)]
        [string]$CertPassword
    )

    try {
        Write-EnhancedLog -Message "Attempting to load certificate from path: $CertPath" -Level "INFO"

        # Validate certificate path before loading
        $certExistsBefore = Validate-Certificate -CertPath $CertPath
        if (-not $certExistsBefore) {
            throw "Certificate path does not exist: $CertPath"
        }

        # Check the OS and convert the certificate path if running on Linux
        if ($PSVersionTable.PSVersion.Major -ge 7) {
            if ($PSVersionTable.Platform -eq 'Unix') {
                $CertPath = Convert-WindowsPathToLinuxPath -WindowsPath $CertPath
            }
        } else {
            $os = [System.Environment]::OSVersion.Platform
            if ($os -eq [System.PlatformID]::Unix) {
                $CertPath = Convert-WindowsPathToLinuxPath -WindowsPath $CertPath
            }
        }

        # Load the certificate directly from the file
        $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertPath, $CertPassword)
        
        Write-EnhancedLog -Message "Successfully loaded certificate from path: $CertPath" -Level "INFO"

        # Validate certificate path after loading
        $certExistsAfter = Validate-Certificate -CertPath $CertPath
        if ($certExistsAfter) {
            Write-EnhancedLog -Message "Certificate path still exists after loading: $CertPath" -Level "INFO"
        } else {
            Write-EnhancedLog -Message "Certificate path does not exist after loading: $CertPath" -Level "WARNING"
        }

        return $cert
    }
    catch {
        Write-EnhancedLog -Message "Error loading certificate from path: $CertPath. Error: $_" -Level "ERROR"
        throw $_
    }
}
#EndRegion '.\Public\Load-Certificate.ps1' 51
#Region '.\Public\Open-CertificateStore.ps1' -1

function Open-CertificateStore {
    if (Is-ServerCore) {
        Write-Output "Running on Windows Server Core. Skipping opening of certificate manager."
    } else {
        # Open the certificate store
        Start-Process certmgr.msc
    }
}
#EndRegion '.\Public\Open-CertificateStore.ps1' 9
#Region '.\Public\Output-secrets.ps1' -1

function Output-Secrets {
    param (
        [Parameter(Mandatory = $false)]
        [string]$AppDisplayName,
        
        [Parameter(Mandatory = $false)]
        [string]$ApplicationID,
        
        [Parameter(Mandatory = $false)]
        [string]$Thumbprint,
        
        [Parameter(Mandatory = $false)]
        [string]$TenantID,
        
        [Parameter(Mandatory = $false)]
        [string]$SecretsFile,
        
        [Parameter(Mandatory = $false)]
        [string]$CertPassword,
        
        [Parameter(Mandatory = $false)]
        [string]$CertName,
        
        [Parameter(Mandatory = $false)]
        [string]$TenantName,
        
        [Parameter(Mandatory = $false)]
        [string]$TenantDomainName,
        
        [Parameter(Mandatory = $false)]
        [string]$OutputPath
    )

    try {
        Write-EnhancedLog -Message "Starting to output secrets." -Level "INFO"
        
        $secrets = @{
            AppDisplayName   = $AppDisplayName
            ClientId         = $ApplicationID
            Thumbprint       = $Thumbprint
            TenantID         = $TenantID
            CertPassword     = $CertPassword
            CertName         = $CertName
            TenantName       = $TenantName
            TenantDomainName = $TenantDomainName
            OutputPath       = $OutputPath
        }

        $secrets | ConvertTo-Json | Set-Content -Path $SecretsFile

        Write-EnhancedLog -Message "Secrets have been written to file: $SecretsFile" -Level "INFO"

        Write-Host "================ Secrets ================"
        Write-Host "`$AppDisplayName = $($AppDisplayName)"
        Write-Host "`$ClientId = $($ApplicationID)"
        Write-Host "`$Thumbprint = $($Thumbprint)"
        Write-Host "`$TenantID = $TenantID"
        Write-Host "`$CertPassword = $CertPassword"
        Write-Host "`$CertName = $CertName"
        Write-Host "`$TenantName = $TenantName"
        Write-Host "`$TenantDomainName = $TenantDomainName"
        Write-Host "`$OutputPath = $OutputPath"
        Write-Host "================ Secrets ================"
        Write-Host " SAVE THESE IN A SECURE LOCATION "

        Write-EnhancedLog -Message "Secrets have been output to the console." -Level "INFO"

    } catch {
        Write-EnhancedLog -Message "An error occurred while outputting secrets." -Level "ERROR"
        Handle-Error -ErrorRecord $_
        throw $_
    }
}

# # Example usage
# $params = @{
# AppDisplayName = $app.DisplayName
# ApplicationID = $app.AppId
# TenantID = $tenantDetails.Id
# SecretsFile = $secretsfile
# CertName = $Certname
# Thumbprint = $thumbprint
# CertPassword = $CertPassword
# TenantName = $tenantDetails.DisplayName
# TenantDomainName = $tenantDetails.DomainName
# OutputPath = $certexportDirectory
# }

# Output-Secrets @params
#EndRegion '.\Public\Output-secrets.ps1' 90
#Region '.\Public\Remove-AppListJson.ps1' -1

function Remove-AppListJson {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$JsonPath
    )

    begin {
        Write-EnhancedLog -Message "Starting Remove-AppListJson function" -Level "INFO"
        Log-Params -Params $PSCmdlet.MyInvocation.BoundParameters
    }

    process {
        try {
            # Check if the file exists
            if (Test-Path -Path $JsonPath) {
                # Remove the file
                $removeParams = @{
                    Path  = $JsonPath
                    Force = $true
                }
                Remove-Item @removeParams
                Write-EnhancedLog -Message "The applist.json file has been removed successfully." -Level "INFO"
            }
            else {
                Write-EnhancedLog -Message "The file at path '$JsonPath' does not exist." -Level "WARNING"
            }
        }
        catch {
            Write-EnhancedLog -Message "An error occurred while removing the file: $_" -Level "ERROR"
            Handle-Error -ErrorRecord $_
            throw $_
        }
    }

    end {
        Write-EnhancedLog -Message "Remove-AppListJson function execution completed." -Level "INFO"
    }
}

# Example usage
# Remove-AppListJson -JsonPath "C:\Path\To\Your\applist.json"
#EndRegion '.\Public\Remove-AppListJson.ps1' 43
#Region '.\Public\Remove-AppRegistrationsAndDeletedItems.ps1' -1

function Remove-AppRegistrationsAndDeletedItems {
    param (
        [Parameter(Mandatory = $true)]
        [string]$AppDisplayNamePattern
    )

    try {
        Write-EnhancedLog -Message "Starting cleanup process for app registrations with display names like: $AppDisplayNamePattern" -Level "INFO"

        # Retrieve all applications with the specified display name pattern
        $apps = Get-MgApplication -Filter "startswith(DisplayName,'graphapp-test')"

        if ($apps.Count -eq 0) {
            Write-EnhancedLog -Message "No applications found with display names like: $AppDisplayNamePattern" -Level "WARNING"
            return
        }

        Write-EnhancedLog -Message "Applications to be deleted: $($apps.DisplayName -join ', ')" -Level "INFO"

        # Remove each application
        foreach ($app in $apps) {
            Remove-MgApplication -ApplicationId $app.Id -Confirm:$false
            Write-EnhancedLog -Message "Deleted application: $($app.DisplayName) with ID: $($app.Id)" -Level "INFO"
        }

        Write-EnhancedLog -Message "Cleanup process completed successfully." -Level "INFO"
    } catch {
        Write-EnhancedLog -Message "An error occurred during the cleanup process." -Level "ERROR"
        Handle-Error -ErrorRecord $_ 
        throw $_
    }
}

# # Example usage
# $scopes = @("Application.ReadWrite.All", "Directory.ReadWrite.All")
# Connect-MgGraph -Scopes $scopes

# Remove-AppRegistrationsAndDeletedItems -AppDisplayNamePattern "*graphapp-test*"
#EndRegion '.\Public\Remove-AppRegistrationsAndDeletedItems.ps1' 39
#Region '.\Public\Remove-MGApplication-Run-InterActivefromConsole.ps1' -1

# # Import the module
# Import-Module Microsoft.Graph.Applications

# # Connect to Microsoft Graph
# Connect-MgGraph -Scopes "Application.ReadWrite.All"

# # Get and remove applications starting with 'GraphApp-Test001'
# Get-MgApplication -Filter "startswith(displayName, 'GraphApp-Test001')" | ForEach-Object {
# Remove-MgApplication -ApplicationId $_.Id -Confirm:$false
# }

# # Disconnect the session
# Disconnect-MgGraph
#EndRegion '.\Public\Remove-MGApplication-Run-InterActivefromConsole.ps1' 14
#Region '.\Public\Run-DumpAppListToJSON.ps1' -1

#the following function work in GUI mode but not in containers and honestly the idea of it having to start a PowerShell session is not vert appealing sure we start seperate powershell sessions all of the time but we need to figure out a way to make it work inside of containers


# function Run-DumpAppListToJSON {
# [CmdletBinding()]
# param (
# [Parameter(Mandatory = $true)]
# [string]$JsonPath
# )

# $scriptContent = @"
# function Dump-AppListToJSON {
# param (
# [string]`$JsonPath
# )

# try {
# Disconnect-MgGraph

# # Connect to Graph interactively
# Connect-MgGraph -Scopes 'Application.ReadWrite.All' -ErrorAction Stop

# # Retrieve all application objects
# `$allApps = Get-MgApplication -ErrorAction Stop

# # Export to JSON
# `$allApps | ConvertTo-Json -Depth 10 | Out-File -FilePath `$JsonPath -ErrorAction Stop

# Write-Host "Application list successfully dumped to JSON at: `$JsonPath"

# } catch {
# Write-Error "An error occurred in Dump-AppListToJSON: `$_"
# $_ | Format-List -Property Message, Exception, ScriptStackTrace
# throw
# } finally {
# Disconnect-MgGraph
# }
# }

# # Dump application list to JSON
# Dump-AppListToJSON -JsonPath `"$JsonPath`"
# "@

# try {
# # Create a temporary file in the temp directory
# $tempScriptPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName() + ".ps1")
# Set-Content -Path $tempScriptPath -Value $scriptContent -ErrorAction Stop

# # Start a new PowerShell session to run the script and wait for it to complete
# $startProcessParams = @{
# FilePath = "pwsh"
# ArgumentList = @("-NoProfile", "-NoLogo", "-File", $tempScriptPath)
# PassThru = $true
# ErrorAction = "Stop"
# }
# $process = Start-Process @startProcessParams
# $process.WaitForExit()

# if ($process.ExitCode -ne 0) {
# Write-Error "The PowerShell script process exited with code $($process.ExitCode)."
# throw "The PowerShell script process exited with an error."
# }

# Write-Host "Script executed successfully."

# } catch {
# Write-Error "An error occurred in Run-DumpAppListToJSON: $_"
# $_ | Format-List -Property Message, Exception, ScriptStackTrace
# throw
# } finally {
# # Remove the temporary script file after execution
# if (Test-Path -Path $tempScriptPath) {
# Remove-Item -Path $tempScriptPath -ErrorAction Stop
# }
# }
# }

# # Example usage
# # Run-DumpAppListToJSON -JsonPath "C:\Path\To\Your\Output.json"
#EndRegion '.\Public\Run-DumpAppListToJSON.ps1' 80
#Region '.\Public\Update-ApplicationPermissions.ps1' -1

# function Update-ApplicationPermissions {
# param (
# [string]$appId,
# [string]$permissionsFile
# )

# $resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph

# # Load permissions from the JSON file
# if (Test-Path -Path $permissionsFile) {
# $permissions = Get-Content -Path $permissionsFile | ConvertFrom-Json
# }
# else {
# Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
# throw "Permissions file not found: $permissionsFile"
# }

# # Retrieve the existing application (optional, uncomment if needed)
# # $app = Get-MgApplication -ApplicationId $appId

# # Prepare the required resource access
# $requiredResourceAccess = @(
# @{
# ResourceAppId = $resourceAppId
# ResourceAccess = $permissions
# }
# )

# # Update the application
# try {
# Update-MgApplication -ApplicationId $appId -RequiredResourceAccess $requiredResourceAccess
# Write-EnhancedLog -Message "Successfully updated application permissions for appId: $appId" -Level "INFO"
# }
# catch {
# Write-EnhancedLog -Message "Failed to update application permissions for appId: $appId. Error: $_" -Level "ERROR"
# throw $_
# }
# }



# function Update-ApplicationPermissions {
# param (
# [string]$appId,
# [string]$permissionsFile
# )

# $resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph

# # Load permissions from the JSON file
# if (Test-Path -Path $permissionsFile) {
# $permissions = Get-Content -Path $permissionsFile | ConvertFrom-Json
# }
# else {
# Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
# throw "Permissions file not found: $permissionsFile"
# }

# # Convert permissions to the required type
# $resourceAccess = @()
# foreach ($permission in $permissions) {
# $resourceAccess += [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphResourceAccess]@{
# Id = [Guid]$permission.Id
# Type = $permission.Type
# }
# }

# # Prepare the required resource access
# $requiredResourceAccess = @(
# [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphRequiredResourceAccess]@{
# ResourceAppId = [Guid]$resourceAppId
# ResourceAccess = $resourceAccess
# }
# )

# # Update the application
# try {
# Update-MgApplication -ApplicationId $appId -RequiredResourceAccess $requiredResourceAccess
# Write-EnhancedLog -Message "Successfully updated application permissions for appId: $appId" -Level "INFO"
# }
# catch {
# Write-EnhancedLog -Message "Failed to update application permissions for appId: $appId. Error: $_" -Level "ERROR"
# throw $_
# Handle-Error -ErrorRecord $_
# }
# }


# function Update-ApplicationPermissions {
# param (
# [string]$appId,
# [string]$permissionsFile
# )

# $resourceAppId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph

# # Load permissions from the JSON file
# if (Test-Path -Path $permissionsFile) {
# $permissions = Get-Content -Path $permissionsFile | ConvertFrom-Json
# }
# else {
# Write-EnhancedLog -Message "Permissions file not found: $permissionsFile" -Level "ERROR"
# throw "Permissions file not found: $permissionsFile"
# }

# # Convert permissions to the required type
# $resourceAccess = @()
# foreach ($permission in $permissions) {
# if ($null -eq $permission.Id) {
# Write-EnhancedLog -Message "Permission Id is null. Skipping this entry." -Level "WARNING"
# continue
# }

# try {
# $resourceAccess += [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphResourceAccess]@{
# Id = [Guid]$permission.Id
# Type = $permission.Type
# }
# }
# catch {
# Write-EnhancedLog -Message "Failed to convert permission Id: $($permission.Id). Error: $_" -Level "ERROR"
# throw $_
# }
# }

# if ($resourceAccess.Count -eq 0) {
# Write-EnhancedLog -Message "No valid permissions found to update the application." -Level "ERROR"
# throw "No valid permissions found to update the application."
# }

# # Prepare the required resource access
# $requiredResourceAccess = @(
# [Microsoft.Graph.PowerShell.Models.IMicrosoftGraphRequiredResourceAccess]@{
# ResourceAppId = [Guid]$resourceAppId
# ResourceAccess = $resourceAccess
# }
# )

# # Update the application
# try {
# Update-MgApplication -ApplicationId $appId -RequiredResourceAccess $requiredResourceAccess
# Write-EnhancedLog -Message "Successfully updated application permissions for appId: $appId" -Level "INFO"
# }
# catch {
# Write-EnhancedLog -Message "Failed to update application permissions for appId: $appId. Error: $_" -Level "ERROR"
# throw $_
# Handle-Error -ErrorRecord $_
# }
# }
#EndRegion '.\Public\Update-ApplicationPermissions.ps1' 150
#Region '.\Public\Validate-AppCreation.ps1' -1

function Validate-AppCreation {
    param (
        [string]$AppName,
        [string]$JsonPath
    )

    # Call the function to run the script in its own instance of pwsh
    

    # Example usage
    # $jsonPath = "C:\path\to\your\jsonfile.json"
    # $appInfo = Get-AppInfoFromJson -jsonPath $jsonPath

   
    Write-EnhancedLog -Message "validating AppName $AppName from $JsonPath"

    try {
        # Import application objects from JSON using Get-AppInfoFromJson function
        $allApps = Get-AppInfoFromJson -jsonPath $JsonPath

         # Output the extracted data
        # $allApps | Format-Table -AutoSize

        # # List all applications
        # Write-EnhancedLog -Message "Listing all applications:"
        # $allApps | Format-Table Id, DisplayName, AppId, SignInAudience, PublisherDomain -AutoSize

        # Filter the applications to find the one with the specified display name
        $app = $allApps | Where-Object { $_.DisplayName -eq $AppName }

        # Debug output
        # Write-EnhancedLog -Message "Filtered applications count: $($app.Count)"
        if ($app.Count -eq 0) {
            Write-EnhancedLog -Message "No applications found with the name $AppName"
        }
        else {
            # Write-EnhancedLog -Message "Filtered applications details:"
            # $app | Format-Table Id, DisplayName, AppId, SignInAudience, PublisherDomain -AutoSize
        }

        # Log the parameters and the retrieved application object
        $params = @{
            AppName    = $AppName
            AppCount   = ($app | Measure-Object).Count
            AppDetails = $app
        }
        Log-Params -Params $params

        # Check if the application object is not null and has items
        if ($null -ne $app -and ($app | Measure-Object).Count -gt 0) {
            Write-EnhancedLog -Message "Application found."
            return $true
        }
        Write-EnhancedLog -Message "Application not found."
        return $false
    }
    catch {
        Write-EnhancedLog -Message "An error occurred: $_"
        throw $_
    }
}
#EndRegion '.\Public\Validate-AppCreation.ps1' 62
#Region '.\Public\Validate-AppCreationWithRetry.ps1' -1

function Validate-AppCreationWithRetry {
    param (
        [Parameter(Mandatory = $true)]
        [string]$AppName,
        [Parameter(Mandatory = $true)]
        [string]$JsonPath
    )

    $maxDuration = 120  # Maximum duration in seconds (2 minutes)
    $interval = 2       # Interval in seconds
    $elapsed = 0        # Elapsed time counter

    while ($elapsed -lt $maxDuration) {
        try {
            # Validate the app creation
            Write-EnhancedLog -Message 'second validation'
            Remove-AppListJson -jsonPath $jsonPath
            # Start-Sleep -Seconds 30
            Run-DumpAppListToJSON -JsonPath $JsonPath
            $appExists = Validate-AppCreation -AppName $AppName -JsonPath $JsonPath
            if (-not $appExists) {
                Write-EnhancedLog -Message "App creation validation failed" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
                throw "App creation validation failed"
            }

            # If the app validation passes, exit the loop
            break
        }
        catch {
            Write-EnhancedLog -Message "An error occurred during app creation validation: $_" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
            Start-Sleep -Seconds $interval
            $elapsed += $interval
        }
    }

    if ($elapsed -ge $maxDuration) {
        Write-EnhancedLog -Message "App creation validation failed after multiple retries" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
        throw "App creation validation failed after multiple retries"
    }
}
#EndRegion '.\Public\Validate-AppCreationWithRetry.ps1' 41
#Region '.\Public\Validate-CertCreation.ps1' -1

function Validate-CertCreation {
    param (
        [string]$Thumbprint,
        [string[]]$StoreLocations = @("Cert:\LocalMachine", "Cert:\CurrentUser")
    )

    foreach ($storeLocation in $StoreLocations) {
        $cert = Get-ChildItem -Path "$storeLocation\My" | Where-Object { $_.Thumbprint -eq $Thumbprint }
        if ($null -ne $cert) {
            Write-EnhancedLog -Message "Certificate validated successfully in $storeLocation" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)
            return $cert
        }
    }

    Write-EnhancedLog -Message "Certificate validation failed" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
    throw "Certificate not found"
    Handle-Error -ErrorRecord $_
}
#EndRegion '.\Public\Validate-CertCreation.ps1' 19
#Region '.\Public\Validate-Certificate.ps1' -1

function Validate-Certificate {
    param (
        [Parameter(Mandatory = $true)]
        [string]$CertPath
    )

    try {
        if (Test-Path -Path $CertPath) {
            Write-EnhancedLog -Message "Certificate path exists: $CertPath" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green)
            return $true
        } else {
            Write-EnhancedLog -Message "Certificate path does not exist: $CertPath" -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow)
            return $false
        }
    }
    catch {
        Write-EnhancedLog -Message "Error validating certificate path: $CertPath. Error: $_" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
        throw $_
    }
}
#EndRegion '.\Public\Validate-Certificate.ps1' 21
#Region '.\Public\Validate-UriAccess.ps1' -1

function Validate-UriAccess {
    param (
        [string]$uri,
        [hashtable]$headers
    )

    Write-EnhancedLog -Message "Validating access to URI: $uri" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan)
    try {
        $response = Invoke-WebRequest -Uri $uri -Headers $headers -Method Get
        if ($response.StatusCode -eq 200) {
            Write-EnhancedLog -Message "Access to $uri PASS" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green)
            return $true
        } else {
            Write-EnhancedLog -Message "Access to $uri FAIL" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
            return $false
        }
    } catch {
        Write-EnhancedLog -Message "Access to $uri FAIL - $_" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red)
        return $false
    }
}
#EndRegion '.\Public\Validate-UriAccess.ps1' 22