Skip to content

feat: add virtual workstation setup scripts for Linux and Windows - #271

Open
crowecawcaw wants to merge 17 commits into
aws-deadline:mainlinefrom
crowecawcaw:virtual-workstation-sample
Open

feat: add virtual workstation setup scripts for Linux and Windows#271
crowecawcaw wants to merge 17 commits into
aws-deadline:mainlinefrom
crowecawcaw:virtual-workstation-sample

Conversation

@crowecawcaw

@crowecawcaw crowecawcaw commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Fixes: #266

What was the problem/requirement? (What/Why)

Issue #266 asks for an example of pre-configuring a workstation for Deadline Cloud submission, so that an artist who logs in only has to sign in. Setting one up by hand means clicking through two installers, wiring a DCC plugin into per-user application preferences, and completing a monitor sign-in dialog. There was no sample showing how to do that unattended.

What was the solution? (How)

Two example scripts, one for Linux and one for Windows, under utility_scripts/virtual_workstation/. Each takes the monitor URL as its only required argument and:

  1. Installs Blender from an official release archive.
  2. Installs the Deadline Cloud submitter with its silent installer, resolved from the published submitter manifest so the download is a pinned, checksummed artifact rather than a moving "latest" URL.
  3. Enables the submitter's Blender add-on. The silent install stages the add-on but cannot enable it, because add-ons live in Blender's per-user preferences while the install runs at system scope.
  4. Installs Deadline Cloud monitor.
  5. Creates a monitor profile non-interactively with deadline-cloud-monitor create-profile.

Every download is verified against a published SHA-256 checksum, and an unreachable checksum is an error rather than a reason to skip the check. The scripts call no AWS APIs and need no credentials.

Blender is a stand-in for whichever DCC you run, chosen because it installs unattended from a public archive with no license server, which keeps the example runnable as-is. Adapting to Maya, Nuke, Houdini, 3ds Max, Cinema 4D, After Effects, or VRED means changing three marked places, called out in comments in both scripts and in the README.

Three behaviors are worth flagging for reviewers, because they are not obvious from the installers alone:

  • create-profile exits 0 even when it fails. Both scripts check its stdout for the success string and read back the [profile ...] stanza it should have written.
  • --monitor-id must be non-empty, but need not be correct. The monitor filters its profile picker on that value and drops any profile whose value is falsy, so an empty ID silently defeats the whole point: the artist gets asked for the monitor URL. The real ID cannot be discovered without AWS credentials, so the scripts pass pending-first-login, which the monitor replaces with the authoritative value at first sign-in.
  • A monitor URL without its Region segment is accepted by the monitor and produces a profile with a wrong region=, so both scripts validate the URL shape themselves first.

What is the impact of this change?

Additive: four new files and two modified. No existing sample is touched. The utility_scripts/ category table gains one row per CONTRIBUTING.md, and the static-check suite gains coverage for utility_scripts/ scripts: tests/conftest.py gets a discovery function and tests/test_utility_scripts.py runs bash -n and the PowerShell parser over them. Previously nothing under utility_scripts/ was syntax-checked in CI, though host_configuration_scripts/ was.

Scope limits, all stated in the README rather than left implicit: the Linux script targets Ubuntu 22.04, because the monitor's .deb depends on libwebkit2gtk-4.0-37, which 24.04 replaced with the 4.1 build, and no official repository offers the 4.0 build for it; both require an image that already has a desktop environment, remote access to reach it, and a working default browser; and on Windows the script must run as the artist's own account, because Windows cannot write another user's per-user state without that user's password.

How was this change tested?

Verified end to end on real EC2 instances against a real monitor, on both platforms, including a manual interactive sign-in and an authenticated API call.

Linux — Ubuntu 22.04 EC2 instance with an XFCE desktop, reached over VNC through an SSM tunnel. Ran the script, then signed in to the monitor by hand.

Before sign-in:

monitor_id=pending-first-login
user_id=
identity_store_id=
{"profile_name": "crowest-us-west-2", "status": "NEEDS_LOGIN", "api_availability": false}

After sign-in, the monitor replaced all three with authoritative values:

user_id=f8115370-70f1-70c2-f3a8-9f857c830818
identity_store_id=d-9267dc065a
monitor_id=monitor-ad9b8cfa2a32417ebb9d44567c5eb0ca
{"profile_name": "crowest-us-west-2", "status": "AUTHENTICATED", "api_availability": true}

$ deadline farm list
- region: us-west-2
  farmId: farm-3ce486c2f12a4f9584afe381615364d1
  displayName: crowest farm

Windows — Windows Server 2022 EC2 instance reached over RDP through an SSM tunnel, script run as the artist with elevation. Same result: monitor opened straight to sign-in with no URL prompt, and after signing in, monitor_id became monitor-ad9b8cfa2a32417ebb9d44567c5eb0ca, auth status reported AUTHENTICATED, and deadline farm list returned the farm.

