Public/Start-cChocoEx.ps1

<#
    .SYNOPSIS
        Bootstraps and manages the cChocoEx PowerShell DSC Module configuration.
 
    .DESCRIPTION
        Comprehensive function that initializes, configures, and manages the cChocoEx environment.
        Handles installation, configuration management, and scheduled task creation for Chocolatey
        package management.
 
        Key features:
        - Chocolatey installation and configuration
        - Package source management
        - Feature configuration
        - Package installation
        - Maintenance window management
        - Environment variable configuration
        - Scheduled task management
 
    .PARAMETER SettingsURI
        URI to a PowerShell Data File containing cChocoEx settings.
 
    .PARAMETER InstallDir
        Installation directory for Chocolatey. Defaults to "$env:ProgramData\chocolatey".
 
    .PARAMETER ChocoInstallScriptUrl
        URL to the Chocolatey installation script. Defaults to official source.
 
    .PARAMETER ChocoDownloadUrl
        Optional URL to a specific Chocolatey nupkg for installation.
 
    .PARAMETER SourcesConfig
        Path or URL to the Chocolatey sources configuration file.
 
    .PARAMETER PackageConfig
        Array of paths or URLs to package configuration files.
 
    .PARAMETER ChocoConfig
        Path or URL to the Chocolatey configuration file.
 
    .PARAMETER FeatureConfig
        Path or URL to the Chocolatey features configuration file.
 
    .PARAMETER NoCache
        Prevents caching of configuration files. Files are downloaded to temp location.
 
    .PARAMETER WipeCache
        Clears all cached configuration files before processing.
 
    .PARAMETER RandomDelay
        Adds a random delay (0-1800 seconds) before processing.
 
    .PARAMETER Loop
        Enables continuous execution through scheduled task.
 
    .PARAMETER LoopDelay
        Minutes to wait between loops when Loop is enabled. Defaults to 60.
 
    .PARAMETER MigrateLegacyConfigurations
        Migrates legacy configuration files to current format.
 
    .PARAMETER OverrideMaintenanceWindow
        Bypasses maintenance window restrictions.
 
    .PARAMETER EnableNotifications
        Enables desktop notifications for operations (Windows 10+ only).
 
    .PARAMETER SetcChocoExEnvironment
        Persists configuration to machine environment variables.
 
    .EXAMPLE
        Start-cChocoEx -SettingsURI "https://config.contoso.com/chocolatey/settings.psd1"
        Initializes cChocoEx using settings from a remote configuration file.
 
    .EXAMPLE
        Start-cChocoEx -Loop -LoopDelay 120 -RandomDelay
        Starts cChocoEx in continuous mode with 2-hour intervals and random startup delay.
 
    .EXAMPLE
        Start-cChocoEx -PackageConfig @("packages1.psd1", "packages2.psd1") -NoCache
        Processes multiple package configurations without caching files.
 
    .NOTES
        Author: Jon Yonke
        Version: 2.0
        Created: 2024-02-11
        Requires: Administrative privileges
         
        Environment Variables Used:
        - ChocoInstallScriptUrl
        - ChocoDownloadUrl
        - cChocoExChocoConfig
        - cChocoExSourcesConfig
        - cChocoExPackageConfig
        - cChocoExFeatureConfig
        - cChocoExBootStrapUri
    #>

