AWS EC2 Mac build host
Use this runbook to operate the ProdTech Apple build host. The host is an interactive build environment; it is not registered as a self-hosted CI runner.
Current deployment
| Setting | Value |
|---|---|
| AWS assignment | prodtech-aws-sand-01 / AdministratorAccess through IAM Identity Center |
| Local profile | prodtech-mac-admin |
| SSO directory Region | us-east-2 |
| Workload Region | us-west-2 (Oregon) |
| Availability Zone | us-west-2a |
| Host | mac-m4.metal: Apple M4, 10 CPU cores, 24 GiB memory |
| Storage | 200 GiB encrypted gp3 EBS |
| Network access | Public outbound path; zero inbound security-group rules |
| Management | AWS Systems Manager Session Manager |
| Price verified 2026-08-07 | $1.23/hour; $29.52 mandatory 24-hour minimum; $897.90 for 730 hours |
| First allocation | 2026-08-07 22:26:47 UTC |
| First legal release | 2026-08-08 22:26:47 UTC |
The host quota is five in Oregon. Quota and an advertised offering do not guarantee physical capacity. The first allocation succeeded in us-west-2a after M4 capacity was unavailable in every advertised Ohio and Northern Virginia Availability Zone.
Post-deployment validation confirmed macOS 26.5.2 on Apple M4 (arm64), 200 GiB root storage, SSM Online, and zero inbound rules. Homebrew 6.0.15, Git 2.55.0, xcodes 2.0.3, and xcbeautify 3.2.1 are installed. Apple Command Line Tools are present; full Xcode is not yet installed.
The permanent Terraform foundation includes the VPC, four management subnets, internet egress, zero-inbound security group, instance role/profile, encrypted remote state, required tags, and a $1,000 monthly EC2 compute budget. Budget notifications warn at 80% forecast, 90% actual, and 100% actual. A budget alarm does not stop or release a host.
Sign in
Human access uses federated temporary credentials. Do not create an IAM user or access key and do not copy SSO cache data into Azure Key Vault.
Set-Location D:\git\platform
.\scripts\identity\Connect-AwsFederatedSession.ps1 -AwsProfile prodtech-mac-adminIf the cached session has expired, the helper opens the browser-based IAM Identity Center login. The access portal is https://tierpointmgmt.awsapps.com/start.
Check status and cost
.\scripts\aws-mac\Get-AwsMacBuildStatus.ps1Wait for SsmPingStatus to become Online before connecting. EstimatedHostCostUsd is a host-time estimate and excludes EBS, public IPv4, snapshots, and data transfer.
Connect by terminal
The Session Manager plugin is installed for the current Windows user. The quickest administrative shell uses Session Manager directly:
.\scripts\aws-mac\Connect-AwsMacBuildHost.ps1 -Mode ShellFor a normal ec2-user SSH session, including SSH agent forwarding for source-control operations:
.\scripts\aws-mac\Connect-AwsMacBuildHost.ps1 -Mode SshSSH is proxied through the AWS-StartSSHSession SSM document and the local ~/.ssh/prodtech-aws-mac-ed25519 key. The security group still has no inbound TCP 22 rule. Type exit to disconnect. Disconnecting does not stop host billing.
Connect to the macOS desktop
Apple's native Screen Sharing service is enabled on the first host. Its governed credential pair is in kv-ptlmgmt-platform:
tp-platform-aws-ec2-mac-build-usernametp-platform-aws-ec2-mac-build-password
The password is generated, never printed or committed, and applied to ec2-user over encrypted SSH standard input through SSM. Re-enable the service after rebuilding the Mac with:
.\scripts\aws-mac\Enable-AwsMacRemoteDesktop.ps1Configure one-click Devolutions RDM access
Download the RDM installer PowerShell script, then run it once on the Windows laptop where RDM is installed:
.\scripts\aws-mac\Install-AwsMacRdmEntry.ps1The first run installs missing official command-line dependencies when necessary. If the laptop does not already have federated sessions, it writes the governed TierPoint IAM Identity Center session and opens the AWS and Microsoft Entra browser sign-ins. Do not paste AWS profile values into a CLI wizard: the installer resolves the prodtech-aws-sand-01 account assignment and AdministratorAccess role by name, then writes the numeric account ID only to the local AWS CLI configuration. It then creates or updates HCS\ProdTech\ProdTech AWS EC2 Mac in the currently selected RDM vault.
After installation, the daily workflow is one action: double-click ProdTech AWS EC2 Mac in RDM. The entry automatically:
- Opens the private Screen Sharing tunnel through AWS Systems Manager.
- Reads the macOS username and password directly from
kv-ptlmgmt-platformwithout storing a second copy in RDM. - Connects with RDM's built-in VNC engine using Apple authentication; RealVNC is not required.
- Stops the managed tunnel after the desktop session closes.
The installer copies only the non-secret runtime scripts to %LOCALAPPDATA%\HCS\AwsMac\RDM. It is idempotent, so rerun the same command after pulling a newer version of this repository or when the RDM entry needs repair.
Manual desktop fallback
To open the desktop without the RDM events:
In PowerShell terminal A, start the SSM tunnel and keep the window open:
powershell.\scripts\aws-mac\Connect-AwsMacBuildHost.ps1 -Mode DesktopIn PowerShell terminal B, copy the password to the Windows clipboard without displaying it:
powershell.\scripts\aws-mac\Get-AwsMacRemoteDesktopCredential.ps1In Devolutions RDM, create or quick-connect a VNC entry with:
RDM field Value Name ProdTech AWS EC2 MacHost localhostPort 5900Username ec2-userPassword Paste from the clipboard Open the VNC entry. If RDM offers multiple VNC authentication modes, select macOS/Apple or username-and-password authentication rather than a password-only legacy mode.
Close the RDM session, then press Ctrl+C in terminal A to close the SSM tunnel.
The tunnel was validated locally on port 5900. Neither TCP 5900 nor TCP 22 is exposed to the internet. Do not connect the viewer to the instance's public IP or DNS name.
Move source and build artifacts
Upload a local project or file through SCP over SSM:
.\scripts\aws-mac\Copy-AwsMacBuildArtifact.ps1 `
-Direction Upload `
-LocalPath 'D:\git\my-apple-app' `
-RemotePath '/Users/ec2-user/work/'Download an archive, .ipa, .xcarchive, log, or other artifact:
.\scripts\aws-mac\Copy-AwsMacBuildArtifact.ps1 `
-Direction Download `
-RemotePath '/Users/ec2-user/work/output/MyApp.xcarchive' `
-LocalPath 'D:\tmp\MyApp.xcarchive'For Git-based work, use the SSH mode and clone/pull/push from the Mac. Prefer SSH agent forwarding, a short-lived GitHub App token, or an interactive gh auth login; do not copy a long-lived PAT or private key onto the host. Preserve source and required artifacts before teardown because the root volume is deleted with the instance.
Provision another work window
Only run the allocation command when the status command reports Allocated: False:
.\scripts\aws-mac\New-AwsMacBuildHost.ps1 `
-InstanceType mac-m4.metal `
-VolumeSizeGiB 200 `
-MaximumHourlyRateUsd 1.23 `
-ApproveCostThe command revalidates identity, price, quota, AMI, Terraform outputs, and regional capacity. It refuses to allocate when a tagged host or instance already exists. The maximum price is an approval ceiling: raise it only after reviewing the new live price.
Stop charges
Stopping or terminating only the EC2 instance does not stop Dedicated Host billing. The host must be released. No automatic destructive release timer is enabled; budget notifications do not stop the host. The teardown command refuses release before the actual 24-hour boundary:
.\scripts\aws-mac\Remove-AwsMacBuildHost.ps1 -ApproveTo terminate the instance immediately, keep the PowerShell process running until the release boundary, wait through AWS host scrubbing, and release the host:
.\scripts\aws-mac\Remove-AwsMacBuildHost.ps1 -Approve -WaitUntilEligibleThis is destructive: local source, build output, and tools on the instance's root EBS volume are deleted. Push source and preserve any approved build artifacts first. After completion, run the status command and confirm Allocated: False. The low-cost Terraform foundation remains.
Deploy or repair the foundation
Review a plan:
.\scripts\aws-mac\Deploy-AwsMacFoundation.ps1Apply the reviewed foundation plan:
.\scripts\aws-mac\Deploy-AwsMacFoundation.ps1 -ApplyTerraform state is encrypted, versioned, public-access-blocked, and stored in the fixed state Region us-east-2. Workload resources are in us-west-2. Numeric account and resource IDs are intentionally absent from this runbook.
Xcode and signing
The AWS macOS AMI supplies macOS and SSM Agent; Xcode and Apple signing identities are separate. Install the non-secret base tools idempotently:
.\scripts\aws-mac\Initialize-AwsMacBuildHost.ps1This installs Homebrew, Git, aria2, xcodes, and xcbeautify. As of 2026-08-07, Apple's latest stable release is Xcode 26.6; Xcode 27 is beta and is not selected by default. Before the first signed build:
- Select the application's minimum Apple platform and required Xcode version.
- Download and verify that Xcode version using the approved Apple Developer identity.
- Pin the Xcode selection with
xcode-selectand record it in the consuming application repo. - Store App Store Connect keys, certificate private keys, and export passwords in the scope-owned TierPoint/ProdTech vault. The GUI pair is already registered there. Commit only secret names and metadata.
- Keep the interactive host out of untrusted pull-request CI while signing material is present.
After selecting the version, connect through SSH and install it interactively. xcodes prompts for the Apple ID, 2FA, and local macOS password; do not pass those values on the command line:
xcodes list
xcodes install 26.6
sudo xcode-select --switch /Applications/Xcode-26.6.0.app/Contents/Developer
xcodebuild -version
sudo xcodebuild -license acceptAlternatively, connect through RDM, sign in to the Mac App Store, and install stable Xcode from the GUI. Record the exact Xcode version in the consuming application repo and validate a clean command-line build before importing signing material.
Failure handling
InsufficientHostCapacity: no host was allocated in that Availability Zone; retry later or deliberately select a separately priced fallback family. Quota changes do not create hardware.- Host allocated but instance launch failed: do not allocate another host. Diagnose and reuse or release the existing tagged host.
- EC2 is running but SSM is not registered: allow first boot to finish, confirm outbound access and the instance profile, then run the status command again.
- Unexpected spend: run status, preserve required work, and use the teardown command. Inspect AWS Cost Explorer after the billing data arrives; the budget itself cannot terminate resources.