Both instances were in private subnets with no inbound rules, reachable only through SSM port forwarding, and have been deleted.

Also tested

  • Full runs in containers on both platforms, with independent post-hoc assertions rather than trusting the scripts' own output: blender --version, the add-on read back from bpy.context.preferences.addons.keys(), deadline --version on the artist's login PATH, and the profile stanza in ~/.aws/config.
  • Checksum verification: a real archive against a multi-file manifest, a deliberately corrupted file, an unreachable checksum URL, and a filename absent from the manifest.
  • Error paths: no arguments, a monitor URL with no Region segment, a non-apt image, and an image whose repositories lack libwebkit2gtk-4.0-37.
  • Static analysis: shellcheck and PSScriptAnalyzer both clean, plus python3 scripts/validate_repository.py, python3 scripts/check_external_links.py, and Vale.

Not tested: submitting an actual job from Blender. Everything up to and including authenticated API calls is verified.

Review round: defects found and fixed after the original testing

Checking the scripts' assumptions against live package indexes and the monitor's own .deb metadata found three defects, each since fixed and re-verified end to end on fresh instances.

  • The stated scope was wrong, and the failure mode was bad. The monitor's .deb declares Depends: libwebkit2gtk-4.0-37. Launchpad shows that package is published up to Ubuntu 22.04 only; later releases carry libwebkit2gtk-4.1-0. "Debian-family" therefore over-promised: on Ubuntu 24.04 the script installed Blender and the submitter before failing at apt dependency resolution. There is now an up-front check that stops before installing anything. It uses apt-cache policy rather than apt-cache show, which also succeeds for a virtual package. The supported scope is now Ubuntu 22.04 alone: Debian 12 was listed for a while, but it was never tested, so claiming it was not something this change could back up.
  • One download skipped verification. libssl1.1 was fetched and installed with no checksum, the sole exception to a rule the README states twice. It is not published with a .sha256 beside it, so the expected hash is now a pinned constant, with a comment naming the Packages.gz index to read a newer one from.
  • The pinned OpenSSL was unpatched. It was 1.1.1f-1ubuntu2, the original 20.04 release, superseded in focal-security by 1.1.1f-1ubuntu2.24. Now on the patched build.

Confirmed the OpenSSL 1.1 requirement is real rather than inherited folklore: strings on /usr/bin/deadline-cloud-monitor shows libssl.so.1.1, and the .deb declares no SSL dependency, which is exactly why a missing library does not fail the install and instead surfaces at first launch.

Also replaced the Blender mirror. It pointed at one volunteer host (mirrors.iu13.net); it now uses Blender's own redirector, mirror.blender.org, verified to serve both the archive and the .sha256 manifest for Linux and Windows.

Re-verified on fresh EC2 instances in a private subnet with no public IP and no inbound rules, reachable only through SSM port forwarding, on Ubuntu 22.04 and Windows Server 2022. Both exited 0. Independent post-hoc assertions rather than the scripts' own output: blender --version, the add-on read back from bpy.context.preferences.addons.keys() as the artist, deadline --version on the artist's login PATH, the profile stanza with a non-empty monitor_id, and deadline auth status reporting NEEDS_LOGIN. Interactive sign-in then completed by hand on both platforms: the monitor opened straight to sign-in with no URL prompt. Both instances have been deleted.

Second review round: silent-failure fixes and verified documentation

A further review pass, plus automated review on the PR, found defects that a passing run hides. Each was reproduced before fixing.

  • The Windows script reported success while doing nothing useful when run as SYSTEM. #Requires -RunAsAdministrator is satisfied by SYSTEM, which Systems Manager Run Command and EC2 user data both run as, and every check still passed because they all read the invoking user's own state. The README documented the consequence; nothing enforced it. It now refuses, verified on a real instance: exit 1 with nothing created.
  • The Blender version check reintroduced a SIGPIPE bug on both platforms. blender --version | head -1 under pipefail, and & $blenderExe --version | Select-Object -First 1 on Windows, both let the consumer's early exit determine the reported status, so a working Blender could be reported as unable to run. Both now capture the full output and narrow it only for the message. Caught by automated review, which was right to flag it as the same class of defect the libssl check had just been fixed for.
  • Two URL validators disagreed. [System.Uri] parses the host out of any scheme, so Windows accepted http://, ftp://, and file:// and would write a non-HTTPS URL into the artist's profile, while Linux rejected the uppercase form Windows accepted. Both now check the scheme and compare in lowercase, and agree on all seven inputs tested.
  • rm -rf / Remove-Item -Recurse -Force ran unconditionally as root or administrator on a prefix constant the README tells readers to edit. Both now refuse a directory that holds no Blender executable.
  • A Blender that installed but could not start was reported as success, and --enable-components hardcoded deadline_cloud_for_blender outside the three places marked as needing changes, so following the adaptation instructions still installed the Blender submitter.
  • Windows left roughly 1 GB of installers in %TEMP%, where Linux traps and removes them. Now removed on success, and deliberately kept on failure so installer logs survive.

