Secure PowerShell Scripts Running with Windows Task Scheduler

Do you currently or have you ever wanted to schedule the execution of PowerShell scripts with Windows Task Scheduler? If so, there are some considerations you should be aware of surrounding the security of those scripts. For example, it wouldn’t be difficult for a user to alter a script to decrypt and export encrypted credentials. Or a script could intentionally or unintentionally be modified, potentially causing undesired outcomes on the next task run, or course there are other ways to protect files using NTFS permissions. Still, I needed a way to safeguard against possible human errors.

I came up with a simple but effective solution that adds a layer of security by preventing the script from executing if it has been maliciously or accidentally modified. I achieved this by simply using a File Hash check in the Scheduled Task Argument along with an “If” statement. If the File Hash string specified in the Argument does not match the script File Hash, the script will fail to execute.

IF ((Get-FileHash "C:\Scripts\Delete-User.ps1").Hash -eq '6A96A1D8865629A75E782709A42A590C25B936EC1F7CC7D847389DF140D43120') {C:\Scripts\Delete-User.ps1}

I then wrapped this solution into a simple PowerShell function New-SecureScriptTask.ps1, which can be found here from my Github Repo.

Function New-SecureScriptTask {

    [CmdletBinding(PositionalBinding = $false)]
        [Parameter(Mandatory = $true)]
        [ValidateScript( { Test-Path $ScriptPath })]
        [Parameter(Mandatory = $true)]
        [Parameter(Mandatory = $false)]
        [String]$TaskDescription = $TaskName,
        [Parameter(Mandatory = $false)]
        [String]$UserID = "Administrator",
        [Parameter(Mandatory = $false)]
        [ValidateSet("Highest", "Limited")]
        [String]$RunLevel = "Highest"
    ## Creating Task Argument String Using File Hash and Script Path.
    [string]$TaskArg = 'If ((Get-FileHash "{0}").Hash -eq "{1}") {{"{0}"}}' -f $ScriptPath, (Get-FileHash -Path $ScriptPath).Hash
    ## Creating New Task Action.
    $Params = @{
        Execute  = "powershell.exe";
        Argument = "$($TaskArg)"
    $TaskAction = New-ScheduledTaskAction @Params
    ## Creating Task Principal.
    $Params = @{
        UserId   = "$($UserID)";
        RunLevel = "$($RunLevel)"
    $TaskPrincipal = New-ScheduledTaskPrincipal @Params
    # Registering The Scheduled Task.
    $Params = @{
        TaskName    = $TaskName;
        Action      = $TaskAction;
        Description = $TaskDescription;
        Principal   = $TaskPrincipal
    Register-ScheduledTask @Params

This PowerShell function will automatically create and register the scheduled task using my solution; all you need is to specify the full path to the PowerShell script and Task Name. Once you have done that, you can modify the task further, like setting up a trigger, schedule etc., via the Task Scheduler Desktop App.

New-SecureScriptTask -ScriptPath "C:\Scripts\Delete-User.ps1" -TaskName "Delete-User-Task"

I hope you found this helpful. Feel free to comment if you have any questions.

Leave a Reply