Files
OrchestrAD/installer/OrchestrAD.wxs
Alphaeus Mote 2befe64a0c feat(install): idempotent Windows firewall rule; skip the blank MSI EULA
Firewall:
- On service initialize/install, create an idempotent inbound allow rule
  ("OrchestrAD") for the configured listen port, scoped to RFC 1918 private
  ranges plus CGNAT (10/8, 172.16/12, 192.168/16, 100.64/10). The rule is
  deleted-then-added so it always reflects the current port, and removed on
  service uninstall. Best-effort (needs admin; the MSI custom action and
  service run elevated); no-op off Windows. Verified the netsh rule lands
  with the expected port and remote-address scoping.

MSI:
- Skip the license/EULA page (Welcome now goes straight to the install
  directory), since it was blank. A standard short notice is kept in
  license.rtf only so the stock license control resolves at build time.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 21:46:08 -04:00

152 lines
8.9 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!--
OrchestrAD Windows installer (WiX v4+ / built with WiX 5 + the UI extension).
Wizard: Welcome -> Install directory -> Network (listen address +
port) -> Ready -> Finish. The install directory, listen address, and listen
port are recorded under HKLM\Software\Grace Solutions\OrchestrAD; the service
reads the address/port from there (env vars still override). The finish page
shows the URL and the default admin/admin credentials and can open the app.
Installs orchestrad.exe to the chosen directory (default Program Files\
OrchestrAD) and registers + starts the service via the binary's idempotent
`initialize`; uninstall runs `remove` first. Upgrades update only the binary;
the runtime data directory (SQLite database) is never touched.
Build (from repo root):
wix extension add -g WixToolset.UI.wixext
wix build -arch x64 -ext WixToolset.UI.wixext installer/OrchestrAD.wxs \
-d Version=<x.y.z> -d BinDir=<dir with orchestrad.exe> \
-d IconPath=<orchestrad.ico> -o OrchestrAD.msi
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package
Name="OrchestrAD"
Manufacturer="Grace Solutions"
Version="$(Version)"
UpgradeCode="41EC0251-FC95-4C00-8C91-EC3F9BE1F278"
Scope="perMachine"
Compressed="yes">
<SummaryInformation Description="OrchestrAD - Active Directory Rule Automation Platform" />
<MajorUpgrade DowngradeErrorMessage="A newer version of OrchestrAD is already installed." />
<MediaTemplate EmbedCab="yes" />
<Icon Id="OrchestradIcon" SourceFile="$(IconPath)" />
<Property Id="ARPPRODUCTICON" Value="OrchestradIcon" />
<!-- Wizard-configurable properties (public: settable via UI and command
line). Listen address/port default to the app's own defaults. -->
<Property Id="LISTEN_ADDRESS" Value="0.0.0.0" />
<Property Id="LISTEN_PORT" Value="18090" />
<!-- Reuse a previously recorded install directory on upgrade. -->
<Property Id="INSTALLDIR">
<RegistrySearch Id="FindInstallDir" Root="HKLM"
Key="Software\Grace Solutions\OrchestrAD"
Name="InstallDir" Type="directory" />
</Property>
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="INSTALLDIR" Name="OrchestrAD" />
</StandardDirectory>
<ComponentGroup Id="ProductComponents" Directory="INSTALLDIR">
<Component Id="OrchestradExe" Bitness="always64">
<File Id="OrchestradExe" Name="orchestrad.exe" Source="$(BinDir)/orchestrad.exe" KeyPath="yes" />
</Component>
<!-- Install location, version, and the chosen listen binding. The service
reads ListenAddress/ListenPort from here. -->
<Component Id="RegistryEntries" Bitness="always64" Guid="7C1E2A64-9B3D-4E1A-9F2B-2D0E6C8A5B10">
<RegistryKey Root="HKLM" Key="Software\Grace Solutions\OrchestrAD">
<RegistryValue Name="InstallDir" Type="string" Value="[INSTALLDIR]" KeyPath="yes" />
<RegistryValue Name="Version" Type="string" Value="$(Version)" />
<RegistryValue Name="ListenAddress" Type="string" Value="[LISTEN_ADDRESS]" />
<RegistryValue Name="ListenPort" Type="string" Value="[LISTEN_PORT]" />
</RegistryKey>
</Component>
</ComponentGroup>
<Feature Id="Main" Title="OrchestrAD" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<!-- Service lifecycle via the binary's idempotent CLI (deferred, LocalSystem). -->
<CustomAction Id="InitializeService" FileRef="OrchestradExe" ExeCommand="initialize"
Execute="deferred" Impersonate="no" Return="check" />
<CustomAction Id="RemoveService" FileRef="OrchestradExe" ExeCommand="remove"
Execute="deferred" Impersonate="no" Return="ignore" />
<!-- Optional: open the app in the browser from the finish page. -->
<CustomAction Id="LaunchApplication" Directory="INSTALLDIR"
ExeCommand="[WindowsFolder]explorer.exe &quot;https://localhost:[LISTEN_PORT]/&quot;"
Execute="immediate" Impersonate="yes" Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="RemoveService" Before="RemoveFiles" Condition="REMOVE=&quot;ALL&quot;" />
<!-- After WriteRegistryValues so the service reads the recorded
ListenAddress/ListenPort on its first start (InstallFiles alone runs
before the registry values are committed). -->
<Custom Action="InitializeService" After="WriteRegistryValues" Condition="NOT REMOVE=&quot;ALL&quot;" />
</InstallExecuteSequence>
<!-- UI: install-dir wizard plus a custom network-binding dialog. The license
page is skipped (see the WelcomeDlg publish below); this variable only
keeps the stock license control resolvable at build time. -->
<WixVariable Id="WixUILicenseRtf" Value="installer/license.rtf" />
<ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLDIR" />
<!-- Finish page: default credentials + URL, and an optional launch box. The
text is built with SetProperty so [LISTEN_PORT] is substituted. -->
<SetProperty Id="WIXUI_EXITDIALOGOPTIONALTEXT" After="CostFinalize" Sequence="both"
Value="OrchestrAD is installed and the service is running. Open https://localhost:[LISTEN_PORT]/ and sign in with username 'admin' and password 'admin'. You will be prompted to change the password at first login." />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Open OrchestrAD in my browser" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
<UI>
<!-- Custom dialog: choose the listen address and port. -->
<Dialog Id="NetworkDlg" Width="370" Height="270" Title="[ProductName] Setup">
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="WixUI_Bmp_Banner" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Title}Network binding" />
<Control Id="Description" Type="Text" X="25" Y="23" Width="320" Height="15" Transparent="yes" NoPrefix="yes" Text="Choose the address and port the OrchestrAD service listens on." />
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="AddressLabel" Type="Text" X="25" Y="70" Width="320" Height="12" NoPrefix="yes" Text="Listen address (use 0.0.0.0 for all interfaces):" />
<Control Id="AddressEdit" Type="Edit" X="25" Y="84" Width="200" Height="17" Property="LISTEN_ADDRESS" />
<Control Id="PortLabel" Type="Text" X="25" Y="115" Width="320" Height="12" NoPrefix="yes" Text="Listen port:" />
<Control Id="PortEdit" Type="Edit" X="25" Y="129" Width="80" Height="17" Property="LISTEN_PORT" />
<Control Id="Note" Type="Text" X="25" Y="160" Width="320" Height="30" Transparent="yes" NoPrefix="yes" Text="The service serves HTTPS with a self-managed certificate by default. These values are recorded in the registry; environment variables still override them." />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="Back">
<Publish Event="NewDialog" Value="InstallDirDlg" Condition="1" />
</Control>
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next">
<Publish Event="NewDialog" Value="VerifyReadyDlg" Condition="1" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
<Publish Event="SpawnDialog" Value="CancelDlg" Condition="1" />
</Control>
</Dialog>
<!-- Skip the (blank) license page: go straight from Welcome to the
install-directory dialog and route its Back button back to Welcome. -->
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Order="10" Condition="NOT Installed" />
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="10" Condition="NOT Installed" />
<!-- Insert NetworkDlg between the install-directory and ready pages. The
higher Order (3) makes these transitions win over the standard ones. -->
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="NetworkDlg" Order="3" Condition="WIXUI_INSTALLDIR_VALID=&quot;1&quot;" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="NetworkDlg" Order="3" Condition="NOT Installed" />
<!-- Launch the app from the finish page when the box is ticked. -->
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication" Condition="WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed" />
</UI>
</Package>
</Wix>