Every factual claim in the README was then checked against the real artifacts rather than left as inference. That corrected the cleanup steps (the Linux uninstaller exits 0 but leaves THIRD_PARTY_LICENSES behind), removed advice to add a repository that does not exist for libwebkit2gtk-4.0-37 on newer releases, and noted that deadline auth login is not an alternative to the monitor since it drives the monitor and accepts only monitor-created profiles.

Re-verified end to end on fresh Ubuntu 22.04 and Windows Server 2022 instances in a private subnet with no public IP and no inbound rules: both exited 0, Linux twice for idempotency, the Windows SYSTEM guard exited 1 creating nothing, and hiding a real DT_NEEDED library confirmed the version check still catches a genuinely broken Blender rather than having been neutered by the exit-status change. All instances have been deleted.

Was this change documented?

Yes. utility_scripts/virtual_workstation/README.md covers prerequisites, how it works, what the profile contains and why its fields start empty, adapting to another DCC, what ends up on the machine, security and cleanup, and troubleshooting for the failures this testing actually produced. utility_scripts/README.md gains the category table row and a short section, per CONTRIBUTING.md.


By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions github-actions Bot added the waiting-on-maintainers Waiting on the maintainers to review. label Jul 30, 2026
Comment thread utility_scripts/virtual_workstation/setup_workstation_linux.sh Outdated
Add provisioning scripts that prepare a Linux or Windows workstation for
Deadline Cloud submission, so an artist who logs in only has to sign in.

Each script installs Blender, resolves the latest submitter installer from
the published manifest and runs it unattended after verifying its SHA-256
checksum, installs Deadline Cloud monitor, and creates a monitor profile
non-interactively with `deadline-cloud-monitor create-profile`.

Two behaviors that are not obvious from the installers alone:

* The unattended submitter install stages the Blender add-on but does not
  enable it, because add-ons live in Blender's per-user preferences. Both
  scripts run the installer's own add_submitter_to_pref.py through Blender
  in background mode and then read the preferences back to confirm.
* `create-profile` exits 0 even when it fails, so both scripts check its
  output and verify the resulting stanza in the user's AWS config.

The monitor profile and Blender preferences are per user. Linux uses
runuser so provisioning as root works; Windows cannot run as another local
user without their password, so it fails fast instead of writing the
profile into the wrong home directory.

Resolves aws-deadline#266

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Record that create-profile was added in monitor 1.0.2 for administrator
provisioning, note that the user guide documents only the interactive
wizard, and cite the GetMonitor API pattern for the monitor ID rather
than asserting the format as a fixed rule.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…2022

Running the script end to end on an EC2 Windows Server 2022 instance
surfaced four defects, each of which made it fail or report success
falsely. Verified fixed by a clean run plus independent assertions.

* Checksum verification always failed. Windows PowerShell 5.1 returns
  Invoke-WebRequest .Content as Byte[] rather than String, so splitting
  it yielded the first byte value ("98") instead of the hash. Added a
  Get-RemoteText helper that decodes when needed, and used it for the
  manifest too.

* The submitter installer rejected the Blender path. On Windows the
  --blender-<version>-path flag expects the full path to blender.exe,
  not the install directory as on Linux.

* The installer rejected paths containing spaces. Start-Process joins
  -ArgumentList without quoting, so "C:\Program Files\..." arrived as
  two arguments. Quote the values that can contain spaces.

* Add-on verification passed unconditionally. Passing a quoted Python
  expression through --python-expr loses the inner quotes, so Blender
  raised NameError while the check still reported success. Use a script
  file instead. Blender does propagate sys.exit status, and Linux is
  unaffected because bash preserves the quoting.

Also resolve the monitor executable from the uninstall registry entry:
under a 32-bit host process the installer honors WOW64 redirection and
lands in the SysWOW64 view of the profile, which %LOCALAPPDATA% does not
point to, so the previous fixed paths could not find it.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
A review pass raised issues that were then confirmed against real images
and live endpoints.

Correctness:

* The script died on its first action on Amazon Linux 2023 and RHEL 9
  derivatives: they ship curl-minimal, which provides /usr/bin/curl but
  conflicts with the curl package, so `dnf install curl` exits 1. Verified
  on amazonlinux:2023. Require the binaries instead of the packages, and
  add python3, which the manifest parser needs and minimal images omit.

* The OpenSSL 1.1 step was gated on a major version of "9", but AL2023
  reports VERSION_ID=2023, so it never ran there. compat-openssl11 is not
  in any AL2023 repo and the monitor RPM declares no SSL dependency, so
  the install succeeded and the monitor then failed at first launch. Gate
  on the missing library instead, and fail with an explanation.

