Interesting scheduled deployment challenge

I have an interesting scheduled deployment challenge. Here is our situation.

We have 50 or so Amazon WorkSpaces that we setup using a Deploy “compound package” that calls about a dozen nested packages, some of which need to run as the logged on user. We run this compound package when we first provision a WorkSpace and it works perfectly.

In addition, we have a compound package that updates the WorkSpaces and also runs some nested packages as the logged on user.

Conceptually, what I’d like to have happen is this: for each WorkSpace, after an update I’d like it to wait a week and then automatically update the WorkSpace again the next time it’s online and logged in.

This turns out to be a non-trivial requirement.

First some things to note:

  1. When a WorkSpace is first started, it will have a heartbeat, but no user will be logged on, so we can’t run the update package quite yet
  2. Each WorkSpace is typically only used a few hours each day, so we can’t rely on a WorkSpace being online and logged in at any particular time
  3. The update package runs for about 10 minutes
  4. I have a dynamic Inventory collection that includes all WorkSpaces that are online (but not necessarily logged in)
  5. The first step of the update package is a Message that only runs if a user is logged on. It uses an `Error Mode` of `Stop Deployment with Success` (very handy feature), so that if the update package runs when no user is logged on, it just fails silently, which is good.
  6. The Heartbeat trigger doesn’t help, because it triggers before the user has logged in, so the update package fails
  7. The Weekly trigger doesn’t work, because it will end up mostly running at a time when a given WorkSpace is not online

One possible approach would be to:

  1. Add a Powershell script step that creates a special flag file when the update succeeds
  2. Add a Powershell script step that 1) only succeeds when today date is more than a week later than the flag file date and 2) uses an `Error Mode` of `Stop Deployment with Success`
  3. Use a schedule that runs frequently—say every 30-60 minutes

Another possible approach would be to use the Heartbeat trigger, but incorporate a Sleep step as the first step, so that the user has enough time to login.

Finally, having nothing to do with PDQ, I could try to develop some code that can automatically start and login to a WorkSpace. Then I could just run this as the first step and use a simple weekly schedule.

Surely others have dealt with a similar challenge?

1

Comments

5 comments
Date Votes
  • Your main problem seems to be to reliably detect when there is actually a user logged on. For that you could use a scheduled task triggered by user login that sends something to your deployment server, telling it to start the deployment via the PDQDeploy.exe command line.

    1
  • That's an interesting approach, but devising a secure way for the WorkSpace to run PDQDeploy.exe seems challenging--especially since they exists in different AWS accounts (necessary for security).

    If the paucity of responses is any measure, it really surprises me that more people have not encountered the issue of updating systems where a package must run as the logged in user. Surely that's not so unusual?

     

    1
  • No, I'm not suggesting to run it on the Workspace, but to have the Workspace tell the server that a user just logged on. Just an HTTP request, for example, that somehow causes the server to run PDQDeploy.exe.

    1
  • I understand that and, in many contexts, such an HTTP request would be no problem. In our context, however, executing an HTTP request from the AWS WorkSpace account into the AWS admin account containing PDQ is non-trivial. 

    Your idea is a good one, it's just a question of whether it's feasible in our context.

    1
  • I have come up with a solution that seems to work reasonably well:

    1. When Deploy updates a WorkSpace, it runs a PowerShell step that creates a machine environment variable, `ItsLastUpdatePackageDeployedDate`, with the ISO date/time as a value
    2. I created the PowerShell scanner listed below, `ReadyForUpdate`, that is triggered via Heartbeat. This scanner determines that a WorkSpace is ready to be updated if a) it has been more than `WaitBetweenUpdatesDays` (set to 7) since the last update and b) the env var, `USERDOMAIN_ROAMINGPROFILE` exists. This env var seems to only exist when a WorkSpace user is actually logged on (of course other approaches could be easily employed). Detection of this env var is repeated in a loop for roughly `TimeoutSecs` (set to 60 to give the user a minute to login after the WorkSpace is online). 
    3. I have a dynamic Inventory collection, `Ready for Update`, that contains WorkSpaces that are ready for update
    4. I have a Deploy schedule for the update package that runs every 10 minutes against the `Ready for Update` collection

    BTW, the update package alerts the user (via message step) at the start and end of the update process but, I will shortly be implementing the ability for the user to cancel the update as per this: https://help.pdq.com/hc/en-us/community/posts/360000252611-Give-logged-on-user-a-choice

    Here is the PowerShell scanner (alas, the indentation was lost when I pasted it):

    [CmdletBinding()]
    param (
    # How long to wait before assuming system is not online or no user is logged on
    [UInt32]$TimeoutSecs = 60,
    # How many days to wait between updates
    [UInt32]$WaitBetweenUpdatesDays = 7
    )

    Write-Verbose "TimeoutSecs = [$TimeoutSecs], WaitBetweenUpdatesDays = [$WaitBetweenUpdatesDays]"

    $loggedOn = $false

    for($i = 1; $i -le $TimeoutSecs; $i++) {
    if ($env:USERDOMAIN_ROAMINGPROFILE -ne $null) {
    $loggedOn = $true
    Write-Verbose "Found `USERDOMAIN_ROAMINGPROFILE` on attempt #$i"
    break
    }
    Write-Verbose "Failed to find `USERDOMAIN_ROAMINGPROFILE` on attempt #$i"
    Start-Sleep -s 1
    }

    $updateRequired = $false

    if ($loggedOn -eq $true) {
    $daysSinceLastUpdate = 0
    $lastUpdateDt = $env:ItsLastUpdatePackageDeployedDate
    if ($lastUpdateDt -ne $null) {
    $lastUpdateDate = [datetime]::Parse($env:ItsLastUpdatePackageDeployedDate)
    Write-Verbose "lastUpdateDate = [$lastUpdateDate]"
    if ($lastUpdateDate -ne $null) {
    $nowIso = Get-Date -format s
    $nowDt = [datetime]::Parse($nowIso)
    $ts = New-TimeSpan -Start $lastUpdateDate -End $nowDt
    $daysSinceLastUpdate = $ts.Days
    }
    Write-Verbose "daysSinceLastUpdate = [$daysSinceLastUpdate]"
    if ($daysSinceLastUpdate -ge $WaitBetweenUpdatesDays) {
    $updateRequired = $true
    }
    }
    else {
    $updateRequired = $true
    }
    }

    $readyForUpdate = $loggedOn -and $updateRequired


    [PSCustomObject]@{
    "ReadyForUpdate" = $readyForUpdate
    }

    1

Please sign in to leave a comment.

Didn't find what you were looking for?

New post