Powershell Task Image Download

Set the desktop background to a new image of the sun pulled from NASA every day. How hard can it be?

Use powershell to make the request for the file, and manage which file should be the "latest" while marking the rest with a date. I also include how to invoke that script with a service account to run against a schedule so you can forget about it.

# Scripting

You can see my actual production file is here. In plain English:

  1. Accept a URL to download, defaulting to my chosen background (perks of being the author).
  2. Get the filename from the end of the URL.
  3. If that file exists already in the current location, copy it and append the time now.
  4. Download the file to the current location, overwriting the file.

note we also set powershell to use TLS 1.2, by default the dot net method and Invoke-WebRequest would try, and fail with TLS 1.0 against this url target. I thought this was improved in powershell v7 but a system improvement would be to enable strong crypto for all dot net.

You must use full paths to any file manipulation. Aside from good practice for the sake of clarity this will avoid an error when running the task as the gMSA user. I have found any time you access dot net from powershell you can't take for granted it will understand relative paths.

My file timestamps are actually a the second time run, it should use the file creation date instead of now, but script run date is close enough for me. Unique and ordered was all I needed in this application.

# Signing Your Script

Signing is not as important as it sounds and if this is new ground to you don't worry about it, just add the parameter -ExecutionPolicy Bypass when creating the Scheduled Task Action arguments.

# Create A Scheduled Task

It's fastest to use the GUI and create the task on a target machine, from that we'll edit that job in powershell. Schedule the trigger to run daily and the action as follows;

-ExecutionPolicy ByPass -File .\Get-Image.ps1
Start In
<Full path to the download directory and where the script file is>

Create the task as your own user account. This will make initial testing easier to change task settings and re-run. Check that it is running correctly, if not skip down to the debugging section for advice.

# Group Managed Service Account

We can't leave the task to run as our account, Naturally you won't be logged in all the time but you can't leave the password saved in the task.

Follow Microsofts' steps if this is your first service account to prepare Active Directory, read carefully about the root KDS Key. Then create an account for this task, it would be ideal to have 1 job per user. It's more work to keep documentation updated of one account used for many different things when it comes to cleaning up years later.

New-ADServiceAccount -Identity DownloadImageTask -Description "Downloads an image from one URL for the system backgrounds"  -DNSHostName <DC> -PrincipalsAllowedToRetrievePassword (Get-AdComputer <server with task scheduled>)

Avoid specifically explaining the script or targets in the description. Those readable in the target fields and your script help text. If you change the script or servers later nobody's going to update this description anyway.

Next install that service account on the server running the task. Install-ADServiceAccount requires AD cmdlets are installed locally or you get an error. You can't imported them from a remote pssession. But just to tools are easy enough;

Install-WindowsFeature RSAT-AD-Powershell
Install-ADServiceAccount -Identity FilesTask
Test-ADServiceAccount -identity FilesTask
# True indicates this server can retrieve the password from AD.
# Otherwise this will fail in about a month when the password rotates automatically.
Remove-WindowsFeature RSAT-AD-Powershell

# Don't close powershell yet.

This service account must be given permission to run scheduled tasks on the computer. I do this by GPO, the setting is Computer Settings > Windows Settings > Security > Local Policy > User Rights Assignment > Logon As Batch Job and add the service account identity. If you're making this a new workflow rather than a one-off, now might be the time to define a "localrunner" group and put it into this setting, adding the service account to the group of course.

Warning: "Logon As Batch Job" value is replaced, not appended. So be careful not to remove the default memberships and any other modifications set.

# Allow NTFS Permissions

The task is going to run the service account, it won't be able to change any files on the server. On the task target folder (with the script), Allow Modify NTFS rights for This folder and files

Finally change the task to run as the service account, It's a good job you're learning powershell because this can't be done from the GUI:

    # Get the task and inspect the output you indeed have the correct task.
    Get-ScheduledTask '<Task Name Here>' -OutVariable mytask

    # Check the user account, You will see your current account settings we'll change later

    # This only updates the representation of the scheduled task in the variable,
    $mytask.principal = New-ScheduledTaskPrincipal -identity DownloadImageTask$ -AuthType password

    # Pipe our edited task to overwrite the settings.
    $myTask | Set-ScheduledTask

note: AD Principals have a dollar $ suffix, that's nothing to do with powershell

Once the user is changed you won't be able to edit settings on the task due to the above error when trying to save changes.

I suggest the easiest way to edit lots of settings would be to change the task to run back as yourself, make changes and then repeat the above principal setting.

# Set the backgrounds

Now that one server is downloading the image for us, we've done our part to avoid hitting some poor image service will clients booting up and getting the image constantly. You just need a GPO to copy the new background at startup, and set that copied file for each user logon. Doing this rather than using network location for the background will prevent then entire network losing the background at once should there be a problem.