* `log "... $("$MONITOR_BIN" --version)"` could not abort under set -e, so
  a monitor that installed but could not start was reported as success.
  Capture the version in an assignment.

* The profile name was interpolated into grep patterns unescaped, so a
  name containing "." could match output the monitor never printed, which
  defeats the success check that exists because create-profile exits 0 on
  failure. Use grep -F.

* Refuse --skip-blender without --skip-submitter: it pointed the submitter
  at a Blender path that was never created.

* A trailing flag with no value exited 1 with no output at all.

Verification: the monitor packages and the Blender archive were downloaded
and executed unverified, though both publish checksums. Verify all three,
and make verification strict, since silently downgrading to no check on a
transient error defeats the purpose. Surface AWS CLI errors instead of
discarding them.

Reuse: Blender is a placeholder for other DCCs, but the Blender-specific
logic was spread across five regions with no signposting. Add an
"ADAPTING THIS SCRIPT TO A DIFFERENT DCC" header to both scripts and a
numbered DCC (n of 5) marker at each place that needs changing.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
The scripts had accumulated flags, fallbacks, and branches that obscured the
flow an administrator needs to read and adapt. Cut them back to a single
linear path, from 536 to 313 lines on Linux and 472 to 321 on Windows.

Drop the monitor ID discovery entirely. Verified in the monitor source that
sign-in calls set_credentials, which rewrites the profile with authoritative
monitor, user, and identity store IDs from the portal, and verified that
create-profile accepts --monitor-id "". The scripts now pass an empty value,
which removes the AWS CLI dependency, the deadline:ListMonitors permission,
the placeholder ID, and two warning paths. The example needs no credentials.

Stop installing OpenSSL 1.1. Verified libssl.so.1.1 is absent from Ubuntu
22.04, 24.04, and Rocky 9, so it cannot be assumed, but auto-installing it
meant a hardcoded archive.ubuntu.com URL pinned to one Ubuntu build plus an
EPEL dance that cannot work on Amazon Linux 2023. Check for the library and
stop with per-distribution instructions instead, so the requirement is the
administrator's to satisfy and is documented as a prerequisite.

Replace the flag parsers with positional arguments: the monitor URL, and on
Linux an optional workstation user. That removes the need_value helper, the
three --skip flags and the invalid combination they allowed, --profile-name,
--monitor-id, --blender-version, and --blender-mirror. Version, component,
mirror, and prefixes are now constants at the top of each file.

Collapse download plus verify into one download_verified/Get-VerifiedFile
helper, so every artifact is fetched and checked the same way in one line.

Replace the five numbered "DCC (n of 5)" markers with three, now that the
per-version component map is a single constant.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Rerunning the simplified script on a Windows Server 2022 instance caught
both of these; neither is reachable on Linux.

The empty monitor ID was passed as --monitor-id "", which PowerShell drops
entirely, so the monitor reported that the flag requires a value and no
profile was created. Use --monitor-id= instead, which both platforms accept.
The Linux script now uses the same form so the two read alike.

Resolving the monitor executable from the uninstall registry entry failed.
The installer is 32-bit, so under a service account its writes are
redirected into the SysWOW64 view of the profile while the InstallLocation
it records still names System32: the registry path does not exist on disk.
Neither source is reliable alone, so try the recorded path and its SysWOW64
equivalent, and report every candidate when none exists.

Verified end to end on Windows: exit 0, three checksums verified, add-on
enabled, deadline CLI 0.60.3, profile written and read back. Linux re-run
after the flag change with the same result.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Testing the RHEL branch for the first time showed it did not work: on Rocky 9
the submitter's Blender add-on fails to import its Qt bindings, so the add-on
never registers. Supporting it would mean documenting a list of per-distro
library packages, which is noise in an example.

Drop the RHEL branch and target Debian-family images. That removes the
distribution detection, the pkg_install indirection, the dual .deb/.rpm
monitor install, and the "do not ask for curl" workaround for curl-minimal.
A single check for apt-get fails fast on anything else, and the README says
what to change for another distribution.

With one distribution in scope, installing OpenSSL 1.1 is a single known
step again rather than a per-distro guess, so do it instead of making the
administrator satisfy it. Verified end to end on a bare Ubuntu 22.04 desktop
image with no manual preparation: exit 0, three checksums verified, add-on
enabled, deadline CLI 0.60.3, profile written and read back.

Linux is now 288 lines, down from 313.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Manual sign-in testing on real EC2 instances, against a real monitor, on
both Linux and Windows, showed the empty --monitor-id broke the one thing
this sample exists to do: the monitor ignored the pre-created profile and
asked the artist for the monitor URL.

