JesseTG
JesseTG

Reputation: 2123

How to download a file using BITS in a Packer provisioner?

I'm writing a provisioning script in PowerShell for a Packer-built Windows image on a CI pipeline. This process involves downloading several large files. I'm under the impression that BITS is faster than Invoke-WebRequest, so I've decided to use BITS to asynchronously download these large files.

The problem is that BITS will only process jobs for users that are interactively logged on...

BITS transfers files only when the job's owner is logged on to the computer (the user must have logged on interactively). BITS does not support the RunAs command.

...unless the job was submitted by a service account.

You can use BITS to transfer files from a service. The service must use the LocalSystem, LocalService, or NetworkService system account. These accounts are always logged on; therefore, jobs submitted by a service using these accounts always run.

But even then, there's a wrinkle:

If a service running under a system account impersonates the user before calling BITS, BITS responds as it would for any user account (for example, the user needs to be logged on to the computer for the transfer to occur).

This is an issue because the provisioning script runs as the Administrator account, which is not a service account and therefore must be logged in interactively to use BITS. This happens to be Packer's behavior, so I can't change this. I'm wrong, I can change this. See my final answer. How can I do the following in one PowerShell script?

Upvotes: 0

Views: 848

Answers (2)

JesseTG
JesseTG

Reputation: 2123

It turns out there's a solution to this, although it's specific to Packer. I didn't mention much about my use of it because I didn't think it was that important.

Contrary to my initial belief, Packer's PowerShell provisioner lets you run the provisioning script with elevated privileges as any user...

  • elevated_user and elevated_password (string) - If specified, the PowerShell script will be run with elevated privileges using the given Windows user.
provisioner "powershell" {
   elevated_user = "Administrator"
   elevated_password = build.Password
}

...including service users.

If you specify an empty elevated_password value then the PowerShell script is run as a service account. For example:

provisioner "powershell" {
  elevated_user = "SYSTEM"
  elevated_password = ""
}

After adjusting my Packer template's provisioner block accordingly, I can now confirm that Start-BitsTransfer and friends work as expected. No need to pass complicated arguments or play tricks with sessions.

Upvotes: 1

alexzelaya
alexzelaya

Reputation: 255

You could use psexec to run a secondary script with SYSTEM rights by the administrator content and have the primary script identify the exit code of the psexec process to confirm it has successfully executed.

https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-7.1

https://learn.microsoft.com/en-us/sysinternals/downloads/psexec#:~:text=PsExec%20is%20a%20light-weight,to%20manually%20install%20client%20software.

https://weblogs.asp.net/soever/returning-an-exit-code-from-a-powershell-script

Upvotes: 1

Related Questions