{"id":27958433,"url":"https://github.com/determinatesystems/macos-ephemeral","last_synced_at":"2025-08-10T10:15:02.864Z","repository":{"id":58500873,"uuid":"528644256","full_name":"DeterminateSystems/macos-ephemeral","owner":"DeterminateSystems","description":"Scripts and instructions for making ephemeral macOS machines with Mosyle MDM support.","archived":false,"fork":false,"pushed_at":"2024-07-24T21:06:45.000Z","size":131,"stargazers_count":83,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-07-22T20:44:37.048Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DeterminateSystems.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-25T00:54:20.000Z","updated_at":"2025-07-22T01:58:15.000Z","dependencies_parsed_at":"2023-01-29T22:45:37.200Z","dependency_job_id":"40e92a1f-2581-47a8-94d7-2e811168cef9","html_url":"https://github.com/DeterminateSystems/macos-ephemeral","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DeterminateSystems/macos-ephemeral","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeterminateSystems%2Fmacos-ephemeral","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeterminateSystems%2Fmacos-ephemeral/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeterminateSystems%2Fmacos-ephemeral/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeterminateSystems%2Fmacos-ephemeral/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DeterminateSystems","download_url":"https://codeload.github.com/DeterminateSystems/macos-ephemeral/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DeterminateSystems%2Fmacos-ephemeral/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269708514,"owners_count":24462594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-07T18:23:54.901Z","updated_at":"2025-08-10T10:15:02.825Z","avatar_url":"https://github.com/DeterminateSystems.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ephemeral macOS Deployment\n\nInternally, we use this tooling to support the testing of our software on macOS, and the Nix installer itself.\n\nThis repository makes many assumptions about your workflow and how you want to use this code.\nThese assumptions are a byproduct of the repository only being used internally, and are likely not difficult to remove.\nIf you use this code and documentation for yourself, consider sending contributions upstream that make it easier for people to use.\n\n### Included configuration.nix\n\nSet up macOS machines to automatically erase and provision themselves on a Tailscale network with Buildkite.\nAn erase/reinstall cycle can complete in less than 10 minutes, making it suitable for regular automation.\n\nThis README and tooling is public documentation for Determinate Systems, Inc.'s internal use.\nThe goal of making it public is to share the information, and foster the use of ephemeral macOS machines running Nix.\n\n\n## Requirements\n\n* We assume you are using recent Macs with either a T2 chip or Apple Silicon.\n* You're using Mosyle MDM.\n  Other MDMs might work, but we're focused on Mosyle.\n  Feel free to send pull requests supporting other MDMs.\n* Your Macs are already part of your Apple Business Manager account.\n  Once you have an Apple Business Manager account, they can provide documentation on adding existing Macs.\n\n### Hardware Requirements\n\n* [A USB-A Logitech Unifying Receiver](https://www.amazon.com/dp/B072JW9LT8) to act as a mouse and keyboard.\n  The Logitech receiver doesn't need to be configured or paired.\n  Note that macOS is very paricular about the mouse and keyboard hardware directly after erasing.\n  The TinyPilot KVM was not recognized as a mouse.\n  Using a USB hub between the mouse and the computer didn't work either.\n  A cheap Targus mouse's dongle worked as well.\n* [A USB-C thumb disk](https://www.amazon.com/dp/B09WB2RPS4) formatted and named \"CONFIG\".\n  SSH keys and other persistent state is stored here.\n* [A \"Dummy\" HMDI plug](https://www.amazon.com/dp/B07FB64V4Y) to convince macOS to stay alive.\n  A TinyPilot KVM works as well.\n  Any display should be fine.\n* [A TRRS 3.5mm male audio jack](https://www.amazon.com/dp/B01CHNZHQY) to disable built-in speakers and microphones.\n  Optional.\n\n#### Port Availability\n\n##### 2020 M1 Mac Mini\n\n* 2x Thunderbolt 4 / USB-C\n* 1x 1Gbase-T Ethernet (10Gbase-T optional)\n* 1x HDMI\n* 2x USB-A\n* 1x 3.5mm headphone jack\n\n##### 2018 Intel Mac Mini\n\n* 4x Thunderbolt 3 / USB-C\n* 1x 1Gbase-T Ethernet (10Gbase-T optional)\n* 1x HDMI\n* 2x USB-A\n* 1x 3.5mm headphone jack\n\n##### 2022 M1 (Max, Ultra) Mac Studio\n\nFront:\n\n* 2x Thunderbolt 4 / USB-C\n* 1x SDXC\n\nBack:\n\n* 4x Thunderbolt 4 / USB-C\n* 1x 10Gbase-T Ethernet\n* 1x HDMI\n* 2x USB-A\n* 1x 3.5mm headphone jack\n\n## Erasing a Mac\n\nSelect the device in `Management`,\nthen `Devices Overview`,\nthen select the `More` menu.\nClick `Erase device`.\nChange `Obliteration Behavior` to `Do not Obliterate`.\nThis requires a T2 or Apple Silicon chip.\nSee \"ObliterationBehavior\" on https://developer.apple.com/documentation/devicemanagement/erasedevicecommand/command/.\n\n# Setting up Mosyle\n\n## Automatic Enrollment\n\nAfter erasing, the machine should fully boot and configure itself without any human interaction.\nThe main tasks here are to configure the region, language, and the initial user account.\n\n### Steps\n\nOn the `Organization` tab,\nselect `Apple Basic Setup`,\nselect `Enrollment`,\nclick `Automated Device Enrollment`\nto get to the `Device Enrollment (DEP)` page.\nClick your default profile.\n\n1. Tick `If enabled, macOS will automatically advance through all Setup Assistant screens. Available for macOS 11+ when connected to Ethernet.`\n1. Select your language and region\n1. Untick `Prompt user to create an account`\n1. Move on to `Create additional local admin during Setup Assistant`\n1. Enter a full name and use `ephemeraladmin` for the username. Note that other pieces of this system depends on the user being named `ephemeraladmin`.\n1. Change the `Password` dropdown to automatically generate a password for each device\n1. Tick `Set this account to be managed.`\n1. Set `Rename devices after enrollment` to `mac-ephemeral-%SerialNumber%`\n\nClick `Save`.\n\n## Device Groups\n\nThe described automation is applied to specific machines through `Device Groups`.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Devices`,\nselect `Device Groups`,\nclick `Add Device Group`.\n\n1. Name it `Ephemeral CI`\n1. Add your macs to the group\n\nClick `Save`.\n\n## Management Profile: Software Update\n\nIn general, software updates should be applied quickly and without any user interaction.\nI want to be able to forget this machine exists after setup, so we have fully automated the update process.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Software Update`,\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Automatic Updates`\n1. All of the defaults are fine as is\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n## Management Profile: Energy Saver\n\nIf the machine sleeps it is generally not easy to wake it back up.\nOn my Mac Studio, waking it back up requires physically pressing the `Power` button on the back.\nI tried using a wireless mouse and a KVM, but neither were able to replace it.\n\nThis profile disables sleeping.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Energy Saver`,\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Don't sleep`\n1. Select the `Desktop` profile tab\n1. Set `Put the display(s) to sleep after:` to `2 minutes`\n1. Set `Put the computer to sleep after:` to `Never`\n1. Set `Put the hard disk(s) to sleep after` to `Do not configure this option`\n1. Under `Wake options`, tick `Wake for Ethernet network administrator access`\n1. Under `Other options`, tick `Start up automatically after a power failure`\n\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n## Management Profile: Security \u0026 Privacy: Granting Mosyle access to Removable Volumes\n\nOur provisioning script uses SSH keys stored on an external volume to survive wipes.\nApple widely prohibits programs from reading removable storage.\nThis means Mosyle MDM agent cannot access removable media out of the box.\n\nThis profile allows Mosyle to access removable storage.\n\nNote that we don't actually _enable_ anything in this profile except a single checkbox for the Self-Service app.\nThat is intentional: that tickbox is all we need.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Security \u0026 Privacy`,\nnear the top of the screen select the `Privacy` tab\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Allow Mosyle access to Removable Volumes`\n1. Tick `Install the Privacy Preferences Policy Control settings for the Mosyle Self-Service app to allow access to all necessary files and application data.`\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n## Management Profile: Custom Commands: Autologin as CI\n\nAutologin is necessary to allow fast erases and reprovisions.\n\nModern macOS software and hardware has two erase modes: \"Erase All Content and Settings\" (EACS) and \"Obliterate\".\nEACS takes approximately 5 minutes and involves a brief reboot after clearing the existing content and settings.\nObliterate completely erases the disk and then rewrites the operating system, annd can take up to several hours.\nObliterate is the only option on older hardware.\n\nEACS is the preferred method of implementing an ephemeral macOS machine because of the fast cycle time.\nIn order for EACS to work, the machine must have a \"Bootstrap Token\" escrowed with our MDM server.\nThe only way to escrow a bootstrap token is to have an administrative user log in.\n\nThis profile creates an administrative user with a random, unknown password, and causes it to automatically log in.\nAfter creating the user, the machine is rebooted to cause the login to happen.\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Custom Commands`,\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Autologin as CI`\n1. Select the `Code` profile tab\n1. Click the code text box\n1. Paste the contents of `auto-login.sh` into the box\n1. Click the checkmark in the top right of the Code Edit window\n1. Select the `Execution Settings` profile tab\n1. For `Execute Command` select `Only based on schedule or events`\n1. For `Event` tick `Upon Enrollment Only`\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n## Management Profile: Custom Commands: Setup SSH\n\nConfigure SSH keys and start the SSH daemon for the DEP-managed administrative user, `ephemeraladmin`.\n\nThis script runs very frequently to ensure SSH is both running, and your users' keys are on the machine.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Custom Commands`,\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Setup SSH`\n1. Select the `Code` profile tab\n1. Click the code text box\n1. Paste the contents of `setup-ssh.sh` into the box\n1. Edit the list of GitHub user names near line 9 to match your own users\n1. Click the checkmark in the top right of the Code Edit window\n1. Select the `Execution Settings` profile tab\n1. For `Execute Command` select `Only based on schedule or events`\n1. For `Event` untick `Upon Enrollment Only`\n1. For `Event` tick `Every start up of the Mac`, `Every user sign-in`, and `Every \"Device Info Update\"`.\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n\n## Management Profile: Custom Commands: Install Nix\n\nInstalls Nix and nix-darwin, which is configured to run a Buildkite agent and join our Tailscale network.]\n\nNote that right now this code assumes you're installing everything for DetSys purposes.\nIt is an explicit goal for this repository to support configuring things for *your* purposes without necessarily having to fork the repo.\nPlease open issues discussing or send PRs improving this.\n\n#### Tailscale Token\n\nFirst configure a tag to assign to ephemeral macs, by adding this to your Tailscale ACL:\n\n```json\n\t\"tagOwners\": {\n\t\t\"tag:ephemeral-mac-ci\": [\"you@example.com\"],\n\t}\n```\n\nThe actual acquisition of pre-auth tokens is done through Vault on our systems (see `setup-vault.sh`).\n\n#### Buildkite Token\n\nSave the buildkite agent token into `/Volumes/CONFIG/buildkite.token`.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Custom Commands`,\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Install Nix`\n1. Select the `Code` profile tab\n1. Click the code text box\n1. Paste the contents of `install-nix-fetcher.sh` into the box\n1. Edit the last lines (`repo`, `branch`, `cfgpath`) to point to your repository and configuration.\n   Note you can use Mosyle's tags and variables to do dynamic configuration dispatch.\n   See the end for an example.\n1. Click the checkmark in the top right of the Code Edit window\n1. Select the `Execution Settings` profile tab\n1. For `Execute Command` select `Only based on schedule or events`\n1. For `Event` untick `Upon Enrollment Only`\n1. For `Event` tick `Every user sign-in`\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n\n\n## Management Profile: Custom Commands: Show Public SSH Key\n\nShows the public key of the private key generated on the box.\n\n### Steps\n\nOn the `Management` tab,\non the left side under `Management Profiles`,\nselect `Custom Commands`,\nclick `Add new profile`.\n\nIf the profile type isn't there,\nclick `Activate New Profile Type`,\nsearch for it by name,\nclick `Activate`,\nthen click `Add new profile`.\n\n1. Name the profile `Show Public SSH Key`\n1. Select the `Code` profile tab\n1. Click the code text box\n1. Paste `cat /Volumes/CONFIG/buildkite-agent/sshkey.pub` into the box\n1. Click the checkmark in the top right of the Code Edit window\n1. Select the `Execution Settings` profile tab\n1. For `Execute Command` select `Only based on schedule or events`\n1. For `Event` untick `Upon Enrollment Only`\n1. For `Event` tick `Every start up of the Mac`\n1. For `Event` tick `Every user sign-in`\n1. For `Event` tick `Every \"Device info\" update\"`\n\nUnder `Profile Assignment`,\nclick `+ Add Assignment`,\nselect `Devices from specific Devices Group`,\ntick `Ephemeral CI`.\n\nClick `Save`.\n\n---\n\n### Using Mosyle's Variables for Dynamic Dispatch\n\n```sh\nrepo=\"https://github.com/DeterminateSystems/macos-ephemeral.git\"\nbranch=\"HEAD\"\ncfgpath=\"config.nix\"\n\nif (echo \"%Tags%\" | grep -q \"beta\"); then\n    branch=\"beta\"\n    cfgpath=\"configuration.nix\"\nfi\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeterminatesystems%2Fmacos-ephemeral","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeterminatesystems%2Fmacos-ephemeral","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeterminatesystems%2Fmacos-ephemeral/lists"}