The monitor filters its profile picker on the monitor_id value from
~/.aws/config and drops any profile whose value is falsy, so an empty ID
removes the profile from the list before auto-login is evaluated. The check
is only for non-emptiness: no format, length, or prefix is required, and
the value is not resolved against the service before sign-in. Passing
"pending-first-login" is therefore enough, and keeps the property that made
the empty value attractive, namely that no AWS credentials are needed to
discover the real ID.

Verified end to end on both platforms: the monitor opens straight to
sign-in with no URL prompt, and first sign-in replaces the placeholder with
the real monitor ID in both ~/.aws/config and the monitor's settings.json,
alongside user_id and identity_store_id. After sign-in, deadline auth
status reports AUTHENTICATED and deadline farm list returns the farm.

Also drop the --monitor-id= syntax, which existed only to smuggle an empty
value past PowerShell's argument handling. With a real value the ordinary
form works on both platforms, so the two scripts read alike again.

README: document the default-browser requirement, since the monitor hands
off to a browser to finish sign-in and Ubuntu's snap-only Firefox does not
work in a remote-desktop session; note that Windows needs an already
elevated shell because of #Requires -RunAsAdministrator; and add
troubleshooting for the monitor missing from the applications menu, which
happens because its desktop entry declares no category.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
…bssl1.1

Three problems found by checking the scripts' assumptions against live
package indexes and the monitor's own .deb metadata.

* The README's stated scope of "Debian-family images" is wrong, and the
  script's apt-get check accepts images the monitor cannot install on.
  The monitor's .deb declares Depends: libwebkit2gtk-4.0-37, which the
  Launchpad and Debian indexes show is published up to Ubuntu 22.04 and
  Debian 12 only; later releases carry libwebkit2gtk-4.1-0 instead. On
  Ubuntu 24.04 or Debian 13 the script would install Blender and the
  submitter, then fail at apt dependency resolution. Check for an
  installable candidate up front and stop with an explanation.

* libssl1.1 was downloaded and installed with no checksum, the one
  artifact in the script exempt from a rule the README states twice. It
  is not published with a .sha256 beside it, so pin the expected hash as
  a constant and name the Packages index to read a newer one from.

* The pinned libssl1.1 was 1.1.1f-1ubuntu2, the original 20.04 release,
  superseded in focal-updates and focal-security by 1.1.1f-1ubuntu2.24.
  Use the patched build.

Also point the Blender mirror at Blender's own redirector,
mirror.blender.org, rather than naming one downstream mirror. Verified
that it serves both the archive and the .sha256 manifest for Linux and
Windows, so the two scripts no longer hardcode a single volunteer host.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
@crowecawcaw
crowecawcaw force-pushed the virtual-workstation-sample branch from 00807b7 to 3f1d74a Compare July 30, 2026 23:15
Signing in by hand on both platforms surfaced four gaps between the README
and observed behavior.

* The profile section showed only the Linux credential_process, which shells
  out to cat over a cache file. Windows writes a different form entirely,
  invoking the monitor executable with get-credentials. Show both, since the
  Windows one is what an administrator debugging a Windows workstation sees.

* Nothing warned against running the Windows script as SYSTEM. Systems
  Manager Run Command and EC2 user data both execute that way, and both are
  plausible ways to reach for a provisioning script. Doing so writes the
  profile and Blender preferences into a service profile the artist never
  logs in to, so the artist sees nothing pre-configured and the failure is
  silent. Name the two mechanisms and give the scheduled-task flags that
  run it as the artist instead.

* The prerequisites asked for a desktop environment but not for the remote
  access needed to reach it, and did not say when the artist's sign-in
  happens relative to the script. State that the scripts install neither.

* The Firefox note stopped short of the apt pin, without which Ubuntu's
  snap transitional package can win and sign-in fails in a remote session.
  Record that the Mozilla .deb was verified to complete sign-in over VNC.

Also mark the monitor's install scope in the layout table: system-wide on
Linux, per user on Windows. That asymmetry is why the Windows script must
run as the artist, and the table read as though both were equivalent.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
A review pass found defects that a passing run hides, each reproduced before
fixing.

Correctness:

* The Windows script reported success after writing everything into a service
  profile no artist logs in to. #Requires -RunAsAdministrator is satisfied by
  SYSTEM, which Systems Manager Run Command and EC2 user data both run as, and
  every check still passed because they all read the invoking user's own state.
  The README already documented the consequence; nothing enforced it. Refuse to
  run as SYSTEM.

* The libssl check could report a library that is present as missing. Under
  pipefail, `ldconfig -p | grep -q` dies with SIGPIPE once ldconfig outpaces
  grep's early exit, so the pipeline returns 141. Reproduced: status 141
  whenever the match is not in the final 64 KB, which on a desktop image it is
  not. The result was a redundant download and reinstall on every re-run,
  including over a newer libssl from another source. Capture, then match.

* The two URL validators disagreed. [System.Uri] parses the host out of any
  scheme, so Windows accepted http://, ftp://, and file:// and wrote a
  non-HTTPS URL into the artist's profile, while Linux rejected the uppercase
  form Windows accepted. Check the scheme explicitly and compare in lowercase.

