-
Notifications
You must be signed in to change notification settings - Fork 1
Home
So we have a park of hundreds of Windows computers, operating 24x7x365, hosting important user services, provided by our great software. Redundant, geographically distributed, etc. We designed the whole system in a way allowing different versions of the software to run on different hosts simultaneously and flawlessly.
Here is the question - how do we perform upgrades with minimum human intervention? Obviously, hosts should be able check for updates during periods of inactivity (maybe self initiated), and upgrade themselves as needed. Doing that on regular scheduled reboot (once a week, or a day) seems to be a natural first step.
s3i
allows us to configure each host only once, marking its group identity with configuration file URI.
After that, at each s3i service restart (computer reboot), it's software configuration will be synchronized with remote config file.
Software amy be produced by a separate CI/CD system and uploaded to version specific folders and/or AWS S3 buckets automatically. Upgarding a group of hosts to newer version would require changing a single line in group configuration file. s3i, ran manually or automatically, would compare already installed product semantic version and properties with requested one and take proper actions, like install, reinstall, or uninstall. A newer version would be installed over existing one in one pass, reinstall (to downgrade or change of properties) would require uninstall, followed by install.
s3i allows to install/upgrade/downgrade software packaged for Microsoft Installer, downloading configuration files and installers from local or remote location (http/file Uri not requiring user credentials supported, or AWS S3 with profile credentials)
command line to run on host S3/http
/------\ /--------------\
| Host | -- s3i http://../products.ini---> | products.ini | host (group of hosts)
\------/ \--------------/ configuration file
/|\ | |
| S3/GitHub/http | | links to products
| /-------\ | | to be downloaded
\------------ | *.msi |---\ <----------------/ | and installed
download \-------/ | <------------------/
and install \-------/
/|\
| /--------------\
upload \--------| CI/CD system |
to version-named subfolders \--------------/
s3i reads configuration files, specified in command line, downloads and caches product installers and properties in the staging area, and performs required uninstalls, installations, upgrades, or downgrades by invoking Windows msiexec.exe with proper arguments.
By default, s3i uses current user's [default]
AWS profile. Profile name can be changed using --profile
command line option.
Profile credentials should allow read access to all necessary S3 buckets and prefixes.
Product Installer (.msi) is expected to be able to run in unattended mode, being configured with use of public properties,
passed as msiexec command line arguments (s3i_setup
project is a simple example of such product)
Configuration file contains one or several product specifications:
- Product name, for example,
SomethingUseless
- Product installer URL, like
https://deployment.s3.amazonaws.com/useless.product/develop/1.2.3.4-beta2+test/installer.msi
- Optional set of product public properties (key/value pairs) to be passed to unattended MSI installation
Here is an example of products.ini
file:
[$define$]
$deploymentBase$ = https://deployment.s3.amazonaws.com
$ssm_param$ = ${ssm:/local/params/something}
$env_var$ = ${env:VARNAME}
; List of product name = installer URL
[$products$]
SomethingUseless = $deploymentBase$/useless.product/develop/1.2.3-beta2+test/installer.msi
EvenMoreUseless = $deploymentBase$/other.product/release/3.7.5/setup.msi
; Sections specify optional product properties
[SomethingUseless]
ImportantProperty = just an example
NotSoImportant = but we pass it anyway, just for fun
[EvenMoreUseless]
HelloWorld = You welcome!
${ssm:} and ${env:} entries get replaces with AWS Simple Systems Management Parameter values, and environment variables. These entries get expanded recursively, as other variables, and default values can be specified for missing SSM or ENV variables.
Variable reference is provided as ${type:name} or ${type:name?default}.
Consider these examples:
Assert.AreEqual(3, vars.Count);
Assert.AreEqual("== 1+222", vars["three"]);
Assert.AreEqual("1", vars["one"]);
Assert.AreEqual("== 1+222", vars.Expand("${three}"));
Assert.AreEqual("222", vars["two"]);
Assert.AreEqual("== 1+222 ==", vars["three"]);
Assert.AreEqual("== 1+222 ==", vars.Expand("${nope?${three}}"));
Assert.AreEqual("== 1+222 ==", vars.Expand("${three?nope}"));
Assert.AreEqual("== 1+222 ==", vars.Expand("${three?${two}}"));
Assert.AreEqual("== 1+222 ==", vars.Expand("${three?${nope}}"));
List of variables can also be supplied in a separate, yaml-like format file, using -z (--vars) <path.yaml>
command line option
Printing s3i help info
PS C:\Users\rdp> s3i --help
s3i: msi package batch installer v1.0.386
Usage:
s3i [<option> ...] <products> ...
Options:
-h, --help Print this help info [True]
-p, --profile <profile-name> AWS user profile name [default]
-r, --region <region-name> AWS default region name [us-east-1]
-x, --prefixes <list> List of comma separated allowed version prefixes [v,V,ver,Ver]
-e, --envvar <var-name> Environment variable name (default command line) [s3i_args]
-z, --vars <path> Path to variables file (yaml map format) []
-s, --stage <path> Path to staging folder [C:\Users\rdp\AppData\Local\Temp\2\s3i]
-c, --clean Clear staging folder at startup [False]
-m, --msiexec <path> MsiExec command [msiexec.exe]
-a, --msiargs <args> MsiExec extra args [/passive]
-t, --timeout <timespan> Installation timeout [00:03:00]
-d, --dryrun Dry run [False]
-v, --verbose Print full log info [False]
PS C:\Users\rdp>
Dry run (running with no actual installation)
C:\Users\current-user>s3i https://install.company.com.s3.amazonaws.com/Test/Group/products.ini --verbose --dryrun
Products [2]:
SomethingUseless: https://deployment.s3.amazonaws.com/useless.product/develop/1.2.3-beta2+test/installer.msii
=> C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\useless.product/develop/1.2.3-beta2+test/installer.msi
ImportantProperty = just an example
NotSoImportant = but we pass it anyway, just for fun
EvenMoreUseless = https://deployment.s3.amazonaws.com/other.product/release/3.7.5/setup.msi
=> C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\other.product\release\3.7.5\setup.msi
HelloWorld = You welcome!
Install [2]:
...
(DryRun) Download ...
(DryRun) Install https://deployment.s3.amazonaws.com/useless.product/develop/1.2.3-beta2+test/installer.msi
(DryRun) Install https://deployment.s3.amazonaws.com/other.product/release/3.7.5/setup.msi
Save C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\useless.product/develop/1.2.3-beta2+test/installer.json
Save C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\other.product\release\3.7.5\setup.json
Similar results can be achieved by setting msiexec
command to echo msiexec
:
C:\Users\current-user>s3i https://install.company.com.s3.amazonaws.com/Test/Group/products.ini --verbose --msiexec "echo msiexec"
Products [2]:
...
Install [2]:
...
(Execute) Download ...
(Execute) Install https://deployment.s3.amazonaws.com/useless.product/develop/1.2.3-beta2+test/installer.msi
msiexec /i C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\useless.product/develop/1.2.3-beta2+test/installer.msi /passive
...
Installing products from configuration file on AWS S3
C:\Users\current-user>s3i https://install.company.com.s3.amazonaws.com/Test/Group/products.ini --verbose
Products [2]:
...
Install [2]:
...
(Execute) Download ...
(Execute) Install https://deployment.s3.amazonaws.com/useless.product/develop/1.2.3-beta2+test/installer.msii
(Execute) Install https://deployment.s3.amazonaws.com/other.product/release/3.7.5/setup.msi
Save C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\useless.product/develop/1.2.3-beta2+test/installer.json
Save C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\other.product\release\3.7.5\setup.json
Upgrading one product
After changing products.ini
file: develop/1.2.3-beta2+test release/1.2.4, run the same s3i command again:
C:\Users\current-user>s3i https://install.company.com.s3.amazonaws.com/Test/Group/products.ini --verbose
Products [2]:
...
Install [1]:
...
(Execute) Download ...
(Execute) Install https://deployment.s3.amazonaws.com/useless.product/release/1.2.4/installer.msi
Save C:\Users\current-user\AppData\Local\Temp\s3i\deployment.s3.amazonaws.com\useless.product/release/1/2/4/installer.json
Downgrading or change of product properties
Can be done the same way, as upgrading, but the version in the URL should be earlier than the one already installed, and newer (or same) version of the product will be uninstalled first, and then the earlier version will be installed back.
Uninstalling product:
To uninstall a product, delete (or comment out with semicolon) product name = URL
entry
from [$products$]
section of the config file, and run s3i again.
Default command line arguments can be set in s3i_args
environment variable:
set s3i_args= https://install.company.s3.amazonaws.com/Test/Group/products.ini --verbose
Running s3i with no arguments (or at s3i
service startup) will result in using the arguments specified in this variable. To see help info, run it with --help key:
s3i --help
To be available to s3i service, system environment variable with the same name should be set, otherwise service would do essentially nothing.
s3i
service runs s3i command line tool with arguments specified in s3i_args
environment variable.
CMDLINEARGS
s3i.msi
installer property sets s3i service
parameters at service installation time, for example:
msiexec /i s3i.msi CMDLINEARGS="-d 00:00:30 -t 00:10:00 -l Warning"
Service Parameters:
-p, --path <path-to-exe> Process executable path [installed service folder\s3i.exe]
-d, --delay <timespan> Process start delay [00:00:08]
-t, --timeout <timespan> Process timeout [00:05:00]
-l, --level <log-level> Logging level [Information]