What this does

Fonts and styles for each site live in S3. Upload a new or updated one there, run one command, and it's copied to the server(s), registered with Windows if needed, and the app briefly restarts to pick it up. New servers get the current fonts/styles automatically when they launch -- no action needed for those.

1. Upload the file to S3

  • App font (used inside the digitizing app only): s3://securedownload.pulsemicro.com/pulseid/fontpacks/<Site>/app/
  • Windows font (needs to be installed as an OS-level font, e.g. for something outside the app that also needs it): s3://securedownload.pulsemicro.com/pulseid/fontpacks/<Site>/windows/
  • Style (.style file): s3://securedownload.pulsemicro.com/pulseid/fontpacks/<Site>/styles/

Most fonts only need app/. Only upload to windows/ if you specifically know this font needs to be registered with Windows itself -- not "just in case." Uploading an app-only font to windows/ by mistake can install it as a real OS font, which Windows then locks, making it harder to update later. If you're not sure which one a font needs, ask engineering rather than uploading to both.

<Site> is the site name as used today, e.g. Saas1 (production) or StageSaas1 (staging). Test in StageSaas1 first, then upload the same file to Saas1.

2. Run the install command for Stage

aws ssm send-command --document-name InstallFonts --targets Key=tag:Alias,Values=<Site>

Replace <Site> the same way (e.g. Values=StageSaas1). Runs on every server currently up for that site.

That's it

No manifest to edit, no hashes to compute. If the uploaded file already matches what's installed, nothing happens -- no restart. If it's new or changed, it installs and the site restarts briefly (a few seconds) to load it.

New instances get fonts automatically (engineering)

A newly launched instance needs fonts/styles installed before it starts serving traffic, not just whenever someone happens to run the install command next. That's handled by an SSM State Manager association on the InstallFonts document, targeted by tag:Alias=<Site> with no schedule -- State Manager applies it the moment a new instance registers with SSM (i.e. at launch), in addition to on-demand runs via send-command.

Currently exists:

  • InstallFonts-StageSaas1 (association id 44d945a7-675f-4f9b-8b24-df07ca44431b) -- targets tag:Alias=StageSaas1, tracks $DEFAULT.
  • InstallFonts-StageSaas5 (association id 447ebc5f-c868-4bd1-af3b-59bc3c74deb6) -- targets tag:Alias=StageSaas5, tracks $DEFAULT.
  • InstallFonts-Saas5 (association id cfa071ec-7563-4ddf-91d3-b3f1ddac07d7) -- targets tag:Alias=Saas5, pinned to document version 15 (production, per the pinning note below).

To create one for another site:

aws ssm create-association \
  --name InstallFonts \
  --targets Key=tag:Alias,Values=<Site> \
  --association-name InstallFonts-<Site>

Two things to know before creating one for a production site:

  1. It fires immediately on every currently running matching instance the moment you create it -- not just future launches.
  2. If it tracks $DEFAULT (the default, as above), every future InstallFonts default-version promotion re-fires it on all matching instances automatically, live, with no review step. For a production site, pin it to a specific version instead (aws ssm create-association ... --document-version 15) and re-point it deliberately when you're ready to roll a new version out, rather than letting it track $DEFAULT.

For a production site's existing fleet specifically, use the safer rolling rollout instead of relying on immediate-apply-on-creation: see AWSSSM/Documents/Automation/InstallFontsRollout.yaml, which drains each instance's target groups, runs InstallFonts, and rejoins before moving to the next instance. Registered in AWS as InstallFontsRollout; run it against a whole cluster with:

aws ssm start-automation-execution \
  --document-name InstallFontsRollout \
  --targets "Key=tag:Alias,Values=<Site>" \
  --target-parameter-name InstanceId \
  --max-concurrency "1" \
  --max-errors "100%"

Successfully used end-to-end against both StageSaas5 and production Saas5 (including the site's Main instance's OutOfTG path -- see below).

A site's Main server, if it only runs solitons services and never serves web traffic (see OutOfTG below), should be tagged OutOfTG: Yes so InstallFontsRollout skips target-group draining for it entirely and just runs InstallFonts directly.

One-time setup per site (engineering)

Before this works for a new site, two SSM Parameter Store entries (type StringList) must exist, since the script reads them at runtime instead of taking them as arguments:

  • /pulseid/services/<site-lowercase>/<prod|stage> -- services to restart on every server for that site. Example: /pulseid/services/saas1/stagePulse AG_Pulse Design Generator,Pulse Alpha_Pulse Design Generator
  • /solitons/<site-lowercase> -- additional services that should restart only on the site's Main server (no prod/stage split -- shared by both, since staging only ever has one server). Example: /solitons/saas1Pulse Alpha_Pulse Order Processor,Pulse Alpha_Pulse Report Generator

Create with:

aws ssm put-parameter --name /pulseid/services/<site-lowercase>/<prod|stage> --type StringList --value "Service One,Service Two"
aws ssm put-parameter --name /solitons/<site-lowercase> --type StringList --value "Service Three,Service Four"

If /solitons/<site-lowercase> doesn't exist for a site, the script just skips it (no Main-only services) rather than failing.

The solitons list only applies on the instance tagged Main: <Site> (only one instance per cluster should carry this tag at a time -- InstallFonts checks Main against its own Alias tag to decide). If that Main instance never serves web traffic, also tag it OutOfTG: Yes -- InstallFontsRollout reads that tag to skip target-group draining for it entirely and just run InstallFonts directly, since there's nothing to drain.

InstallFontsRollout also skips any instance that isn't in a running state (e.g. Warm Pool standby instances, which carry the same Alias tag as the live fleet but aren't reachable by SSM) before it can waste time waiting on an SSM Agent that will never respond.