* `rm -rf "$BLENDER_PREFIX"` ran unconditionally as root on a constant the
  README tells readers to edit, so a prefix of /opt or C:\Program Files would
  be destroyed. Only delete a directory holding a Blender executable.

* Blender installed but unable to start was reported as success: a command
  substitution in an argument cannot abort under set -e, and `| head -1` masks
  the status regardless. Reproduced with a stub failing on a missing shared
  library. Capture in an assignment, matching what the monitor step already did.

* A failed download exited with curl's status and no error line, unlike the
  checksum fetch beside it.

Portability: --enable-components hardcoded deadline_cloud_for_blender outside
the three places marked as needing changes, so following the instructions still
installed the Blender submitter. Hoist it to SUBMITTER_COMPONENT.

Windows left roughly 1 GB of installers in %TEMP%, where Linux traps and
removes them. Remove them on success, keeping them when a run fails so the
installer logs survive.

Tests: utility_scripts/ had no CI coverage, though host_configuration_scripts/
gets bash -n and PowerShell parse checks from machinery that only needed a
second discovery function. Verified both new checks fail on injected syntax
errors. The scriptBody length limit stays scoped to host configuration, which
is the only place it applies.

README: document --enable-auto-login and --set-as-deadline-default, which
change observable behavior and were passed silently; replace troubleshooting
that sent readers to re-run the script for a state the script cannot produce;
stop recommending --python-expr on Windows, which the script itself avoids
because PowerShell drops the inner quotes; give concrete uninstall commands
for both platforms; and disclose that Debian 12 takes an untested path that
installs an Ubuntu-built libssl.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Re-running on a bare Ubuntu 22.04 server image showed the new "will not run"
check firing correctly but reporting nothing actionable: Blender was missing
libXrender, libXfixes, libXi, libxkbcommon, libSM, libICE, and libGL, and the
message said only that it would not run.

That image has no desktop environment, which the README lists as a prerequisite,
so failing is right. Report ldd's unresolved libraries and name the likely cause
so the reader can act instead of guessing which of the preceding steps broke.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Both the README and the script's error message told readers on Ubuntu 24.04 or
Debian 13 to "add a repository that provides the 4.0 build" of
libwebkit2gtk-4.0-37. Checking rather than assuming: it is absent from every
noble pocket and component (main, universe, restricted, multiverse, across
release, updates, security, and backports), and Launchpad's primary archive
carries it no later than mantic. There is no such repository to add, so the
advice reads like an escape hatch and is a dead end.

Say plainly that no official repository offers it for those releases, and give
the real alternative: install the submitter without the monitor and use an
ordinary AWS credential source. Note explicitly that `deadline auth login` is
not that alternative -- it drives the monitor and accepts only profiles the
monitor created -- because it is the obvious thing to reach for and would waste
the reader's time. Also note that going this route gives up what the sample
pre-configures, so the tradeoff is stated rather than implied.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Comment thread utility_scripts/virtual_workstation/setup_workstation_linux.sh Outdated
Swept the remaining assertions and checked each against the actual installers,
packages, and binaries rather than leaving them as inference. Most held; three
did not.

* Cleanup was incomplete. The Linux uninstaller exits 0 and removes
  /etc/profile.d/deadline.sh, so the deadline CLI does leave new login shells,
  but it leaves /opt/DeadlineCloudSubmitter behind holding THIRD_PARTY_LICENSES.
  Following the old instructions left a stray directory. Add the removal and
  say why.

* The Microsoft Edge claim was stated as fact but never checked. Soften it, and
  make the neighboring Firefox point concrete: `apt install firefox` on 22.04
  gets a transitional package (version 1:1snap1-0ubuntu2) that installs the
  snap, which is the actual trap.

* The PATH claim named the right file but not what it does. It appends
  /opt/DeadlineCloudSubmitter/DeadlineClient; say so.

Verified and left alone: the deb package name is deadline-cloud-monitor;
--blender-45-path is a real flag, confirming the derivation from the component
name; /etc/profile.d/deadline.sh exists; /opt/DeadlineCloudSubmitter/uninstall
exists and accepts --mode; create-profile's required options are exactly
--profile, --monitor-id, and --monitor-url, with --enable-auto-login and
--set-as-deadline-default optional; get-credentials is a real subcommand,
matching the Windows credential_process; and the monitor's desktop entry really
does declare `Categories=` empty, which is why it lands in no menu.

Incidentally reconfirmed the OpenSSL 1.1 dependency: on an image without it the
monitor fails with "error while loading shared libraries: libssl.so.1.1",
independently of this sample.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Comment thread utility_scripts/virtual_workstation/setup_workstation_windows.ps1 Outdated
…atforms

