Functions/GenXdev.Windows/Initialize-ScheduledTaskScripts.ps1

################################################################################
<#
.SYNOPSIS
Creates daily and hourly PowerShell scripts and their corresponding scheduled task.
 
.DESCRIPTION
Creates daily and hourly PowerShell scripts and their corresponding scheduled tasks
that will run as the current user with elevated privileges. Tasks are created for:
- Every hour of every day of the week
- Every hour daily
- At system startup
- At user logon
 
.PARAMETER FilePath
The directory path where the task scripts will be created. If not specified,
defaults to a ScheduledTasks folder in the parent directory.
 
.PARAMETER Prefix
A prefix for the scheduled task names. Default is "PS".
 
.EXAMPLE
Initialize-ScheduledTaskScripts -FilePath "C:\Tasks" -Prefix "MyTasks"
 
.EXAMPLE
Initialize-ScheduledTaskScripts
#>

function Initialize-ScheduledTaskScripts {

    [CmdletBinding()]
    param(
        ###########################################################################
        [parameter(
            Position = 0,
            Mandatory = $false,
            HelpMessage = "The directory path where task scripts will be created"
        )]
        [string] $FilePath = "",
        ###########################################################################
        [parameter(
            Position = 1,
            Mandatory = $false,
            HelpMessage = "Prefix for the scheduled task names"
        )]
        [string] $Prefix = "PS"
        ###########################################################################
    )

    begin {

        # define days of week array for task creation
        $daysOfWeek = @(
            "Sunday", "Monday", "Tuesday", "Wednesday",
            "Thursday", "Friday", "Saturday"
        )

        # get current UTC time for task scheduling
        $now = [DateTime]::UtcNow

        # get current user credentials for task execution
        $credential = Get-Credential -UserName `
            ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)

        # normalize and validate file path
        if ([string]::IsNullOrWhiteSpace($FilePath)) {
            $FilePath = Expand-Path `
                -FilePath "$PSScriptRoot\..\..\..\..\..\ScheduledTasks\" `
                -CreateDirectory
        }
        else {
            $FilePath = Expand-Path -FilePath $FilePath
        }

        # set global workspace folder
        $Global:WorkspaceFolder = Expand-Path "$PSScriptRoot\..\..\..\..\..\"

        Write-Verbose "Using script directory: $FilePath"
        Write-Verbose "Using task prefix: $Prefix"
    }

    process {

        function New-TaskDefinition {
            param([string]$TaskName, [string]$Description, $Trigger)

            # create script path
            $scriptPath = Expand-Path -CreateDirectory `
                -FilePath "$FilePath\$TaskName.ps1"

            # create script file if it doesn't exist
            if (-not (Test-Path $scriptPath -ErrorAction SilentlyContinue)) {
                $scriptContent = @"
# $Description
 
$($Description | ConvertTo-Json) | Out-File '$Global:WorkspaceFolder\scheduledtasks.log.txt' -Append
 
"@

                $null = Set-Content -Path $scriptPath -Value $scriptContent
            }

            # check if task already exists
            if (-not (Get-ScheduledTask -TaskName $TaskName `
                -TaskPath "\$Prefix\" -ErrorAction SilentlyContinue)) {

                Write-Verbose "Creating task \$Prefix\$TaskName : $Description"

                # create task action
                $actionArguments = "-ExecutionPolicy Bypass -NoLogo -Command & `
                    `"'$scriptPath'`""

                $action = New-ScheduledTaskAction `
                    -Execute ((Get-Command "pwsh.exe").source) `
                    -Argument $actionArguments `
                    -Id "Exec $TaskName".Replace(" ", "_") `
                    -WorkingDirectory $Global:WorkspaceFolder

                # create task settings
                $settings = New-ScheduledTaskSettingsSet `
                    -AllowStartIfOnBatteries `
                    -DontStopIfGoingOnBatteries `
                    -Hidden `
                    -StartWhenAvailable
                $settings.AllowHardTerminate = $true
                $settings.ExecutionTimeLimit = 'PT1H'
                $settings.Volatile = $false

                # set task end boundary
                $trigger.EndBoundary = $now.AddYears(99).ToString(
                    "yyyy-MM-dd'T'HH:mm:ss")

                # convert secure string password for task registration
                $ptr = [Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode(
                    $credential.Password)
                $plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringUni($ptr)
                [Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode($ptr)

                # register the scheduled task
                $taskParams = @{
                    TaskName = $TaskName
                    User = $credential.UserName
                    Password = $plainPassword
                    RunLevel = "Highest"
                    Action = $action
                    Description = $Description
                    Settings = $settings
                    Trigger = $trigger
                    TaskPath = $Prefix
                    Force = $true
                }
                Register-ScheduledTask @taskParams
            }
        }

        # create startup task
        New-TaskDefinition `
            -TaskName "${Prefix}_at_startup" `
            -Description "Scheduled-task executed at startup" `
            -Trigger (New-ScheduledTaskTrigger -AtStartup)

        # create logon task
        New-TaskDefinition `
            -TaskName "${Prefix}_at_logon" `
            -Description "Scheduled-task executed at logon" `
            -Trigger (New-ScheduledTaskTrigger -AtLogOn)

        # create weekly tasks for each day and hour
        foreach ($day in $daysOfWeek) {
            for ($hour = 0; $hour -lt 24; $hour++) {
                $taskName = "$Prefix_$($day.ToLower())_$($hour.ToString('D2'))00h_utc"
                $description = "Scheduled-task for $day at $($hour.ToString('D2')):00h"

                $dayDiff = ([int]$now.DayOfWeek) - $daysOfWeek.IndexOf($day)
                $at = $now.Date.AddDays($dayDiff).AddHours($hour)
                if ($at -lt $now) { $at = $at.AddDays(7) }

                New-TaskDefinition `
                    -TaskName $taskName `
                    -Description $description `
                    -Trigger (New-ScheduledTaskTrigger -Weekly -DaysOfWeek $day -At $at)
            }
        }

        # create daily tasks for each hour
        for ($hour = 0; $hour -lt 24; $hour++) {
            $taskName = "$Prefix_daily_$($hour.ToString('D2'))00h_utc"
            $description = "Scheduled-task executed Daily at $($hour.ToString('D2')):00h"

            $at = $now.Date.AddHours($hour)
            if ($at -lt $now) { $at = $at.AddDays(1) }

            New-TaskDefinition `
                -TaskName $taskName `
                -Description $description `
                -Trigger (New-ScheduledTaskTrigger -Daily -At $at)
        }
    }

    end {
    }
}
################################################################################