Public/Start-cChocoEx.ps1

<#
.SYNOPSIS
Bootstraps the cChoco PowerShell DSC Module
 
.DESCRIPTION
Bootstraps the cChoco PowerShell DSC Module
#>

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
    )

    #Ensure Running as Administrator
    if (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
        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 Global Variables
    Set-GlobalVariables
    $Global:MaintenanceWindowEnabled = $True
    $Global:MaintenanceWindowActive = $True
    $Global:TSEnv = Test-TSEnv
    $Global:EnableNotifications = $EnableNotifications

    #Ensure cChocoEx Data Folder Structure is Created
    Set-cChocoExFolders
    
    Write-Log -Severity 'Information' -Message "Starting cChocoEx"
    
    #Register cChocoEx Task
    Register-cChocoExTask

    #Ensure Registry Is Setup
    Set-RegistryConfiguration

    #Ensure cChocoEx Tasks are Running
    Start-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
    }

    if ($MigrateLegacyConfigurations) {
        Move-LegacyConfigurations
    }

    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    
        }
    }
    
    #Log Task Sequence Detection
    Write-Log -Severity 'Information' -Message "Task Sequence Environemnt Detected: $TSEnv"

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

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

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

    try {
        Write-Log -Severity 'Information' -Message 'cChocoEx Started'
    }
    catch {
        Write-Warning "Error Starting Log, wiping and retrying"
        Write-Log -Severity 'Information' -Message 'cChoco Bootstrap Started' -New

    }

    #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 -and (-not($TSEnv))) {
        Write-Log -Severity "Information" -Message "Function Looping Enabled"
        Write-Log -Severity "Information" -Message "Looping Delay: $LoopDelay Minutes"
        Register-cChocoExBootStrapTask -LoopDelay $LoopDelay
    }

    $null = Set-ExecutionPolicy $CurrentExecutionPolicy -Scope CurrentUser -ErrorAction SilentlyContinue
    RotateLog
}