Automated review caught the version check reintroducing, on both platforms, the
very pitfall the libssl check was just fixed to avoid.

Linux: `blender --version | head -1` under pipefail. head exits after the first
line and closes the pipe, Blender takes SIGPIPE on its next write, and the
pipeline reports 141, so a working Blender is reported as unable to run.
Reproduced with a stub emitting 40 lines, which is the order Blender itself
emits: the pipe form returns 141 while capturing first returns 0. This was worse
than the libssl case, which needed enough output to fill the pipe buffer before
it misfired; here the consumer exits immediately, so it misfires at any size.

Windows: `& $blenderExe --version 2>&1 | Select-Object -First 1` then checking
$LASTEXITCODE. Select-Object halts the upstream pipeline once it has its object,
which can terminate a still-running native command and leave $LASTEXITCODE
reflecting the termination rather than Blender's own exit. Not reproducible on
PowerShell 7, and Windows PowerShell 5.1 was not available to test, but the
capture form is unconditionally correct and matches Linux.

Both now capture the full output, then take the first line for the message.
Verified the fixed logic still reports a genuinely broken Blender, so the check
did not lose its purpose.

Audited the rest of both scripts for the same shape. The remaining Linux
pipelines are safe: the awk with `exit` reads a here-string, so there is no
producer process to signal, and cut, paste, and awk without exit all read to
EOF. On Windows, `| Out-Null` consumes its entire input, and $LASTEXITCODE
survives it correctly for both zero and nonzero exits, verified both directions.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
Debian 12 was listed as supported but never tested. The package indexes say it
should work, and the README disclosed that the libssl1.1 step installs an
Ubuntu-built .deb there, but a documented caveat on an untested platform is
still a claim this change cannot back up.

Drop Debian from the supported list and say plainly that the script was written
and tested against Ubuntu 22.04. The adaptation guidance stays, since other
Debian-family releases are likely to work and the script uses nothing
Ubuntu-specific beyond apt-get and the libssl1.1 package it fetches, but that is
now framed as the reader's own porting work rather than as support.

The webkit gate keeps its behavior; only the message narrows, since Ubuntu 22.04
is the release the sample now names. Also drop the paragraph about installing an
Ubuntu .deb onto Debian, which no longer describes a supported path.

Signed-off-by: Stephen Crowe <6042774+crowecawcaw@users.noreply.github.com>
@crowecawcaw
crowecawcaw marked this pull request as ready for review July 31, 2026 22:34
@crowecawcaw
crowecawcaw requested a review from a team as a code owner July 31, 2026 22:34

MONITOR_URL="${1:-}"
[[ -n "$MONITOR_URL" ]] || die "usage: $0 MONITOR_URL [WORKSTATION_USER]"
WORKSTATION_USER="${2:-${SUDO_USER:-$(id -un)}}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows script refuses to run as SYSTEM (line 91), and the comment there makes a convincing case: the profile and Blender preferences are per-user, so a service account produces a workstation where the artist finds nothing configured. Would it be worth adding the mirror-image check here?

As written, WORKSTATION_USER on line 84 falls back to the invoking user, which is root under EC2 user data. That's the path the header on line 13 and the README on line 17 both recommend, and I think every verification step downstream would still pass, since each one reads root's own state. The profile would land in /root/.aws/config and the script would report success, but the artist wouldn't see a pre-configured monitor.

Something like refusing when WORKSTATION_USER resolves to root unless it was passed explicitly would close the gap and match the reasoning already in the Windows script. Happy to be told I'm wrong if there's a provisioning flow where configuring root is the intent.

# substitution inside an argument cannot abort under set -e. The usual cause of a
# real failure is a server image lacking Blender's X11 and GL libraries.
if ! blender_output="$("$BLENDER_PREFIX/blender" --version 2>&1)"; then
missing="$(ldd "$BLENDER_PREFIX/blender" 2>/dev/null | awk '/not found/ {print $1}' | paste -sd' ' -)"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pipefail reasoning in the comment above this block is a nice catch, and I think one more instance of it bites the diagnostic itself. When ldd exits non-zero, that failure propagates through the pipeline into the missing= assignment, and under set -e the script exits right there, so neither the "missing shared libraries" message nor the fallback on line 223 gets printed. I tried the construct standalone and got exit 1 with no output.

The case I'd expect to hit it is arm64: the archive name is pinned to linux-x64, so on Graviton the download and checksum both succeed and the binary fails to exec, which is exactly when ldd says "not a dynamic executable" and exits 1. An || true on that line would keep both messages intact. Would an early uname -m check also be worth it, so an arm64 host fails before the download rather than after?