function Start-cChocoEx {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string]
        $SettingsURI,
        # Chocolatey Installation Directory
        [Parameter()]
        [string]
        $InstallDir = "$env:ProgramData\chocolatey",
        # Chocolatey Installation Script URL
        [Parameter()]
        [string]
        $ChocoInstallScriptUrl = 'https://chocolatey.org/install.ps1',
        # URL to chocolatey nupkg
        [Parameter()]
        [string]
        $ChocoDownloadUrl,
        # URL to cChoco sources configuration file
        [Parameter()]
        [string]
        $SourcesConfig,
        # URL to cCHoco packages
        [Parameter()]
        [array]
        $PackageConfig,
        # URL to cChoco Chocolatey configuration file
        [Parameter()]
        [string]
        $ChocoConfig,
        # URL to cChoco Chocolatey features configuration file
        [Parameter()]
        [string]
        $FeatureConfig,
        # Do not cache configuration files
        [Parameter()]
        [switch]
        $NoCache,
        # Wipe locally cached psd1 configurations
        [Parameter()]
        [switch]
        $WipeCache,
        # RandomDelay
        [Parameter()]
        [switch]
        $RandomDelay,
        # Loop the Function
        [Parameter()]
        [Switch]
        $Loop,
        # Loop Delay in Minutes
        [Parameter()]
        [int]
        $LoopDelay = 60,
        # Legacy Migration Automation
        [Parameter()]
        [Switch]
        $MigrateLegacyConfigurations,
        # OverrideMaintenanceWindow
        [Parameter()]
        [switch]
        $OverrideMaintenanceWindow,
        # Enable Desktop Notifications
        [Parameter()]
        [Switch]
        $EnableNotifications,
        # Set machine enviroment variables
        [Parameter()]
        [switch]
        $SetcChocoExEnvironment
    )

    #Ensure Running as Administrator
    if (-Not (Test-IsAdmin)) {
        Write-Warning "This function requires elevated access, please reopen PowerShell as an Administrator"
        Break
    }   
    
    #Enable TLS 1.2
    #https://docs.microsoft.com/en-us/dotnet/api/system.net.securityprotocoltype?view=net-5.0
    [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072

    #Set Notifications Variable
    $Global:EnableNotifications = $EnableNotifications

    #Default Maintenance Windows Active and Enabled
    $Global:MaintenanceWindowEnabled = $true 
    $Global:MaintenanceWindowActive = $true


    #Validate Current Execution Policy
    $CurrentExecutionPolicy = Get-ExecutionPolicy
    try {
        $null = Set-ExecutionPolicy Bypass -Scope CurrentUser
    }
    catch {
        Write-Log -Severity 'Warning' -Message "Error Changing Execution Policy"
    }

    #Exclude Machines Set to Exclude Ring
    if ((Get-cChocoExRing) -eq 'Exclude') {  
        Write-Log -Severity 'Information' -Message 'This machine is set to the Exclude Ring. cChocoEx Stopped'
        Break
    }

    #Ensure cChocoExBootStrapTask is not running
    $PendingFile = Join-Path $env:cChocoExDataFolder '.cChocoExPending'
    if (Test-Path -Path $PendingFile) {
        $PendingFileItem = Get-Item -Path $PendingFile
        $LastBootTime = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
        #Autoremove if older than 24 hours or older than last reboot
        if ($PendingFileItem.CreationTime -lt (Get-Date).AddDays(-1) -or $PendingFileItem.CreationTime -lt $LastBootTime) {
            Write-Log -Severity 'Warning' -Message 'Stale cChocoEx pending file found, removing'
            Remove-Item -Path $PendingFile -Force
        }
    }
    if (Test-Path -Path $PendingFile) {
        Write-Log -Severity 'Warning' -Message 'cChocoEx pending completion, please wait until it finishes to invoke again'
        break
    }
    Set-Content -Path $PendingFile -Value '' -Force

    #Ensure choco.exe is not active
    $i = 0
    do {
        $IsChocoActive = Test-IsChocoActive
        if ($i -eq 1) {
            Write-Log -Severity 'Information' -Message 'Choco.exe is active, waiting up to 300 seconds'
        }
        if ($i -gt 0) {
            Start-Sleep -Seconds 1        
        }
        $i++
    } until (
        ($IsChocoActive -eq $False) -or ($i -gt 300)
    )
    if (Test-IsChocoActive) {
        Write-Log -Severity 'Information' -Message 'Choco.exe is active, cChocoEx Stopped'
        Break
    }

    #Log Start
    try {
        Write-Log -Severity 'Information' -Message 'cChocoEx Started'
        Write-EventLog -LogName 'Application' -Source 'cChocoEx' -EventId 4000 -EntryType Information -Message 'cChocoEx Started'
    }
    catch {
        Write-Warning "Error Starting Log, wiping and retrying"
        Write-Log -Severity 'Information' -Message 'cChoco Bootstrap Started' -New
        Write-EventLog -LogName 'Application' -Source 'cChocoEx' -EventId 4000 -EntryType Information -Message 'cChocoEx Started'
    }
    
    #Register and Start cChocoEx Task
    Register-cChocoExTask

    #Update Media Folder
    $null = Copy-Item -Path (Join-Path -Path ($PSScriptRoot | Split-Path) -ChildPath 'Media\*') -Destination $cChocoExMediaFolder -Recurse -Force

    #Ensure cChoco Module Is Present and Available
    if (-not($ModuleBase)) {
        Write-Log -Severity 'Error' -Message 'Required Module cChoco Not Found'
        Break
    }

    #Migrate Legacy Configuration Files
    if ($MigrateLegacyConfigurations) {
        Move-LegacyConfigurations
    }

    #Evaluate Mainteance Window Override
    if ($OverrideMaintenanceWindow) {
        $Global:OverrideMaintenanceWindow = $True
        Write-Log -Severity 'Information' -Message 'Global OverrideMaintenanceWindow Enabled'
    }
    else {
        $Global:OverrideMaintenanceWindow = $False
    }

    #Ensure Notification Prerequisites are Installed and Imported
    if ($Global:EnableNotifications) {
        $OSMajorVersion = (Get-CimInstance -ClassName Win32_OperatingSystem -Property Version).Version.Split('.')[0]
        if ([int]$OSMajorVersion -lt 10) {
            Write-Log -Severity 'Warning' -Message 'Notifications Require Windows 10 or Server 2016 and Greater'
            $Global:EnableNotifications = $false
        }
        else {
            Install-BurntToast
            Install-RunAsUser    
        }
    }
    
    #####################################
    # Gather Environment Variables
    #####################################
        
    #ChocoInstallScriptUrl
    if ($env:ChocoInstallScriptUrl) {
        Write-Log -Severity 'Information' -Message "Environment Variable `$env:ChocoInstallScriptUrl: $env:ChocoInstallScriptUrl"
        $ChocoInstallScriptUrl = $env:ChocoInstallScriptUrl
    }
    #ChocoDownloadUrl
    if ($env:ChocoDownloadUrl -and ([string]::IsNullOrEmpty($ChocoDownloadUrl))) {
        Write-Log -Severity 'Information' -Message "Environment Variable `$env:ChocoDownloadUrl: $env:ChocoDownloadUrl"
        $ChocoDownloadUrl = $env:ChocoDownloadUrl
    }
    #cChocoExChocoConfig
    if ($env:cChocoExChocoConfig -and ([string]::IsNullOrEmpty($ChocoConfig))) {
        Write-Log -Severity 'Information' -Message "Environment Variable `$env:cChocoExChocoConfig: $env:cChocoExChocoConfig"
        $ChocoConfig = $env:cChocoExChocoConfig
    }
    #cChocoExSourcesConfig
    if ($env:cChocoExSourcesConfig -and ([string]::IsNullOrEmpty($SourcesConfig))) {
        Write-Log -Severity 'Information' -Message "Environment Variable `$env:cChocoExSourcesConfig: $env:cChocoExSourcesConfig"
        $SourcesConfig = $env:cChocoExSourcesConfig
    }
    #cChocoExPackageConfig
    if ($env:cChocoExPackageConfig -and ([string]::IsNullOrEmpty($PackageConfig))) {
        Write-Log -Severity 'Information' -Message "Environment Variable `$env:cChocoExPackageConfig: $env:cChocoExPackageConfig"
        $PackageConfig = $env:cChocoExPackageConfig -split ';'
    }
    #cChocoExFeatureConfig
    if ($env:cChocoExFeatureConfig -and ([string]::IsNullOrEmpty($FeatureConfig))) {
        Write-Log -Severity 'Information' -Message "Environment Variable `$env:cChocoExFeatureConfig: $env:cChocoExFeatureConfig"
        $FeatureConfig = $env:cChocoExFeatureConfig
    }

    #####################################
    # Set Environment Variables
    #####################################
    if ($SetcChocoExEnvironment) {
        #ChocoInstallScriptUrl
        if ($ChocoInstallScriptUrl) {
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:ChocoInstallScriptUrl: $ChocoInstallScriptUrl"
            [Environment]::SetEnvironmentVariable('ChocoInstallScriptUrl', $ChocoInstallScriptUrl, 'Machine')
            $env:ChocoInstallScriptUrl = $ChocoInstallScriptUrl  
        }
        #ChocoDownloadUrl
        if ($ChocoDownloadUrl) {
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:ChocoDownloadUrl: $ChocoDownloadUrl"
            [Environment]::SetEnvironmentVariable('ChocoDownloadUrl', $ChocoDownloadUrl, 'Machine')  
            $env:ChocoDownloadUrl = $ChocoDownloadUrl     
        }
        #cChocoExChocoConfig
        if ($ChocoConfig) {
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:ChocoConfig: $ChocoConfig"
            [Environment]::SetEnvironmentVariable('cChocoExChocoConfig', $ChocoConfig, 'Machine')
            $env:cChocoExChocoConfig = $ChocoConfig
        }
        #cChocoExSourcesConfig
        if ($SourcesConfig) {
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:SourcesConfig: $SourcesConfig"
            [Environment]::SetEnvironmentVariable('cChocoExSourcesConfig', $SourcesConfig, 'Machine')
            $env:cChocoExSourcesConfig = $SourcesConfig
        }
        #cChocoExPackageConfig
        if ($PackageConfig) {
            $PackageConfigString = $PackageConfig -join ';'
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:cChocoExPackageConfig: $PackageConfigString"
            [Environment]::SetEnvironmentVariable('cChocoExPackageConfig', $PackageConfigString, 'Machine')
            $env:cChocoExPackageConfig = $PackageConfigString
        }
        #cChocoExFeatureConfig
        if ($FeatureConfig) {
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:FeatureConfig: $FeatureConfig"
            [Environment]::SetEnvironmentVariable('cChocoExFeatureConfig', $FeatureConfig, 'Machine')
            $env:cChocoExFeatureConfig = $FeatureConfig
        }
        #cChocoExBootStrapUri
        if ($env:cChocoExBootStrapUri) {
            Write-Log -Severity 'Information' -Message "Setting Environment Variable `$env:cChocoExBootStrapUri: $env:cChocoExBootStrapUri"
            [Environment]::SetEnvironmentVariable('cChocoExBootStrapUri', $env:cChocoExBootStrapUri, 'Machine')
        }
    }

    #####################################
    # cChocoInstaller
    #####################################
    $Configuration = @{
        InstallDir            = $InstallDir
        ChocoInstallScriptUrl = $ChocoInstallScriptUrl
    }

    #Set Enviromental Variable for chocolatey url to nupkg
    if ($ChocoDownloadUrl) {
        $env:chocolateyDownloadUrl = $ChocoDownloadUrl
    }
    Start-cChocoInstaller -Configuration $Configuration

    #Ensure Chocolatey Config is Valid
    #https://github.com/chocolatey/choco/issues/1047
    if (-not(Test-ChocolateyConfig)) {
        $Reset = Reset-ChocolateyConfig
        Write-Log -Severity 'Information' -Message $Reset.Config
        Write-Log -Severity 'Information' -Message "Chocolatey Config Reset: $($Reset.Reset)"
    }
    
    #Evaluate Random Delay Switch
    if ($RandomDelay) {
        $RandomSeconds = Get-Random -Minimum 0 -Maximum 1800
        Write-Log -Severity 'Information' -Message "Random Delay Enabled"
        Write-Log -Severity 'Information' -Message "Delay: $RandomSeconds`s"
        Start-Sleep -Seconds $RandomSeconds
    }

    #Settings
    if ($SettingsURI) {
        $Destination = (Join-Path $cChocoExTMPConfigurationFolder "bootstrap-cchoco.psd1")

        try {
            Write-Log -Severity 'Information' -Message "Downloading SettingsURI File"
            Write-Log -Severity 'Information' -Message "Source: $SettingsURI"
            Write-Log -Severity 'Information' -Message "Destination: $Destination"

            switch (Test-PathEx -Path $SettingsURI) {
                'URL' { Invoke-WebRequest -Uri $SettingsURI -UseBasicParsing -OutFile $Destination }
                'FileSystem' { Copy-Item -Path $SettingsURI -Destination $Destination -Force }
            }        
        }
        catch {
            Write-Log -Severity 'Warning' -Message $_.Exception.Message
        }
        $SettingsFile = Import-PowerShellDataFile -Path $Destination
        $Settings = $SettingsFile | ForEach-Object { $_.Keys | ForEach-Object { $SettingsFile.$_ } } 
    
        #Variables
        $InstallDir = $Settings.InstallDir
        $ChocoInstallScriptUrl = $Settings.ChocoInstallScriptUrl
        $SourcesConfig = $Settings.SourcesConfig
        $PackageConfig = $Settings.PackageConfig
        $ChocoConfig = $Settings.ChocoConfig
        $FeatureConfig = $Settings.FeatureConfig
    }

    Write-Log -Severity 'Information' -Message "cChocoEx Settings"
    Write-Log -Severity 'Information' -Message "SettingsURI: $SettingsURI"
    Write-Log -Severity 'Information' -Message "InstallDir: $InstallDir"
    Write-Log -Severity 'Information' -Message "ChocoInstallScriptUrl: $ChocoInstallScriptUrl"
    Write-Log -Severity 'Information' -Message "SourcesConfig: $SourcesConfig"
    Write-Log -Severity 'Information' -Message "PackageConfig: $PackageConfig"
    Write-Log -Severity 'Information' -Message "ChocoConfig: $ChocoConfig"
    Write-Log -Severity 'Information' -Message "FeatureConfig: $FeatureConfig"

    if ($WipeCache) {
        Write-Log -Severity 'Information' -Message 'WipeCache Enabled. Wiping any previously downloaded psd1 configuration files'
        Get-ChildItem -Path $cChocoExConfigurationFolder -Filter *.psd1 | Remove-Item -Recurse -Force
    }
    #Preclear any previously downloaded NoCache configuration files
    if ($NoCache) {
        Write-Log -Severity 'Information' -Message 'NoCache Enabled. Wiping any previously downloaded NoCache configuration files from temp'
        Get-ChildItem -Path $cChocoExTMPConfigurationFolder -Filter *.psd1 | Remove-Item -Recurse -Force
    }

    #Copy Config Config?
    $Global:ChocoConfigDestination = (Join-Path $cChocoExConfigurationFolder "config.psd1")
    if ($ChocoConfig) {
        if ($NoCache) {
            $Global:ChocoConfigDestination = (Join-Path $cChocoExTMPConfigurationFolder "config.psd1")
        }
        try {
            Write-Log -Severity 'Information' -Message "Downloading Choco Config File"
            Write-Log -Severity 'Information' -Message "Source: $ChocoConfig"
            Write-Log -Severity 'Information' -Message "Destination: $ChocoConfigDestination"

            switch (Test-PathEx -Path $ChocoConfig) {
                'URL' { Invoke-WebRequest -Uri $ChocoConfig -UseBasicParsing -OutFile $ChocoConfigDestination }
                'FileSystem' { Copy-Item -Path $ChocoConfig -Destination $ChocoConfigDestination -Force }
            } 
            Write-Log -Severity 'Information' -Message 'Chocolatey Config File Set.'   
        }
        catch {
            Write-Log -Severity 'Warning' -Message $_.Exception.Message
        }
    }

    #Copy Sources Config
    $Global:SourcesConfigDestination = (Join-Path $cChocoExConfigurationFolder "sources.psd1")
    if ($SourcesConfig) {
        if ($NoCache) {
            $Global:SourcesConfigDestination = (Join-Path $cChocoExTMPConfigurationFolder "sources.psd1")
        }
        try {
            Write-Log -Severity 'Information' -Message "Downloading Source Config File"
            Write-Log -Severity 'Information' -Message "Source: $SourcesConfig"
            Write-Log -Severity 'Information' -Message "Destination: $SourcesConfigDestination"

            switch (Test-PathEx -Path $SourcesConfig) {
                'URL' { Invoke-WebRequest -Uri $SourcesConfig -UseBasicParsing -OutFile $SourcesConfigDestination }
                'FileSystem' { Copy-Item -Path $SourcesConfig -Destination $SourcesConfigDestination -Force }
            }
            Write-Log -Severity 'Information' -Message 'Chocolatey Sources File Set.'
        }
        catch {
            Write-Log -Severity 'Warning' -Message $_.Exception.Message
        }
    }

    #Copy Features Config
    $Global:FeatureConfigDestination = (Join-Path $cChocoExConfigurationFolder "features.psd1")
    if ($FeatureConfig) {
        if ($NoCache) {
            $Global:FeatureConfigDestination = (Join-Path $cChocoExTMPConfigurationFolder "features.psd1")
        }
        try {
            Write-Log -Severity 'Information' -Message "Downloading Feature Config File"
            Write-Log -Severity 'Information' -Message "Source: $FeatureConfig"
            Write-Log -Severity 'Information' -Message "Destination: $FeatureConfigDestination"

            switch (Test-PathEx -Path $FeatureConfig) {
                'URL' { Invoke-WebRequest -Uri $FeatureConfig -UseBasicParsing -OutFile $FeatureConfigDestination }
                'FileSystem' { Copy-Item -Path $FeatureConfig -Destination $FeatureConfigDestination -Force }
            }
            Write-Log -Severity 'Information' -Message 'Chocolatey Feature File Set.'
        }
        catch {
            Write-Log -Severity 'Warning' -Message $_.Exception.Message
        }
    }

    #Copy Package Config
    $Global:PackageConfigDestination = $cChocoExConfigurationFolder
    if ($PackageConfig) {
        if ($NoCache) {
            $Global:PackageConfigDestination = $cChocoExTMPConfigurationFolder
        }
        $PackageConfig | ForEach-Object {
            $Path = $_
            $Destination = (Join-Path $PackageConfigDestination ($_ | Split-Path -Leaf))

            try {
                Write-Log -Severity 'Information' -Message "Downloading Package Config File"
                Write-Log -Severity 'Information' -Message "Source: $Path"
                Write-Log -Severity 'Information' -Message "Destination: $Destination"

                switch (Test-PathEx -Path $_) {
                    'URL' { Invoke-WebRequest -Uri $Path -UseBasicParsing -OutFile $Destination }
                    'FileSystem' { Copy-Item -Path $Path -Destination $Destination -Force }
                }
                Write-Log -Severity 'Information' -Message 'Chocolatey Package File Set.'
            }
            catch {
                Write-Log -Severity 'Warning' -Message $_.Exception.Message
            }
        }
    }

    #####################################
    # cChocoConfig
    #####################################
    if (Test-Path $ChocoConfigDestination ) {
        $ConfigImport = $null
        $ConfigImport = Import-PowerShellDataFile $ChocoConfigDestination
        Start-cChocoConfig -ConfigImport $ConfigImport
    }
    else {
        Write-Log -Severity 'Information' -Message "File not found, configuration will not be modified"
    }
    #####################################
    # cChocoFeature
    #####################################
    if (Test-Path $FeatureConfigDestination ) {
        $ConfigImport = $null
        $ConfigImport = Import-PowerShellDataFile $FeatureConfigDestination
        Start-cChocoFeature -ConfigImport $ConfigImport
    }
    else {
        Write-Log -Severity 'Information' -Message "File not found, features will not be modified"
    }
    #####################################
    # cChocoSource
    #####################################
    if (Test-Path $SourcesConfigDestination ) {
        $ConfigImport = $null
        $ConfigImport = Import-PowerShellDataFile $SourcesConfigDestination
        Start-cChocoSource -ConfigImport $ConfigImport
    }
    else {
        Write-Log -Severity 'Information' -Message "File not found, sources will not be modified"
    }
    #####################################
    # cChocoPackageInstall
    #####################################
    [array]$Configurations = $null
    Get-ChildItem -Path $PackageConfigDestination -Filter *.psd1 | Where-Object { $_.Name -notmatch "sources.psd1|config.psd1|features.psd1" } | ForEach-Object {
        $ConfigImport = $null
        $ConfigImport = Import-PowerShellDataFile $_.FullName 
        $Configurations += $ConfigImport | ForEach-Object { $_.Keys | ForEach-Object { $ConfigImport.$_ } }
    }

    if ($Configurations ) {
        Start-cChocoPackageInstall -Configurations $Configurations
    }
    else {
        Write-Log -Severity 'Information' -Message "File not found, packages will not be modified"
    }
    
    #Cleanup
    #Preclear any previously downloaded NoCache configuration files
    if ($NoCache) {
        Write-Log -Severity "Information" -Message "Preclear any previously downloaded NoCache configuration files"
        Get-ChildItem -Path $cChocoExTMPConfigurationFolder -Filter *.psd1 | Remove-Item -Recurse -Force
    }

    #Register cChocoEx BootStrap Task if Enabled
    if ($Loop) {
        Write-Log -Severity "Information" -Message "Function Looping Enabled"
        Write-Log -Severity "Information" -Message "Looping Delay: $LoopDelay Minutes"
        Register-cChocoExBootStrapTask -LoopDelay $LoopDelay
    }
    #Clear Pending file
    if (Test-Path -Path $PendingFile) {
        Remove-Item -Path $PendingFile -Force    
    }
    $null = Set-ExecutionPolicy $CurrentExecutionPolicy -Scope CurrentUser -ErrorAction SilentlyContinue
    Write-EventLog -LogName 'Application' -Source 'cChocoEx' -EventId 4001 -EntryType Information -Message 'cChocoEx Finished'
    RotateLog
}