sudo ./setup_workstation_linux.sh https://mystudio.us-west-2.deadlinecloud.amazonaws.com/ artist
```

Windows, in an elevated PowerShell session **as the artist's own account**. Start PowerShell with **Run as administrator** first: the script declares `#Requires -RunAsAdministrator`, so launching it from an unelevated shell fails with `ScriptRequiresElevation` rather than prompting.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading #Requires -RunAsAdministrator together with the SYSTEM refusal, I think this implies the artist's own account has to be a local administrator, since it must run elevated as that account. Would it be worth calling that out in Prerequisites?

The case I'm thinking of is a studio where artists are standard users. There the interactive route isn't available, and I don't think the scheduled task with /RU <artist> /RL HIGHEST helps either, since it still needs an account that can elevate. Line 28 currently says "an elevated PowerShell session on Windows," which reads as satisfiable by any admin. Naming whose account it has to be would let people tell early whether the sample fits how they manage workstations.

Comment on lines +204 to +208
if [[ -e "$BLENDER_PREFIX" ]]; then
[[ -x "$BLENDER_PREFIX/blender" ]] \
|| die "$BLENDER_PREFIX exists but holds no blender executable. Refusing to delete it; check BLENDER_PREFIX."
rm -rf "$BLENDER_PREFIX"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refuse-to-delete guard makes sense, and I want to flag one interaction with the comment above it. If a run is interrupted partway through the tar -xJf, /opt/blender is left holding a partial extraction that probably has no ./blender in it yet, since tar's member order isn't guaranteed. Every later run then trips the guard and stops with "exists but holds no blender executable," so the script can't get past it without someone removing the directory by hand.

Extracting into a temp directory and then moving it into place would mean the prefix only ever exists complete, which keeps the guard doing its job while making re-runs clean the way the comment describes. Alternatively, a line in Troubleshooting about clearing the prefix would at least give the operator the recovery step. The Windows script has the same shape around line 184, though the staged extract there makes it less likely.

# upstream pipeline once it has its object, which can terminate a still-running
# native command and leave $LASTEXITCODE reflecting that rather than Blender's own
# exit. Same reason the Linux script does not pipe into head.
$blenderOutput = (& $blenderExe --version 2>&1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I wasn't able to test, so treat it as a question rather than a finding: in Windows PowerShell 5.1, 2>&1 on a native command turns stderr lines into error records, and with $ErrorActionPreference = "Stop" that becomes a terminating NativeCommandError. If a Blender build ever writes a startup warning to stderr (a GPU or driver notice, say), I think this line would fail with a .NET error rather than reaching your check on line 208.

It clearly didn't come up in your Windows Server 2022 testing, so blender --version must be quiet there. It just seems to depend on the Blender build rather than on anything the script controls. Would wrapping it in try/catch, or relaxing $ErrorActionPreference around just this call, be worth the insurance?

Comment on lines +194 to +199
# The archive contains a single blender-<version>-windows-x64\ directory.
Move-Item -Path (Get-ChildItem -Path $extractDir -Directory | Select-Object -First 1).FullName -Destination $BlenderPrefix
$blenderExe = Join-Path $BlenderPrefix "blender.exe"
if (-not (Test-Path $blenderExe)) {
Write-Fatal "expected blender.exe at $blenderExe after extraction"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Regarding error messages rather than behavior... in both of these spots the specific message underneath can't actually be reached, because the statement above it throws first under EAP=Stop.

On line 192, if Expand-Archive yields no top-level directory, .FullName is $null and Move-Item -Path $null throws a parameter-binding error before the "expected blender.exe at ..." check runs. Same shape on line 356: a missing .aws\config makes Select-String throw path-not-found rather than reaching your "profile is missing from" message. Capturing the directory and testing it before the move, plus a Test-Path before Select-String, would let both messages actually surface. Either way the script fails, so purely a nicer-diagnostics thing.

@@ -0,0 +1,75 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you add a row to the table in tests/README.md for test_utility_scripts.py? Right now the table lists every other area, so someone adding a script under utility_scripts/ would read it and conclude nothing validates theirs.

The part I'd find most useful there is the bit already explained in the module docstring: these get the same bash -n and PowerShell parse checks, but the scriptBody limit deliberately doesn't apply because they aren't uploaded to the service. That contrast with the host configuration row is the thing a future contributor would otherwise have to work out from the source.

Comment on lines +122 to +125
# Deadline Cloud monitor's .deb depends on libwebkit2gtk-4.0-37, which was
# dropped after Ubuntu 22.04 in favor of the 4.1 build. Installing
# it elsewhere fails at dependency resolution, so say so here rather than partway
# through. Check before anything is installed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This says "before anything is installed," but apt-get install ca-certificates curl xz-utils python3 five lines up has already run by then. The check is still well placed, since it comes before the monitor, submitter, and Blender, which are the parts that actually cost time to undo. Maybe "before the monitor or submitter are installed"? The comments in these scripts are precise enough that I found myself reading this one literally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-on-maintainers Waiting on the maintainers to review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: example of pre-configuring an EC2 instance as a virtual workstation that's set up for Deadline submission

2 participants