├── .gitignore ├── LICENSE.md ├── README.md ├── packer └── template.json ├── prepare_iso ├── create_firstboot_pkg.sh ├── prepare_iso.sh ├── prepare_ovf.sh ├── prepare_vdi.sh └── support │ ├── OSInstall.collection │ ├── generate_shadowhash │ ├── minstallconfig.xml │ ├── pkg-postinstall │ ├── user.plist │ └── vagrant.jpg ├── scripts ├── add-network-interface-detection.sh ├── autologin.sh ├── chef-omnibus.sh ├── parallels.sh ├── puppet.sh ├── shrink.sh ├── support │ ├── arc4random.py │ ├── generatehash.py │ ├── pbkdf2.py │ ├── plistutils.py │ ├── set_kcpassword.py │ └── shadowhash.py ├── system-update.sh ├── vagrant.sh ├── vmware.sh └── xcode-cli-tools.sh └── veewee ├── NOTICE.erb └── definition.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | packer_cache 3 | *.dmg 4 | *.box 5 | AutoPartition-* 6 | packer/output-* 7 | *.pvm 8 | *.pyc 9 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2017 Timothy Sutton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OS X templates for Packer and VeeWee 2 | 3 | This is a set of Packer templates and support scripts that will prepare an OS X installer media that performs an unattended install for use with [Packer](http://packer.io) and [VeeWee](http://github.com/jedi4ever/veewee). These were originally developed for VeeWee, but support for the VeeWee template has not been maintained since Packer's release and so it is only provided for historical purposes. I plan on removing VeeWee support from this repo soon, but VeeWee can still make use of the preparation script and the [OS X template](https://github.com/jedi4ever/veewee/tree/master/templates/OSX) remains in the core VeeWee repo. 4 | 5 | The machine built by this Packer template defaults to being configured for use with [Vagrant](http://www.vagrantup.com), and supports three Vagrant providers by using Packer's respective builders: 6 | 7 | - The [Hashicorp VMware Fusion provider](http://www.vagrantup.com/vmware) (recommended) 8 | - Vagrant's included [VirtualBox provider](http://docs.vagrantup.com/v2/virtualbox/index.html) 9 | - [Parallels](https://github.com/Parallels/vagrant-parallels) 10 | 11 | It's possible to build a machine with different admin account settings, and without the vagrant ssh keys, for use with other systems, e.g. continuous integration. 12 | 13 | Use with the Fusion provider requires Vagrant 1.3.0, and use with the VirtualBox provider Vagrant 1.6.3 if using the Rsync file sync mechanism. Note that the VeeWee template also does not have any VirtualBox or Parallels support. 14 | 15 | Provisioning steps that are defined in the template via items in the [scripts](https://github.com/timsutton/osx-vm-templates/tree/master/scripts) directory: 16 | - [Vagrant-specific configuration](https://www.vagrantup.com/docs/boxes/base.html) 17 | - VM guest tools installation if on VMware 18 | - Xcode CLI tools installation 19 | - Chef installation via the [Chef Omnitruck method](https://docs.chef.io/install_omnibus.html) 20 | - Puppet installation via [Puppetlabs Mac installers](https://downloads.puppetlabs.com/mac), both legacy and 4 21 | - Disk shrinking for VMware 22 | 23 | ## Supported guest OS versions 24 | 25 | Currently this prepare script and template supports all versions of OS X that are distributed through the App Store: OS X Lion (10.7) through El Capitan (10.11), and macOS Sierra (10.12). 26 | 27 | This project currently only supplies a single Packer template (`template.json`), so the hypervisor's configured guest OS version (i.e. `darwin12-64`) does not accurately reflect the actual installed OS. I haven't found there to be any functional differences depending on these configured guest versions. 28 | 29 | To build a VMware box of an OS version less than El Capitan (10.11), note that as of VMare Fusion 8.5.4, you will need to change the `tools_upload_flavor` from `darwin` to `darwinPre15`. 30 | 31 | ## Issue with 10.12.4 32 | 33 | **Important note:** The Sierra 10.12.4 installer seems to no longer support including custom packages as part of the installer, unless they are signed by Apple. This produces an error with the text, "macOS could not be installed on your computer.. The package veewee-config.pkg is not signed." 34 | 35 | The `prepare_iso.sh` script in this repo makes use of functionality Apple supports as part of a [NetInstall workflow](https://help.apple.com/systemimageutility/mac/10.12/#/sysmb1457f0b), but because of this (undocumented) additional requirement of additional packages needing to be signed by Apple as of 10.12.4, these tools can't currently install the necessary configuration for Packer to log in to perform additional configuration, installing guest tools, etc. The rest of the OS install still completes successfully. 36 | 37 | It may be possible to work around this by modifying the rc script directly with the contents of our postinstall script. 38 | 39 | The `prepare_vdi.sh` script uses [AutoDMG](https://github.com/MagerValp/AutoDMG)'s approach running the installer's ```OSInstall.pkg``` creating a fresh install in a temporary DMG sparse disk image which is converted into a VDI disk image using VirtualBox's command line tools. 40 | 41 | ## Preparing the ISO 42 | 43 | OS X's installer cannot be bootstrapped as easily as can Linux or Windows, and so exists the [prepare_iso.sh](https://github.com/timsutton/osx-vm-templates/blob/master/prepare_iso/prepare_iso.sh) script to perform modifications to it that will allow for an automated install and ultimately allow Packer and later, Vagrant, to have SSH access. 44 | 45 | **Note:** VirtualBox users currently have to disable Remote Management to avoid [periodic freezing](https://github.com/timsutton/osx-vm-templates/issues/43) of the VM by adding `-D DISABLE_REMOTE_MANAGEMENT` to the `prepare_iso.sh` options. See [Remote Management freezing issue](#remote-management-freezing-issue) for more information. 46 | 47 | Run the `prepare_iso.sh` script with two arguments: the path to an `Install OS X.app` or the `InstallESD.dmg` contained within, and an output directory. Root privileges are required in order to write a new DMG with the correct file ownerships. For example, with a 10.8.4 Mountain Lion installer: 48 | 49 | `sudo prepare_iso/prepare_iso.sh "/Applications/Install OS X Mountain Lion.app" out` 50 | 51 | ...should output progress information ending in something this: 52 | 53 | ``` 54 | -- MD5: dc93ded64396574897a5f41d6dd7066c 55 | -- Done. Built image is located at out/OSX_InstallESD_10.8.4_12E55.dmg. Add this iso and its checksum to your template. 56 | ``` 57 | 58 | `prepare_iso.sh` accepts command line switches to modify the details of the admin user installed by the script. 59 | 60 | * `-u` modifies the name of the admin account, defaulting to `vagrant` 61 | * `-p` modifies the password of the same account, defaulting to `vagrant` 62 | * `-i` sets the path of the account's avatar image, defaulting to `prepare_iso/support/vagrant.jpg` 63 | 64 | For example: 65 | 66 | `sudo prepare_iso/prepare_iso.sh -u admin -p password -i /path/to/image.jpg "/Applications/Install OS X Mountain Lion.app" out` 67 | 68 | Additionally, flags can be set to disable certain default configuration options. 69 | 70 | * `-D DISABLE_REMOTE_MANAGEMENT` disables the Remote Management service. 71 | * `-D DISABLE_SCREEN_SHARING` disables the Screen Sharing service. 72 | 73 | #### Clone this repository 74 | 75 | The `prepare_iso.sh` script needs the `support` directory and its content. In other words, the easiest way to run the script is after cloning this repository. 76 | 77 | ## Use with Packer 78 | 79 | The path can now be added to your Packer template or provided as [user variables](http://www.packer.io/docs/templates/user-variables.html). The `packer` directory contains a template that can be used with the `vmware-iso` and `virtualbox-iso` builders. The checksum does not need to be added because the `iso_checksum_type` has been set to "none". The `veewee` directory contains a definition, though as mentioned above it is not currently being maintained. 80 | 81 | The Packer template adds some additional VM options required for OS X guests. Note that the paths given in the Packer template's `iso_url` builder key accepts file paths, both absolute and relative (to the current working directory). 82 | 83 | Given the above output, we could run then run packer: 84 | 85 | ```sh 86 | cd packer 87 | packer build \ 88 | -var iso_url=../out/OSX_InstallESD_10.8.4_12E55.dmg \ 89 | template.json 90 | ``` 91 | 92 | You might also use the `-only` option to restrict to either the `vmware-iso` or `virtualbox-iso` builders. 93 | 94 | If you modified the name or password of the admin account in the `prepare_iso` stage, you'll need to pass in the modified details as packer variables. You can also prevent the vagrant SSH keys from being installed for that user. 95 | 96 | For example: 97 | 98 | ``` 99 | packer build \ 100 | -var iso_url=../out/OSX_InstallESD_10.8.4_12E55.dmg \ 101 | -var username=youruser \ 102 | -var password=yourpassword \ 103 | -var install_vagrant_keys=false \ 104 | template.json 105 | ``` 106 | 107 | ### Building to a device with more space 108 | 109 | Local VM builds take up a lot of space. It's possible to make packer work in different directories. 110 | 111 | * [`PACKER_CACHE_DIR`](https://www.packer.io/docs/other/environment-variables.html#packer_cache_dir) is an out-of-the-box environment variable that configures where it will cache ISOs etc. 112 | * `PACKER_OUTPUT_DIR`: configure where packer will build artifacts (like OVF files) to 113 | * `PACKER_VAGRANT_BOX_DIR`: configure where packer will build vagrant boxes via the post-processor to. 114 | 115 | **Note:** don't make `PACKER_OUTPUT_DIR` and `PACKER_VAGRANT_BOX_DIR` the same place. `keep_input_artifacts` in the post-processor defaults to `false`, and it removes them by removing the directory, not the individual files. So if you use the same place, you'll end up with no output at all (packer `v1.0.0`). 116 | 117 | ## Automated installs on OS X 118 | 119 | OS X's installer supports a kind of bootstrap install functionality similar to Linux and Windows, however it must be invoked using pre-existing files placed on the booted installation media. This approach is roughly equivalent to that used by Apple's System Image Utility for deploying automated OS X installations and image restoration. 120 | 121 | The `prepare_iso.sh` script in this repo takes care of mounting and modifying a vanilla OS X installer downloaded from the Mac App Store. The resulting .dmg file can then be added to the Packer template. Because the preparation is done up front, no boot command sequences, attached devices or web server access is required. 122 | 123 | More details as to the modifications to the installer media are provided in the comments of the script. 124 | 125 | 126 | ## Automated GUI logins 127 | 128 | For some kinds of automated tasks, it may be necessary to have an active GUI login session (for example, test suites requiring a GUI, or Jenkins SSH slaves requiring a window server for their tasks). The Packer templates support enabling this automatically by using the `autologin` user variable, which can be set to `1` or `true`, for example: 129 | 130 | `packer build -var autologin=true template.json` 131 | 132 | This was easily made possible thanks to Per Olofsson's [CreateUserPkg](http://magervalp.github.com/CreateUserPkg) utility, which was used to help create the box's vagrant user in the `prepare_iso` script, and which also supports generating the magic kcpassword file with a particular hash format to set up the auto-login. 133 | 134 | ## Configuration management 135 | 136 | By default, the packer template does not install the Chef or Puppet configuration management tools. You can enable the installation of configuration management by setting the `chef_version`, `puppet_agent_version`, `puppet_version`, `facter_version`, and `hiera_version` variables to `latest`, or to a specific version. 137 | 138 | To install the latest version of Chef: 139 | 140 | ``` 141 | packer build -var chef_version=latest template.json 142 | ``` 143 | 144 | To install the last version of Puppet Agent: 145 | 146 | ``` 147 | packer build -var pupet_agent_version=latest template.json 148 | ``` 149 | 150 | To install the last versions of the deprecated standalone Puppet, Facter and Hiera packages: 151 | 152 | ``` 153 | packer build -var puppet_version=latest facter_version=latest hiera_version=latest template.json 154 | ``` 155 | 156 | ## Xcode Command Line Tools 157 | 158 | The Xcode CLI tools are installed by the packer template by default. To disable the installation, set the `install_xcode_cli_tools` variable to `false`: 159 | 160 | ``` 161 | packer build -var install_xcode_cli_tools=false template.json 162 | ``` 163 | 164 | ## System updates 165 | 166 | Packer will instruct the system to download and install all available OS X updates, if you want to disable this default behaviour, use `update_system` variable: 167 | 168 | ``` 169 | packer build -var update_system=0 template.json 170 | ``` 171 | 172 | ## Provisioning delay 173 | 174 | In some cases, it may be helpful to insert a delay into the beginning of the provisioning process. Adding a delay of about 30 seconds may help subsequent provisioning steps that install software from the internet complete successfully. By default, the delay is set to `0`, but you can change the delay by setting the `provisioning_delay` variable: 175 | 176 | ``` 177 | packer build -var provisioning_delay=30 template.json` 178 | ``` 179 | 180 | ## VirtualBox support 181 | 182 | VirtualBox support is thanks entirely to contributions by [Matt Behrens (@zigg)](https://github.com/zigg) to this repo, Vagrant and Packer. 183 | 184 | ### Caveats 185 | 186 | #### Remote Management freezing issue 187 | 188 | The default `prepare_iso.sh` configuration enables Remote Management during installation, which causes the resulting virtual machine to [periodically freeze](https://github.com/timsutton/osx-vm-templates/issues/43). You can avoid enabling Remote Management when using `prepare_iso.sh` by passing `-D DISABLE_REMOTE_MANAGEMENT` this: 189 | 190 | ``` 191 | sudo ./prepare_iso/prepare_iso.sh -D DISABLE_REMOTE_MANAGEMENT "/Applications/Install OS X El Capitan.app" out 192 | ``` 193 | 194 | 195 | #### Shared folders 196 | 197 | Oracle's support for OS X in VirtualBox is very limited, including the lack of guest tools to provide a shared folder mechanism. If using the VirtualBox provider in Vagrant, you will need to configure the shared folder that's set up by default (current folder mapped to `/vagrant`) to use either the `rsync` or `nfs` synced folder mechanisms. You can do this like any other synced folder config in your Vagrantfile: 198 | 199 | ```ruby 200 | Vagrant.configure("2") do |config| 201 | config.vm.provider "virtualbox" do |vb| 202 | config.vm.synced_folder ".", "/vagrant", type: "rsync" 203 | end 204 | end 205 | ``` 206 | 207 | ## Alternative method using ```prepare_vdi.sh```, ```prepare_ovf.sh``` and packer virtualbox-ovf 208 | 209 | This approach requires VirtualBox and unfortunately almost 30GB of free space. 210 | 211 | #### Installing and exporting to a VirtualBox virtual disk image. 212 | 213 | The ```prepare_vdi.sh``` command will run the installer's ```OSInstall.pkg``` creating a fresh install in a temporary disk image which is converted into a VDI disk image. 214 | 215 | ``` 216 | cd packer 217 | sudo ../prepare_iso/prepare_vdi.sh \ 218 | -D DISABLE_REMOTE_MANAGEMENT \ 219 | -o macOS_10.12.vdi \ 220 | /Applications/Install\ macOS\ Sierra.app/ \ 221 | . 222 | ``` 223 | 224 | #### Generating a VirtualBox machine and exporting it into packed virtual machine OVF 225 | 226 | The ```prepare_ovf.sh``` command takes a virtual image disk and adds it into a temporary VirtualBox machine which is exported into a packed virtual machine using the OVF (Open Virtualization Format). 227 | 228 | ``` 229 | ../prepare_iso/prepare_ovf.sh \ 230 | macOS_10.12.vdi 231 | ``` 232 | 233 | #### Generating a packer box using the virtualbox-ovf builder 234 | 235 | Finally the virtualbox-ovf allows to use the previously generated exported virtual machine to generate the provisioned packer box. 236 | 237 | ``` 238 | packer build \ 239 | -var provisioning_delay=30 \ 240 | -var source_path=macOS_10.12.ovf \ 241 | template.json 242 | ``` 243 | 244 | ## Box sizes 245 | 246 | A built box with CLI tools, Puppet and Chef is over 5GB in size. It might be advisable to remove (with care) some unwanted applications in an additional postinstall script. It should also be possible to modify the OS X installer package to install fewer components, but this is non-trivial. One can also supply a custom "choice changes XML" file to modify the installer choices in a supported way, but from my testing, this only allows removing several auxiliary packages that make up no more than 6-8% of the installed footprint (for example, multilingual voices and dictionary files). 247 | 248 | 249 | ## Alternate approaches to VM provisioning 250 | 251 | Joe Chilcote has written a tool, [vfuse](https://github.com/chilcote/vfuse), which converts a never-booted OS X image (such as created with a tool like [AutoDMG](https://github.com/MagerValp/AutoDMG)) into a VMDK and configures a VMware Fusion VM. vfuse can also configure a Packer template alongside the VM, configured with the `vmware-vmx` builder. 252 | -------------------------------------------------------------------------------- /packer/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "builders": [ 3 | { 4 | "boot_wait": "5s", 5 | "disk_size": 40960, 6 | "guest_os_type": "win-8", 7 | "iso_checksum_type": "none", 8 | "iso_url": "{{user `iso_url`}}", 9 | "output_directory": "{{user `output_directory`}}", 10 | "shutdown_command": "echo '{{user `username`}}'|sudo -S shutdown -h now", 11 | "ssh_port": 22, 12 | "ssh_username": "{{user `username`}}", 13 | "ssh_password": "{{user `password`}}", 14 | "ssh_wait_timeout": "10000s", 15 | "parallels_tools_flavor": "mac", 16 | "type": "parallels-iso", 17 | "prlctl": [ 18 | ["set", "{{.Name}}", "--memsize", "2048"], 19 | ["set", "{{.Name}}", "--memquota", "512:2048"], 20 | ["set", "{{.Name}}", "--cpus", "2"], 21 | ["set", "{{.Name}}", "--distribution", "macosx"], 22 | ["set", "{{.Name}}", "--3d-accelerate", "highest"], 23 | ["set", "{{.Name}}", "--high-resolution", "off"], 24 | ["set", "{{.Name}}", "--auto-share-camera", "off"], 25 | ["set", "{{.Name}}", "--auto-share-bluetooth", "off"], 26 | ["set", "{{.Name}}", "--on-window-close", "keep-running"], 27 | ["set", "{{.Name}}", "--shf-host", "off"] 28 | ] 29 | }, 30 | { 31 | "boot_wait": "2s", 32 | "disk_size": 40960, 33 | "guest_os_type": "darwin12-64", 34 | "iso_checksum_type": "none", 35 | "iso_url": "{{user `iso_url`}}", 36 | "output_directory": "{{user `output_directory`}}", 37 | "shutdown_command": "echo '{{user `username`}}'|sudo -S shutdown -h now", 38 | "skip_compaction": true, 39 | "ssh_port": 22, 40 | "ssh_username": "{{user `username`}}", 41 | "ssh_password": "{{user `password`}}", 42 | "ssh_wait_timeout": "10000s", 43 | "tools_upload_flavor": "darwin", 44 | "type": "vmware-iso", 45 | "vmx_data": { 46 | "cpuid.coresPerSocket": "1", 47 | "memsize": "2048", 48 | "numvcpus": "1", 49 | "firmware": "efi", 50 | "keyboardAndMouseProfile": "macProfile", 51 | "smc.present": "TRUE", 52 | "hpet0.present": "TRUE", 53 | "ich7m.present": "TRUE", 54 | "ehci.present": "TRUE", 55 | "usb.present": "TRUE" 56 | } 57 | }, 58 | { 59 | "boot_wait": "2s", 60 | "disk_size": 40960, 61 | "guest_additions_mode": "disable", 62 | "guest_os_type": "MacOS1011_64", 63 | "hard_drive_interface": "sata", 64 | "iso_checksum_type": "none", 65 | "iso_interface": "sata", 66 | "iso_url": "{{user `iso_url`}}", 67 | "output_directory": "{{user `output_directory`}}", 68 | "shutdown_command": "echo '{{user `username`}}'|sudo -S shutdown -h now", 69 | "ssh_port": 22, 70 | "ssh_username": "{{user `username`}}", 71 | "ssh_password": "{{user `password`}}", 72 | "ssh_wait_timeout": "10000s", 73 | "type": "virtualbox-iso", 74 | "vboxmanage": [ 75 | ["modifyvm", "{{.Name}}", "--audiocontroller", "hda"], 76 | ["modifyvm", "{{.Name}}", "--boot1", "dvd"], 77 | ["modifyvm", "{{.Name}}", "--boot2", "disk"], 78 | ["modifyvm", "{{.Name}}", "--chipset", "ich9"], 79 | ["modifyvm", "{{.Name}}", "--firmware", "efi"], 80 | ["modifyvm", "{{.Name}}", "--hpet", "on"], 81 | ["modifyvm", "{{.Name}}", "--keyboard", "usb"], 82 | ["modifyvm", "{{.Name}}", "--memory", "2048"], 83 | ["modifyvm", "{{.Name}}", "--mouse", "usbtablet"], 84 | ["modifyvm", "{{.Name}}", "--vram", "128"], 85 | ["storagectl", "{{.Name}}", "--name", "IDE Controller", "--remove"] 86 | ] 87 | }, 88 | { 89 | "boot_wait": "2s", 90 | "guest_additions_mode": "disable", 91 | "source_path": "{{user `source_path`}}", 92 | "shutdown_command": "echo '{{user `username`}}'|sudo -S shutdown -h now", 93 | "ssh_port": 22, 94 | "ssh_username": "{{user `username`}}", 95 | "ssh_password": "{{user `password`}}", 96 | "ssh_wait_timeout": "10000s", 97 | "type": "virtualbox-ovf", 98 | "vboxmanage": [ 99 | ["modifyvm", "{{.Name}}", "--audiocontroller", "hda"], 100 | ["modifyvm", "{{.Name}}", "--chipset", "ich9"], 101 | ["modifyvm", "{{.Name}}", "--firmware", "efi"], 102 | ["modifyvm", "{{.Name}}", "--hpet", "on"], 103 | ["modifyvm", "{{.Name}}", "--keyboard", "usb"], 104 | ["modifyvm", "{{.Name}}", "--memory", "4096"], 105 | ["modifyvm", "{{.Name}}", "--mouse", "usbtablet"], 106 | ["modifyvm", "{{.Name}}", "--vram", "128"] 107 | ] 108 | } 109 | ], 110 | "min_packer_version": "0.7.0", 111 | "post-processors": [ 112 | { 113 | "type": "vagrant", 114 | "output": "{{user `vagrant_box_directory`}}/packer_{{.BuildName}}_{{.Provider}}.box" 115 | } 116 | ], 117 | "provisioners": [ 118 | { 119 | "type": "shell-local", 120 | "command": "sleep {{user `provisioning_delay`}}" 121 | }, 122 | { 123 | "destination": "/private/tmp/set_kcpassword.py", 124 | "source": "../scripts/support/set_kcpassword.py", 125 | "type": "file" 126 | }, 127 | { 128 | "execute_command": "chmod +x {{ .Path }}; sudo {{ .Vars }} {{ .Path }}", 129 | "scripts": [ 130 | "../scripts/vagrant.sh", 131 | "../scripts/vmware.sh", 132 | "../scripts/parallels.sh", 133 | "../scripts/xcode-cli-tools.sh", 134 | "../scripts/chef-omnibus.sh", 135 | "../scripts/puppet.sh", 136 | "../scripts/add-network-interface-detection.sh", 137 | "../scripts/autologin.sh", 138 | "../scripts/system-update.sh", 139 | "../scripts/shrink.sh" 140 | ], 141 | "environment_vars": [ 142 | "AUTOLOGIN={{user `autologin`}}", 143 | "CHEF_VERSION={{user `chef_version`}}", 144 | "FACTER_VERSION={{user `facter_version`}}", 145 | "HIERA_VERSION={{user `hiera_version`}}", 146 | "INSTALL_VAGRANT_KEYS={{user `install_vagrant_keys`}}", 147 | "NOCM={{user `nocm`}}", 148 | "INSTALL_XCODE_CLI_TOOLS={{user `install_xcode_cli_tools`}}", 149 | "PASSWORD={{user `password`}}", 150 | "PUPPET_VERSION={{user `puppet_version`}}", 151 | "PUPPET_AGENT_VERSION={{user `puppet_agent_version`}}", 152 | "UPDATE_SYSTEM={{user `update_system`}}", 153 | "USERNAME={{user `username`}}" 154 | ], 155 | "type": "shell" 156 | } 157 | ], 158 | "variables": { 159 | "autologin": "false", 160 | "chef_version": "none", 161 | "facter_version": "none", 162 | "hiera_version": "none", 163 | "install_vagrant_keys": "true", 164 | "install_xcode_cli_tools": "true", 165 | "iso_url": "OSX_InstallESD_10.11.1_15B42.dmg", 166 | "output_directory": "{{env `PACKER_OUTPUT_DIR`}}", 167 | "password": "vagrant", 168 | "provisioning_delay": "0", 169 | "puppet_version": "none", 170 | "puppet_agent_version": "none", 171 | "update_system": "true", 172 | "username": "vagrant", 173 | "vagrant_box_directory": "{{env `PACKER_VAGRANT_BOX_DIR`}}" 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /prepare_iso/create_firstboot_pkg.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Handle the possibility of being sourced from outside this project dir 4 | called=$_ 5 | if [[ $called != "${0}" ]]; then 6 | SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" 7 | else 8 | SCRIPT_DIR="$(cd "$(dirname "$0")" || exit; pwd)" 9 | fi 10 | SUPPORT_DIR="$SCRIPT_DIR/support" 11 | 12 | # Parse the optional command line switches 13 | USER="vagrant" 14 | PASSWORD="vagrant" 15 | IMAGE_PATH="$SUPPORT_DIR/vagrant.jpg" 16 | 17 | # Flags 18 | DISABLE_REMOTE_MANAGEMENT=0 19 | DISABLE_SCREEN_SHARING=0 20 | DISABLE_SIP=0 21 | 22 | render_template() { 23 | eval "echo \"$(cat "$1")\"" 24 | } 25 | 26 | create_firstboot_pkg() { 27 | # payload items 28 | mkdir -p "$SUPPORT_DIR/pkgroot/private/var/db/dslocal/nodes/Default/users" 29 | mkdir -p "$SUPPORT_DIR/pkgroot/private/var/db/shadow/hash" 30 | BASE64_IMAGE=$(openssl base64 -in "$IMAGE_PATH") 31 | ShadowHashData=$($SCRIPT_DIR/../scripts/support/generatehash.py "$PASSWORD") 32 | # Replace USER and BASE64_IMAGE in the user.plist file with the actual user and image 33 | render_template "$SUPPORT_DIR/user.plist" > "$SUPPORT_DIR/pkgroot/private/var/db/dslocal/nodes/Default/users/$USER.plist" 34 | USER_GUID=$(/usr/libexec/PlistBuddy -c 'Print :generateduid:0' "$SUPPORT_DIR/user.plist") 35 | # Generate a shadowhash from the supplied password 36 | "$SUPPORT_DIR/generate_shadowhash" "$PASSWORD" > "$SUPPORT_DIR/pkgroot/private/var/db/shadow/hash/$USER_GUID" 37 | 38 | # postinstall script 39 | mkdir -p "$SUPPORT_DIR/tmp/Scripts" 40 | cat "$SUPPORT_DIR/pkg-postinstall" \ 41 | | sed -e "s/__USER__PLACEHOLDER__/${USER}/" \ 42 | | sed -e "s/__DISABLE_REMOTE_MANAGEMENT__/${DISABLE_REMOTE_MANAGEMENT}/" \ 43 | | sed -e "s/__DISABLE_SCREEN_SHARING__/${DISABLE_SCREEN_SHARING}/" \ 44 | | sed -e "s/__DISABLE_SIP__/${DISABLE_SIP}/" \ 45 | > "$SUPPORT_DIR/tmp/Scripts/postinstall" 46 | chmod a+x "$SUPPORT_DIR/tmp/Scripts/postinstall" 47 | 48 | # build it 49 | BUILT_COMPONENT_PKG="$SUPPORT_DIR/tmp/veewee-config-component.pkg" 50 | BUILT_PKG="$SUPPORT_DIR/tmp/veewee-config.pkg" 51 | pkgbuild --quiet \ 52 | --root "$SUPPORT_DIR/pkgroot" \ 53 | --scripts "$SUPPORT_DIR/tmp/Scripts" \ 54 | --identifier com.vagrantup.veewee-config \ 55 | --version 0.1 \ 56 | "$BUILT_COMPONENT_PKG" 57 | productbuild \ 58 | --package "$BUILT_COMPONENT_PKG" \ 59 | "$BUILT_PKG" 60 | rm -rf "$SUPPORT_DIR/pkgroot" 61 | } 62 | -------------------------------------------------------------------------------- /prepare_iso/prepare_iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # Preparation script for an OS X automated installation for use with VeeWee/Packer/Vagrant 4 | # 5 | # What the script does, in more detail: 6 | # 7 | # 1. Mounts the InstallESD.dmg using a shadow file, so the original DMG is left 8 | # unchanged. 9 | # 2. Modifies the BaseSystem.dmg within in order to add an additional 'rc.cdrom.local' 10 | # file in /etc, which is a supported local configuration sourced in at boot time 11 | # by the installer environment. This file contains instructions to erase and format 12 | # 'disk0', presumably the hard disk attached to the VM. 13 | # 3. A 'veewee-config.pkg' installer package is built, which is added to the OS X 14 | # install by way of the OSInstall.collection file. This package creates the 15 | # 'vagrant' user, configures sshd and sudoers, and disables setup assistants. 16 | # 4. veewee-config.pkg and the various support utilities are copied, and the disk 17 | # image is saved to the output path. 18 | # 19 | # Thanks: 20 | # Idea and much of the implementation thanks to Pepijn Bruienne, who's also provided 21 | # some process notes here: https://gist.github.com/4542016. The sample minstallconfig.xml, 22 | # use of OSInstall.collection and readme documentation provided with Greg Neagle's 23 | # createOSXInstallPkg tool also proved very helpful. (http://code.google.com/p/munki/wiki/InstallingOSX) 24 | # 25 | # User creation via package install method also credited to Greg, and made easy with Per 26 | # Olofsson's CreateUserPkg (http://magervalp.github.io/CreateUserPkg) 27 | # 28 | # Antony Blakey for updates to support OS X 10.11: 29 | # https://github.com/timsutton/osx-vm-templates/issues/40 30 | 31 | usage() { 32 | cat < 44 | Sets the username of the root user, defaults to 'vagrant'. 45 | 46 | -p 47 | Sets the password of the root user, defaults to 'vagrant'. 48 | 49 | -i 50 | Sets the path of the avatar image for the root user, defaulting to the vagrant icon. 51 | 52 | -D 53 | Sets the specified flag. Valid flags are: 54 | DISABLE_REMOTE_MANAGEMENT 55 | DISABLE_SCREEN_SHARING 56 | DISABLE_SIP 57 | 58 | EOF 59 | } 60 | 61 | cleanup() { 62 | hdiutil detach -quiet -force "$MNT_ESD" || echo > /dev/null 63 | hdiutil detach -quiet -force "$MNT_BASE_SYSTEM" || echo > /dev/null 64 | rm -rf "$MNT_ESD" "$MNT_BASE_SYSTEM" "$BASE_SYSTEM_DMG_RW" "$SHADOW_FILE" 65 | } 66 | 67 | trap cleanup EXIT INT TERM 68 | 69 | 70 | msg_status() { 71 | echo "\033[0;32m-- $1\033[0m" 72 | } 73 | msg_error() { 74 | echo "\033[0;31m-- $1\033[0m" 75 | } 76 | 77 | if [ $# -eq 0 ]; then 78 | usage 79 | exit 1 80 | fi 81 | 82 | SCRIPT_DIR="$(cd "$(dirname "$0")"; pwd)" 83 | SUPPORT_DIR="$SCRIPT_DIR/support" 84 | 85 | # Import shared code to generate first boot pkgs 86 | FIRST_BOOT_PKG_SCRIPT="$SCRIPT_DIR/create_firstboot_pkg.sh" 87 | [ -f "$FIRST_BOOT_PKG_SCRIPT" ] && . "$FIRST_BOOT_PKG_SCRIPT" 88 | 89 | # Parse the optional command line switches 90 | USER="vagrant" 91 | PASSWORD="vagrant" 92 | IMAGE_PATH="$SUPPORT_DIR/vagrant.jpg" 93 | 94 | # Flags 95 | DISABLE_REMOTE_MANAGEMENT=0 96 | DISABLE_SCREEN_SHARING=0 97 | DISABLE_SIP=0 98 | 99 | while getopts u:p:i:D: OPT; do 100 | case "$OPT" in 101 | u) 102 | USER="$OPTARG" 103 | ;; 104 | p) 105 | PASSWORD="$OPTARG" 106 | ;; 107 | i) 108 | IMAGE_PATH="$OPTARG" 109 | ;; 110 | D) 111 | if [ x${!OPTARG} = x0 ]; then 112 | eval $OPTARG=1 113 | elif [ x${!OPTARG} != x1 ]; then 114 | msg_error "Unknown flag: ${OPTARG}" 115 | usage 116 | exit 1 117 | fi 118 | ;; 119 | \?) 120 | usage 121 | exit 1 122 | ;; 123 | esac 124 | done 125 | 126 | # Remove the switches we parsed above. 127 | shift $(expr $OPTIND - 1) 128 | 129 | if [ $(id -u) -ne 0 ]; then 130 | msg_error "This script must be run as root, as it saves a disk image with ownerships enabled." 131 | exit 1 132 | fi 133 | 134 | ESD="$1" 135 | if [ ! -e "$ESD" ]; then 136 | msg_error "Input installer image $ESD could not be found! Exiting.." 137 | exit 1 138 | fi 139 | 140 | if [ -d "$ESD" ]; then 141 | # we might be an install .app 142 | if [ -e "$ESD/Contents/SharedSupport/InstallESD.dmg" ]; then 143 | ESD="$ESD/Contents/SharedSupport/InstallESD.dmg" 144 | else 145 | msg_error "Can't locate an InstallESD.dmg in this source location $ESD!" 146 | fi 147 | fi 148 | 149 | VEEWEE_DIR="$(cd "$SCRIPT_DIR/../../../"; pwd)" 150 | VEEWEE_UID=$(/usr/bin/stat -f %u "$VEEWEE_DIR") 151 | VEEWEE_GID=$(/usr/bin/stat -f %g "$VEEWEE_DIR") 152 | DEFINITION_DIR="$(cd "$SCRIPT_DIR/.."; pwd)" 153 | 154 | if [ "$2" = "" ]; then 155 | msg_error "Currently an explicit output directory is required as the second argument." 156 | exit 1 157 | # The rest is left over from the old prepare_veewee_iso.sh script. Not sure if we 158 | # should leave in this functionality to automatically locate the veewee directory. 159 | DEFAULT_ISO_DIR=1 160 | OLDPWD=$(pwd) 161 | cd "$SCRIPT_DIR" 162 | # default to the veewee/iso directory 163 | if [ ! -d "../../../iso" ]; then 164 | mkdir "../../../iso" 165 | chown $VEEWEE_UID:$VEEWEE_GID "../../../iso" 166 | fi 167 | OUT_DIR="$(cd "$SCRIPT_DIR"; cd ../../../iso; pwd)" 168 | cd "$OLDPWD" # Rest of script depends on being in the working directory if we were passed relative paths 169 | else 170 | OUT_DIR="$2" 171 | fi 172 | 173 | if [ ! -d "$OUT_DIR" ]; then 174 | msg_status "Destination dir $OUT_DIR doesn't exist, creating.." 175 | mkdir -p "$OUT_DIR" 176 | fi 177 | 178 | if [ -e "$ESD.shadow" ]; then 179 | msg_status "Removing old shadow file.." 180 | rm "$ESD.shadow" 181 | fi 182 | 183 | MNT_ESD=$(/usr/bin/mktemp -d /tmp/veewee-osx-esd.XXXX) 184 | SHADOW_FILE=$(/usr/bin/mktemp /tmp/veewee-osx-shadow.XXXX) 185 | rm "$SHADOW_FILE" 186 | msg_status "Attaching input OS X installer image with shadow file.." 187 | hdiutil attach "$ESD" -mountpoint "$MNT_ESD" -shadow "$SHADOW_FILE" -nobrowse -owners on 188 | if [ $? -ne 0 ]; then 189 | [ ! -e "$ESD" ] && msg_error "Could not find $ESD in $(pwd)" 190 | msg_error "Could not mount $ESD on $MNT_ESD" 191 | exit 1 192 | fi 193 | 194 | msg_status "Mounting BaseSystem.." 195 | BASE_SYSTEM_DMG="$MNT_ESD/BaseSystem.dmg" 196 | MNT_BASE_SYSTEM=$(/usr/bin/mktemp -d /tmp/veewee-osx-basesystem.XXXX) 197 | [ ! -e "$BASE_SYSTEM_DMG" ] && msg_error "Could not find BaseSystem.dmg in $MNT_ESD" 198 | hdiutil attach "$BASE_SYSTEM_DMG" -mountpoint "$MNT_BASE_SYSTEM" -nobrowse -owners on 199 | if [ $? -ne 0 ]; then 200 | msg_error "Could not mount $BASE_SYSTEM_DMG on $MNT_BASE_SYSTEM" 201 | exit 1 202 | fi 203 | SYSVER_PLIST_PATH="$MNT_BASE_SYSTEM/System/Library/CoreServices/SystemVersion.plist" 204 | 205 | DMG_OS_VERS=$(/usr/libexec/PlistBuddy -c 'Print :ProductVersion' "$SYSVER_PLIST_PATH") 206 | DMG_OS_VERS_MAJOR=$(echo $DMG_OS_VERS | awk -F "." '{print $2}') 207 | DMG_OS_VERS_MINOR=$(echo $DMG_OS_VERS | awk -F "." '{print $3}') 208 | DMG_OS_BUILD=$(/usr/libexec/PlistBuddy -c 'Print :ProductBuildVersion' "$SYSVER_PLIST_PATH") 209 | msg_status "OS X version detected: 10.$DMG_OS_VERS_MAJOR.$DMG_OS_VERS_MINOR, build $DMG_OS_BUILD" 210 | 211 | OUTPUT_DMG="$OUT_DIR/OSX_InstallESD_${DMG_OS_VERS}_${DMG_OS_BUILD}.dmg" 212 | if [ -e "$OUTPUT_DMG" ]; then 213 | msg_error "Output file $OUTPUT_DMG already exists! We're not going to overwrite it, exiting.." 214 | hdiutil detach -force "$MNT_ESD" 215 | exit 1 216 | fi 217 | 218 | # Build our post-installation pkg that will create a user and enable ssh 219 | msg_status "Making firstboot installer pkg.." 220 | create_firstboot_pkg 221 | if [ -z "$BUILT_PKG" ] || [ ! -e "$BUILT_PKG" ]; then 222 | msg_error "Failed building the firstboot installer pkg, exiting.." 223 | exit 1 224 | fi 225 | 226 | # We'd previously mounted this to check versions 227 | hdiutil detach "$MNT_BASE_SYSTEM" 228 | 229 | BASE_SYSTEM_DMG_RW="$(/usr/bin/mktemp /tmp/veewee-osx-basesystem-rw.XXXX).dmg" 230 | 231 | msg_status "Creating empty read-write DMG located at $BASE_SYSTEM_DMG_RW.." 232 | hdiutil create -o "$BASE_SYSTEM_DMG_RW" -size 10g -layout SPUD -fs HFS+J 233 | hdiutil attach "$BASE_SYSTEM_DMG_RW" -mountpoint "$MNT_BASE_SYSTEM" -nobrowse -owners on 234 | 235 | msg_status "Restoring ('asr restore') the BaseSystem to the read-write DMG.." 236 | # This asr restore was needed as of 10.11 DP7 and up. See 237 | # https://github.com/timsutton/osx-vm-templates/issues/40 238 | # 239 | # Note that when the restore completes, the volume is automatically re-mounted 240 | # and not with the '-nobrowse' option. It's an annoyance we could possibly fix 241 | # in the future.. 242 | asr restore --source "$BASE_SYSTEM_DMG" --target "$MNT_BASE_SYSTEM" --noprompt --noverify --erase 243 | rm -r "$MNT_BASE_SYSTEM" 244 | 245 | if [ $DMG_OS_VERS_MAJOR -ge 9 ]; then 246 | MNT_BASE_SYSTEM="/Volumes/OS X Base System" 247 | BASESYSTEM_OUTPUT_IMAGE="$OUTPUT_DMG" 248 | PACKAGES_DIR="$MNT_BASE_SYSTEM/System/Installation/Packages" 249 | 250 | rm "$PACKAGES_DIR" 251 | msg_status "Moving 'Packages' directory from the ESD to BaseSystem.." 252 | mv -v "$MNT_ESD/Packages" "$MNT_BASE_SYSTEM/System/Installation/" 253 | 254 | # This isn't strictly required for Mavericks, but Yosemite will consider the 255 | # installer corrupt if this isn't included, because it cannot verify BaseSystem's 256 | # consistency and perform a recovery partition verification 257 | msg_status "Copying in original BaseSystem dmg and chunklist.." 258 | cp "$MNT_ESD/BaseSystem.dmg" "$MNT_BASE_SYSTEM/" 259 | cp "$MNT_ESD/BaseSystem.chunklist" "$MNT_BASE_SYSTEM/" 260 | else 261 | MNT_BASE_SYSTEM="/Volumes/Mac OS X Base System" 262 | BASESYSTEM_OUTPUT_IMAGE="$MNT_ESD/BaseSystem.dmg" 263 | rm "$BASESYSTEM_OUTPUT_IMAGE" 264 | PACKAGES_DIR="$MNT_ESD/Packages" 265 | fi 266 | 267 | msg_status "Adding automated components.." 268 | CDROM_LOCAL="$MNT_BASE_SYSTEM/private/etc/rc.cdrom.local" 269 | cat > $CDROM_LOCAL << EOF 270 | diskutil eraseDisk jhfs+ "Macintosh HD" GPTFormat disk0 271 | if [ "\$?" == "1" ]; then 272 | diskutil eraseDisk jhfs+ "Macintosh HD" GPTFormat disk1 273 | fi 274 | EOF 275 | chmod a+x "$CDROM_LOCAL" 276 | mkdir "$PACKAGES_DIR/Extras" 277 | cp "$SUPPORT_DIR/minstallconfig.xml" "$PACKAGES_DIR/Extras/" 278 | cp "$SUPPORT_DIR/OSInstall.collection" "$PACKAGES_DIR/" 279 | cp "$BUILT_PKG" "$PACKAGES_DIR/" 280 | rm -rf "$SUPPORT_DIR/tmp" 281 | 282 | msg_status "Unmounting BaseSystem.." 283 | hdiutil detach "$MNT_BASE_SYSTEM" 284 | 285 | if [ $DMG_OS_VERS_MAJOR -lt 9 ]; then 286 | msg_status "Pre-Mavericks we save back the modified BaseSystem to the root of the ESD." 287 | hdiutil convert -format UDZO -o "$MNT_ESD/BaseSystem.dmg" "$BASE_SYSTEM_DMG_RW" 288 | fi 289 | 290 | msg_status "Unmounting ESD.." 291 | hdiutil detach "$MNT_ESD" 292 | 293 | if [ $DMG_OS_VERS_MAJOR -ge 9 ]; then 294 | msg_status "On Mavericks and later, the entire modified BaseSystem is our output dmg." 295 | hdiutil convert -format UDZO -o "$OUTPUT_DMG" "$BASE_SYSTEM_DMG_RW" 296 | else 297 | msg_status "Pre-Mavericks we're modifying the original ESD file." 298 | hdiutil convert -format UDZO -o "$OUTPUT_DMG" -shadow "$SHADOW_FILE" "$ESD" 299 | fi 300 | rm -rf "$MNT_ESD" "$SHADOW_FILE" 301 | 302 | if [ -n "$SUDO_UID" ] && [ -n "$SUDO_GID" ]; then 303 | msg_status "Fixing permissions.." 304 | chown -R $SUDO_UID:$SUDO_GID \ 305 | "$OUT_DIR" 306 | fi 307 | 308 | if [ -n "$DEFAULT_ISO_DIR" ]; then 309 | DEFINITION_FILE="$DEFINITION_DIR/definition.rb" 310 | msg_status "Setting ISO file in definition $DEFINITION_FILE.." 311 | ISO_FILE=$(basename "$OUTPUT_DMG") 312 | # Explicitly use -e in order to use double quotes around sed command 313 | sed -i -e "s/%OSX_ISO%/${ISO_FILE}/" "$DEFINITION_FILE" 314 | fi 315 | 316 | msg_status "Checksumming output image.." 317 | MD5=$(md5 -q "$OUTPUT_DMG") 318 | msg_status "MD5: $MD5" 319 | 320 | msg_status "Done. Built image is located at $OUTPUT_DMG. Add this iso and its checksum to your template." 321 | -------------------------------------------------------------------------------- /prepare_iso/prepare_ovf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | usage() { 4 | cat < /dev/null 19 | fi 20 | } 21 | 22 | trap cleanup EXIT INT TERM 23 | 24 | msg_status() { 25 | echo "\033[0;32m-- $1\033[0m" 26 | } 27 | 28 | msg_error() { 29 | echo "\033[0;31m-- $1\033[0m" 30 | } 31 | 32 | render_template() { 33 | eval "echo \"$(cat "$1")\"" 34 | } 35 | 36 | if [ ! -f "$1" ]; then 37 | usage 38 | exit 1 39 | fi 40 | 41 | TIMESTAMP=$(date +"%s") 42 | VM="macOS_${TIMESTAMP}" 43 | HARDDRIVE="$1" 44 | OUTPUT="${HARDDRIVE%.vdi}.ovf" 45 | 46 | msg_status "Creating new virtual machine" 47 | VBoxManage createvm --name "$VM" --ostype "MacOS_64" --register 48 | 49 | msg_status "Adding SATA Controller" 50 | VBoxManage storagectl "$VM" --name "SATA Controller" --add sata --controller IntelAHCI 51 | 52 | msg_status "Attaching vdi" 53 | VBoxManage storageattach "$VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium "$HARDDRIVE" 54 | 55 | msg_status "Setting up virtual machine" 56 | VBoxManage modifyvm "$VM" --audiocontroller "hda" 57 | VBoxManage modifyvm "$VM" --chipset "ich9" 58 | VBoxManage modifyvm "$VM" --firmware "efi" 59 | VBoxManage modifyvm "$VM" --cpus "2" 60 | VBoxManage modifyvm "$VM" --hpet "on" 61 | VBoxManage modifyvm "$VM" --keyboard "usb" 62 | VBoxManage modifyvm "$VM" --memory "4096" 63 | VBoxManage modifyvm "$VM" --mouse "usbtablet" 64 | VBoxManage modifyvm "$VM" --vram "128" 65 | 66 | msg_status "Exporting the virtual machine" 67 | VBoxManage export "$VM" --output "$OUTPUT" 68 | 69 | msg_status "Done. Virtual machine export located at $OUTPUT." 70 | -------------------------------------------------------------------------------- /prepare_iso/prepare_vdi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # 3 | # Preparation script for an OS X automated installation for use with VeeWee/Packer/Vagrant 4 | # 5 | # What the script does, in more detail: 6 | # 7 | # 1. Mounts the InstallESD.dmg to locate the os's installer pkg (OSInstall.mpkg) 8 | # 2. A 'veewee-config.pkg' installer package is built, which creates the 9 | # 'vagrant' user, configures sshd and sudoers, and disables setup assistants. 10 | # 3. A temporary sparse disk image is created. 11 | # 4. The OSInstall and the veewee-config.pkg are installed into the disk image. 12 | # 5. The image's raw device is converted into a virtual disk image. 13 | # 14 | # Thanks: 15 | # Idea thanks to Per Olofsson's AutoDMG 16 | # (https://github.com/MagerValp/AutoDMG/wiki/How-Does-AutoDMG-Work) 17 | # 18 | # And Joseph Chilcote's vfuse 19 | # (https://github.com/chilcote/vfuse) 20 | # 21 | # User creation via package install method also credited to Greg, and made easy with Per 22 | # Olofsson's CreateUserPkg (http://magervalp.github.io/CreateUserPkg) 23 | # 24 | # 25 | 26 | usage() { 27 | cat < 37 | Sets the disk size in GB, defaults to 32. 38 | 39 | -u 40 | Sets the username of the root user, defaults to 'vagrant'. 41 | 42 | -p 43 | Sets the password of the root user, defaults to 'vagrant'. 44 | 45 | -i 46 | Sets the path of the avatar image for the root user, defaulting to the vagrant icon. 47 | 48 | -o 49 | Sets the name of the generated virtual disk image, defaulting to macOS_[osversion].vdi. 50 | 51 | -D 52 | Sets the specified flag. Valid flags are: 53 | DISABLE_REMOTE_MANAGEMENT 54 | DISABLE_SCREEN_SHARING 55 | DISABLE_SIP 56 | 57 | EOF 58 | } 59 | 60 | cleanup() { 61 | if [ ! -z "$MNT_ESD" ]; then 62 | hdiutil detach -quiet -force "$MNT_ESD" || rm -rf "$MNT_ESD" || echo > /dev/null 63 | fi 64 | 65 | if [ ! -z "$MNT_SPARSEIMAGE" ]; then 66 | hdiutil detach -quiet -force "$MNT_SPARSEIMAGE" || rm -rf "$MNT_SPARSEIMAGE" || echo > /dev/null 67 | fi 68 | 69 | if [[ ! -z "$TEST" && -e "$SPARSEIMAGE" ]]; then 70 | rm -rf "$SPARSEIMAGE" 71 | fi 72 | } 73 | 74 | trap cleanup EXIT INT TERM 75 | 76 | msg_status() { 77 | echo "\033[0;32m-- $1\033[0m" 78 | } 79 | msg_error() { 80 | echo "\033[0;31m-- $1\033[0m" 81 | } 82 | exit_with_error() { 83 | msg_error "$1" 84 | exit 1 85 | } 86 | 87 | SCRIPT_DIR="$(cd "$(dirname "$0")"; pwd)" 88 | SUPPORT_DIR="$SCRIPT_DIR/support" 89 | 90 | # Import shared code to generate first boot pkgs 91 | FIRST_BOOT_PKG_SCRIPT="$SCRIPT_DIR/create_firstboot_pkg.sh" 92 | [ -f "$FIRST_BOOT_PKG_SCRIPT" ] && . "$FIRST_BOOT_PKG_SCRIPT" 93 | 94 | # Parse the optional command line switches 95 | USER="vagrant" 96 | PASSWORD="vagrant" 97 | IMAGE_PATH="$SUPPORT_DIR/vagrant.jpg" 98 | DISK_SIZE_GB=32 99 | 100 | # Flags 101 | DISABLE_REMOTE_MANAGEMENT=0 102 | DISABLE_SCREEN_SHARING=0 103 | DISABLE_SIP=0 104 | DISABLE_APFS=1 105 | 106 | if [ $# -eq 0 ]; then 107 | usage 108 | exit 1 109 | fi 110 | 111 | while getopts s:u:p:i:o:D: OPT; do 112 | case "$OPT" in 113 | s) 114 | DISK_SIZE_GB="$OPTARG" 115 | ;; 116 | u) 117 | USER="$OPTARG" 118 | ;; 119 | p) 120 | PASSWORD="$OPTARG" 121 | ;; 122 | i) 123 | IMAGE_PATH="$OPTARG" 124 | ;; 125 | o) 126 | OUTPUT_DMG="$OPTARG" 127 | ;; 128 | D) 129 | if [ x${!OPTARG} = x0 ]; then 130 | eval $OPTARG=1 131 | elif [ x${!OPTARG} != x1 ]; then 132 | msg_error "Unknown flag: ${OPTARG}" 133 | usage 134 | exit 1 135 | fi 136 | ;; 137 | \?) 138 | usage 139 | exit 1 140 | ;; 141 | esac 142 | done 143 | 144 | # Remove the switches we parsed above. 145 | shift $(expr $OPTIND - 1) 146 | 147 | if [ $(id -u) -ne 0 ]; then 148 | exit_with_error "This script must be run as root, as it saves a disk image with ownerships enabled." 149 | fi 150 | 151 | if [ -z "$(which VBoxManage)" ]; then 152 | exit_with_error "VBoxManage, the command-line interface to VirtualBox, not found" 153 | fi 154 | 155 | INSTALLER_PATH="$1" 156 | if [ ! -e "$INSTALLER_PATH" ]; then 157 | exit_with_error "Input installer image $INSTALLER_PATH could not be found! Exiting.." 158 | fi 159 | 160 | OUT_DIR="$2" 161 | if [ -z "$OUT_DIR" ]; then 162 | exit_with_error "Currently an explicit output directory is required as the second argument." 163 | elif [ ! -d "$OUT_DIR" ]; then 164 | msg_status "Destination dir $OUT_DIR doesn't exist, creating.." 165 | mkdir -p "$OUT_DIR" 166 | fi 167 | 168 | ESD="$INSTALLER_PATH/Contents/SharedSupport/InstallESD.dmg" 169 | if [ ! -e "$ESD" ]; then 170 | exit_with_error "Can't locate an InstallESD.dmg in this source location $ESD!" 171 | fi 172 | 173 | SYSVER_PLIST_PATH="$INSTALLER_PATH/Contents/SharedSupport/InstallInfo.plist" 174 | if [ ! -e "$SYSVER_PLIST_PATH" ]; then 175 | exit_with_error "Can't locate InstallInfo.plist in $INSTALLER_PATH/Contents/SharedSupport/!" 176 | fi 177 | 178 | DMG_OS_VERS=$(/usr/libexec/PlistBuddy -c 'Print :System\ Image\ Info:version' "$SYSVER_PLIST_PATH") 179 | DMG_OS_VERS_MAJOR=$(echo $DMG_OS_VERS | awk -F "." '{print $1}') 180 | DMG_OS_VERS_MINOR=$(echo $DMG_OS_VERS | awk -F "." '{print $2}') 181 | DMG_OS_VERS_PATCH=$(echo $DMG_OS_VERS | awk -F "." '{print $3}') 182 | msg_status "macOS version detected: $DMG_OS_VERS_MAJOR.$DMG_OS_VERS_MINOR.$DMG_OS_VERS_PATCH" 183 | 184 | HOST_OS_VERS=$(sw_vers -productVersion) 185 | HOST_OS_VERS_MAJOR=$(echo $HOST_OS_VERS | awk -F "." '{print $1}') 186 | HOST_OS_VERS_MINOR=$(echo $HOST_OS_VERS | awk -F "." '{print $2}') 187 | HOST_OS_VERS_PATCH=$(echo $HOST_OS_VERS | awk -F "." '{print $3}') 188 | msg_status "host macOS version detected: $HOST_OS_VERS_MAJOR.$HOST_OS_VERS_MINOR.$HOST_OS_VERS_PATCH" 189 | 190 | if [ "$DMG_OS_VERS_MAJOR" != "$DMG_OS_VERS_MAJOR" ] || [ "$DMG_OS_VERS_MINOR" != "$HOST_OS_VERS_MINOR" ]; then 191 | exit_with_error "Unfortunately prepare_vdi can only generate images of same version as the host" 192 | fi 193 | 194 | if [ -z "$OUTPUT_DMG" ]; then 195 | OUTPUT_DMG="$OUT_DIR/macOS_${DMG_OS_VERS}.vdi" 196 | elif [ -e "$OUTPUT_DMG" ]; then 197 | exit_with_error "Output file $OUTPUT_DMG already exists! We're not going to overwrite it, exiting.." 198 | fi 199 | 200 | if [ $DMG_OS_VERS_MINOR -ge 13 ]; then 201 | if [ $DISABLE_APFS = 1 ]; then 202 | FSTYPE="HFS+J" 203 | else 204 | FSTYPE="APFS" 205 | fi 206 | OSPACKAGE="$INSTALLER_PATH/Contents/SharedSupport/InstallInfo.plist" 207 | else 208 | FSTYPE="HFS+J" 209 | MNT_ESD=$(/usr/bin/mktemp -d /tmp/veewee-osx-esd.XXXX) 210 | 211 | msg_status "Attaching input OS X installer image" 212 | hdiutil attach "$ESD" -mountpoint "$MNT_ESD" -nobrowse -owners on 213 | if [ $? -ne 0 ]; then 214 | [ ! -e "$ESD" ] && exit_with_error "Could not find $ESD in $(pwd)" 215 | exit_with_error "Could not mount $ESD on $MNT_ESD" 216 | fi 217 | 218 | OSPACKAGE="$MNT_ESD/Packages/OSInstall.mpkg" 219 | fi 220 | 221 | # Build our post-installation pkg that will create a user and enable ssh 222 | msg_status "Making firstboot installer pkg.." 223 | create_firstboot_pkg 224 | if [ -z "$BUILT_PKG" ] || [ ! -e "$BUILT_PKG" ]; then 225 | exit_with_error "Failed building the firstboot installer pkg, exiting.." 226 | fi 227 | 228 | MNT_SPARSEIMAGE=$(/usr/bin/mktemp -d /tmp/prepare_vdi_mnt_sparseimage.XXXX) 229 | SPARSEIMAGE="$(/usr/bin/mktemp /tmp/prepare_vdi.XXXX).sparseimage" 230 | 231 | msg_status "Creating DMG of "${DISK_SIZE_GB}g" with $FSTYPE located at $SPARSEIMAGE.." 232 | if ! hdiutil create -size "${DISK_SIZE_GB}g" -type SPARSE -fs "$FSTYPE" -volname "Macintosh HD" -uid 0 -gid 80 -mode 1775 "$SPARSEIMAGE"; then 233 | exit_with_error "Failed creating the disk image" 234 | fi 235 | 236 | msg_status "Mounting empty read-write DMG located at $SPARSEIMAGE.." 237 | hdiutil attach "$SPARSEIMAGE" -mountpoint "$MNT_SPARSEIMAGE" -nobrowse -owners on 238 | 239 | msg_status "Installing macOS" 240 | installer -verboseR -dumplog -pkg "$OSPACKAGE" -target "$MNT_SPARSEIMAGE" 241 | if [ $? -ne 0 ]; then 242 | exit_with_error "Failed installing macOS" 243 | fi 244 | 245 | msg_status "Installing firstboot installer pkg" 246 | installer -pkg "$BUILT_PKG" -target "$MNT_SPARSEIMAGE" 247 | if [ $? -ne 0 ]; then 248 | exit_with_error "Failed installing the firstboot installer pkg" 249 | fi 250 | 251 | # Unmount and remount to make sure that is synchronized. 252 | msg_status "Remounting $SPARSEIMAGE" 253 | hdiutil detach -quiet -force "$MNT_SPARSEIMAGE" || echo > /dev/null 254 | MOUNTOUTPUT=$(hdiutil attach "$SPARSEIMAGE" -mountpoint "$MNT_SPARSEIMAGE" -nobrowse -owners on) 255 | DISK_DEV=$(grep GUID_partition_scheme <<< "$MOUNTOUTPUT" | cut -f1 | tr -d '[:space:]') 256 | DISK_SIZE_BYTES=$(($DISK_SIZE_GB * 1024 * 1024 * 1024)) 257 | 258 | if [ ! -e "$DISK_DEV" ]; then 259 | exit_with_error "Failed to find the device file of the image" 260 | fi 261 | 262 | msg_status "Exporting from $DISK_DEV to $OUTPUT_DMG" 263 | VBoxManage convertfromraw stdin "$OUTPUT_DMG" "$DISK_SIZE_BYTES" < "$DISK_DEV" 264 | 265 | msg_status "Checksumming output image.." 266 | MD5=$(md5 -q "$OUTPUT_DMG" | tee "$OUTPUT_DMG.md5") 267 | msg_status "MD5: $MD5" 268 | 269 | if [ -n "$SUDO_UID" ] && [ -n "$SUDO_GID" ]; then 270 | msg_status "Fixing permissions.." 271 | chown -R $SUDO_UID:$SUDO_GID \ 272 | "$OUT_DIR" 273 | fi 274 | 275 | msg_status "Done. Built image is located at $OUTPUT_DMG. Add this iso and its checksum to your template." 276 | 277 | cleanup 278 | exit 0 279 | -------------------------------------------------------------------------------- /prepare_iso/support/OSInstall.collection: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | /System/Installation/Packages/OSInstall.mpkg 6 | /System/Installation/Packages/OSInstall.mpkg 7 | /System/Installation/Packages/veewee-config.pkg 8 | 9 | 10 | -------------------------------------------------------------------------------- /prepare_iso/support/generate_shadowhash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | C78F3A60-FC1D-4377-AD7D-DBAD5A6B8B2C 12 | # 13 | # 2008 Pete Akins, Cincinnati, OH . pete.akins@uc.edu 14 | 15 | /********************* 16 | 17 | FORMAT OF SHADOW FILE 18 | 19 | Offsets and length (hex values) 20 | 0-63 NTLM Password (64) 21 | 64-103 SHA1 Digest (40) 22 | 104-167 CRAM-MD5 (64) 23 | 168-215 Salted SHA1 (48, 8+40) 24 | 216-1239 Recoverable (1024) 25 | 26 | *********************/ 27 | 28 | if (!isset($argv[1])) { 29 | fprintf(STDERR, "Enter password: "); 30 | $password = trim(fgets(STDIN)); 31 | } else { 32 | // get the password as an arg 33 | $password = $argv[1]; 34 | } 35 | 36 | if (empty($password)) { 37 | die("Invalid password"); 38 | } 39 | 40 | do { 41 | 42 | /* make sure we get a big random number, but not too big */ 43 | $randmax = getrandmax(); 44 | $max = pow(2, 31)-1; 45 | if ($max>$randmax) { 46 | $max = $randmax; 47 | } 48 | 49 | /* get our salt integer, and it's hex value */ 50 | $salt = rand(1, $max); 51 | $saltHex = decHex($salt); 52 | 53 | /* get string representation of bytes */ 54 | $saltStr = pack("N", $salt); 55 | 56 | /* compute salted hash. get uppercase values */ 57 | $sha1_salt = sprintf("%08s%s", strtoupper($saltHex), strtoupper(sha1($saltStr . $password))); 58 | 59 | } while (strlen($sha1_salt)!=48); //just in case we have odd ball integers that result in non standard hex. 60 | 61 | /* blank out other hashes */ 62 | $NTLM = str_repeat("0", 64); 63 | $sha1 = str_repeat("0", 40); 64 | $cram_md5 = str_repeat("0", 64); 65 | $recoverable = str_repeat("0", 1024); 66 | 67 | /* put it all together */ 68 | $string = $NTLM . $sha1 . $cram_md5 . $sha1_salt . $recoverable; 69 | 70 | echo $string; 71 | exit(0); 72 | 73 | ?> -------------------------------------------------------------------------------- /prepare_iso/support/minstallconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | InstallType 6 | automated 7 | Language 8 | en 9 | Package 10 | /System/Installation/Packages/OSInstall.collection 11 | Target 12 | /Volumes/Macintosh HD 13 | TargetName 14 | Macintosh HD 15 | 16 | 17 | -------------------------------------------------------------------------------- /prepare_iso/support/pkg-postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | USER="__USER__PLACEHOLDER__" 3 | OSX_VERS=$(sw_vers -productVersion | awk -F "." '{print $2}') 4 | PlistBuddy="/usr/libexec/PlistBuddy" 5 | 6 | target_ds_node="${3}/private/var/db/dslocal/nodes/Default" 7 | # Override the default behavior of sshd on the target volume to be not disabled 8 | if [ "$OSX_VERS" -ge 10 ]; then 9 | OVERRIDES_PLIST="$3/private/var/db/com.apple.xpc.launchd/disabled.plist" 10 | $PlistBuddy -c 'Delete :com.openssh.sshd' "$OVERRIDES_PLIST" 11 | $PlistBuddy -c 'Add :com.openssh.sshd bool False' "$OVERRIDES_PLIST" 12 | if [ __DISABLE_SCREEN_SHARING__ = 0 ]; then 13 | $PlistBuddy -c 'Delete :com.apple.screensharing' "$OVERRIDES_PLIST" 14 | $PlistBuddy -c 'Add :com.apple.screensharing bool False' "$OVERRIDES_PLIST" 15 | fi 16 | else 17 | OVERRIDES_PLIST="$3/private/var/db/launchd.db/com.apple.launchd/overrides.plist" 18 | $PlistBuddy -c 'Delete :com.openssh.sshd' "$OVERRIDES_PLIST" 19 | $PlistBuddy -c 'Add :com.openssh.sshd:Disabled bool False' "$OVERRIDES_PLIST" 20 | if [ __DISABLE_SCREEN_SHARING__ = 0 ]; then 21 | $PlistBuddy -c 'Delete :com.apple.screensharing' "$OVERRIDES_PLIST" 22 | $PlistBuddy -c 'Add :com.apple.screensharing:Disabled bool False' "$OVERRIDES_PLIST" 23 | fi 24 | fi 25 | 26 | # Add user to sudoers 27 | cp "$3/etc/sudoers" "$3/etc/sudoers.orig" 28 | echo "$USER ALL=(ALL) NOPASSWD: ALL" >> "$3/etc/sudoers" 29 | 30 | # Add user to admin group memberships (even though GID 80 is enough for most things) 31 | USER_GUID=$($PlistBuddy -c 'Print :generateduid:0' "$target_ds_node/users/$USER.plist") 32 | USER_UID=$($PlistBuddy -c 'Print :uid:0' "$target_ds_node/users/$USER.plist") 33 | $PlistBuddy -c 'Add :groupmembers: string '"$USER_GUID" "$target_ds_node/groups/admin.plist" 34 | 35 | # Add user to SSH SACL group membership 36 | ssh_group="${target_ds_node}/groups/com.apple.access_ssh.plist" 37 | $PlistBuddy -c 'Add :groupmembers array' "${ssh_group}" 38 | $PlistBuddy -c 'Add :groupmembers:0 string '"$USER_GUID"'' "${ssh_group}" 39 | $PlistBuddy -c 'Add :users array' "${ssh_group}" 40 | $PlistBuddy -c 'Add :users:0 string '$USER'' "${ssh_group}" 41 | 42 | # Enable Remote Desktop and configure user with full privileges 43 | if [ __DISABLE_REMOTE_MANAGEMENT__ = 0 ]; then 44 | echo "enabled" > "$3/private/etc/RemoteManagement.launchd" 45 | $PlistBuddy -c 'Add :naprivs array' "$target_ds_node/users/$USER.plist" 46 | $PlistBuddy -c 'Add :naprivs:0 string -1073741569' "$target_ds_node/users/$USER.plist" 47 | fi 48 | 49 | if [ __DISABLE_SIP__ = 1 ]; then 50 | csrutil disable 51 | fi 52 | 53 | # Pre-create user folder so veewee will have somewhere to scp configinfo to 54 | mkdir -p "$3/Users/$USER/Library/Preferences" 55 | 56 | # Suppress prompts for things relevant to at least macOS VMs 57 | $PlistBuddy -c 'Add :DidSeeCloudSetup bool true' "$3/Users/$USER/Library/Preferences/com.apple.SetupAssistant.plist" 58 | $PlistBuddy -c 'Add :LastSeenCloudProductVersion string 10.'"$OSX_VERS" "$3/Users/$USER/Library/Preferences/com.apple.SetupAssistant.plist" 59 | $PlistBuddy -c 'Add :DidSeeSiriSetup bool true' "$3/Users/$USER/Library/Preferences/com.apple.SetupAssistant.plist" 60 | $PlistBuddy -c 'Add :DidSeePrivacy bool true' "$3/Users/$USER/Library/Preferences/com.apple.SetupAssistant.plist" 61 | $PlistBuddy -c 'Add :DidSeeAppearanceSetup bool true' "$3/Users/$USER/Library/Preferences/com.apple.SetupAssistant.plist" 62 | 63 | # Fix ownership now that the above has made a Library folder as root 64 | chown -R "$USER_UID":20 "$3/Users/$USER" 65 | 66 | # Disable Diagnostics submissions prompt if 10.10 67 | # http://macops.ca/diagnostics-prompt-yosemite 68 | if [ "$OSX_VERS" -ge 10 ]; then 69 | # Apple's defaults 70 | SUBMIT_TO_APPLE=YES 71 | SUBMIT_TO_APP_DEVELOPERS=NO 72 | 73 | CRASHREPORTER_SUPPORT="$3/Library/Application Support/CrashReporter" 74 | CRASHREPORTER_DIAG_PLIST="${CRASHREPORTER_SUPPORT}/DiagnosticMessagesHistory.plist" 75 | if [ ! -d "${CRASHREPORTER_SUPPORT}" ]; then 76 | mkdir "${CRASHREPORTER_SUPPORT}" 77 | chmod 775 "${CRASHREPORTER_SUPPORT}" 78 | chown root:admin "${CRASHREPORTER_SUPPORT}" 79 | fi 80 | for key in AutoSubmit AutoSubmitVersion ThirdPartyDataSubmit ThirdPartyDataSubmitVersion; do 81 | $PlistBuddy -c "Delete :$key" "${CRASHREPORTER_DIAG_PLIST}" 2> /dev/null 82 | done 83 | $PlistBuddy -c "Add :AutoSubmit bool ${SUBMIT_TO_APPLE}" "${CRASHREPORTER_DIAG_PLIST}" 84 | $PlistBuddy -c "Add :AutoSubmitVersion integer 4" "${CRASHREPORTER_DIAG_PLIST}" 85 | $PlistBuddy -c "Add :ThirdPartyDataSubmit bool ${SUBMIT_TO_APP_DEVELOPERS}" "${CRASHREPORTER_DIAG_PLIST}" 86 | $PlistBuddy -c "Add :ThirdPartyDataSubmitVersion integer 4" "${CRASHREPORTER_DIAG_PLIST}" 87 | fi 88 | 89 | # Disable loginwindow screensaver to save CPU cycles 90 | $PlistBuddy -c 'Add :loginWindowIdleTime integer 0' "$3/Library/Preferences/com.apple.screensaver.plist" 91 | 92 | # Disable the welcome screen 93 | touch "$3/private/var/db/.AppleSetupDone" 94 | -------------------------------------------------------------------------------- /prepare_iso/support/user.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | authentication_authority 6 | 7 | ;ShadowHash;HASHLIST:<SALTED-SHA512-PBKDF2> 8 | 9 | ShadowHashData 10 | 11 | 12 | ${ShadowHashData} 13 | 14 | 15 | generateduid 16 | 17 | 11112222-3333-4444-AAAA-BBBBCCCCDDDD 18 | 19 | gid 20 | 21 | 20 22 | 23 | home 24 | 25 | /Users/${USER} 26 | 27 | jpegphoto 28 | 29 | 30 | ${BASE64_IMAGE} 31 | 32 | 33 | name 34 | 35 | ${USER} 36 | 37 | passwd 38 | 39 | ******** 40 | 41 | realname 42 | 43 | ${USER} 44 | 45 | shell 46 | 47 | /bin/bash 48 | 49 | uid 50 | 51 | 501 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /prepare_iso/support/vagrant.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/timsutton/osx-vm-templates/d3de634fc09aed981e8ec53ba302163c4624f039/prepare_iso/support/vagrant.jpg -------------------------------------------------------------------------------- /scripts/add-network-interface-detection.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script adds a Mac OS Launch Daemon, which runs every time the 4 | # machine is booted. The daemon will re-detect the attached network 5 | # interfaces. If this is not done, network devices may not work. 6 | PLIST=/Library/LaunchDaemons/com.github.timsutton.osx-vm-templates.detectnewhardware.plist 7 | cat < "${PLIST}" 8 | 9 | 10 | 11 | 12 | Label 13 | com.github.timsutton.osx-vm-templates.detectnewhardware 14 | ProgramArguments 15 | 16 | /usr/sbin/networksetup 17 | -detectnewhardware 18 | 19 | RunAtLoad 20 | 21 | 22 | 23 | EOF 24 | 25 | # These should be already set as follows, but since they're required 26 | # in order to load properly, we set them explicitly. 27 | /bin/chmod 644 "${PLIST}" 28 | /usr/sbin/chown root:wheel "${PLIST}" 29 | -------------------------------------------------------------------------------- /scripts/autologin.sh: -------------------------------------------------------------------------------- 1 | if [ "$AUTOLOGIN" != "true" ] && [ "$AUTOLOGIN" != "1" ]; then 2 | exit 3 | fi 4 | 5 | echo "Enabling automatic GUI login for the '$USERNAME' user.." 6 | 7 | python /private/tmp/set_kcpassword.py "$PASSWORD" 8 | 9 | /usr/bin/defaults write /Library/Preferences/com.apple.loginwindow autoLoginUser "$USERNAME" 10 | -------------------------------------------------------------------------------- /scripts/chef-omnibus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "${CHEF_VERSION}" == "none" ]; then 4 | exit 5 | fi 6 | 7 | INSTALL_ARGS="" 8 | 9 | if [ "${CHEF_VERSION}" != "latest" ]; then 10 | INSTALL_ARGS="-v ${CHEF_VERSION}" 11 | fi 12 | 13 | curl -LO https://www.chef.io/chef/install.sh 14 | chmod +x ./install.sh 15 | ./install.sh "${INSTALL_ARGS}" 16 | rm install.sh 17 | -------------------------------------------------------------------------------- /scripts/parallels.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -eo pipefail 4 | 5 | TOOLS_PATH="/Users/$USERNAME/prl-tools-mac.iso" 6 | # Parallels Tools specific items 7 | if [ -e .PACKER_BUILDER_TYPE ] || [[ "$PACKER_BUILDER_TYPE" == parallels* ]]; then 8 | if [ ! -e "$TOOLS_PATH" ]; then 9 | echo "Couldn't locate uploaded tools iso at $TOOLS_PATH!" 10 | exit 1 11 | fi 12 | 13 | TMPMOUNT=`/usr/bin/mktemp -d /tmp/parallels-tools.XXXX` 14 | hdiutil attach "$TOOLS_PATH" -mountpoint "$TMPMOUNT" 15 | 16 | INSTALLER_PKG="$TMPMOUNT/Install.app/Contents/Resources/Install.mpkg" 17 | if [ ! -e "$INSTALLER_PKG" ]; then 18 | echo "Couldn't locate Parallels Tools installer pkg at $INSTALLER_PKG!" 19 | exit 1 20 | fi 21 | 22 | echo "Installing Parallels Tools..." 23 | installer -pkg "$INSTALLER_PKG" -target / 24 | 25 | # This usually fails 26 | hdiutil detach "$TMPMOUNT" 27 | rm -rf "$TMPMOUNT" 28 | rm -f "$TOOLS_PATH" 29 | fi 30 | -------------------------------------------------------------------------------- /scripts/puppet.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Install the specified version of Puppet and tools 4 | # 5 | # PUPPET_VERSION, FACTER_VERSION and HIERA_VERSION are set to either 6 | # 'latest' or specific versions via the Packer template 7 | # 8 | # install function mostly borrowed dmg function from hashicorp/puppet-bootstrap, 9 | # except we just take an already-downloaded dmg 10 | 11 | if [[ "${PUPPET_VERSION}" == "none" && \ 12 | "${FACTER_VERSION}" == "none" && \ 13 | "${HIERA_VERSION}" == "none" && \ 14 | "${PUPPET_AGENT_VERSION}" == "none" ]]; then 15 | exit 16 | fi 17 | 18 | install_dmg() { 19 | local name="$1" 20 | local dmg_path="$2" 21 | 22 | echo "Installing: ${name}" 23 | 24 | # Mount the DMG 25 | echo "-- Mounting DMG..." 26 | tmpmount=$(/usr/bin/mktemp -d /tmp/puppet-dmg.XXXX) 27 | hdiutil attach "${dmg_path}" -mountpoint "${tmpmount}" 28 | 29 | echo "-- Installing pkg..." 30 | pkg_path=$(find "${tmpmount}" -name '*.pkg' -mindepth 1 -maxdepth 1) 31 | installer -pkg "${pkg_path}" -tgt / 32 | 33 | # Unmount 34 | echo "-- Unmounting and ejecting DMG..." 35 | hdiutil eject "${tmpmount}" 36 | } 37 | 38 | get_dmg() { 39 | local recipe_name="$1" 40 | local version="$2" 41 | local report_path=$(mktemp /tmp/autopkg-report-XXXX) 42 | 43 | # Run AutoPkg setting VERSION, and saving the results as a plist 44 | "${AUTOPKG}" run --report-plist "${report_path}" -k VERSION="${version}" "${recipe_name}" > \ 45 | "$(mktemp "/tmp/autopkg-runlog-${recipe_name}")" 46 | /usr/libexec/PlistBuddy -c \ 47 | 'Print :summary_results:url_downloader_summary_result:data_rows:0:download_path' \ 48 | "${report_path}" 49 | } 50 | 51 | # Default to installing the current version of Puppet - it's not 2013 anymore. 52 | PUPPET_VERSION=${PUPPET_VERSION:-none} 53 | FACTER_VERSION=${FACTER_VERSION:-none} 54 | HIERA_VERSION=${HIERA_VERSION:-none} 55 | PUPPET_AGENT_VERSION=${PUPPET_AGENT_VERSION:-latest} 56 | 57 | # Get AutoPkg 58 | AUTOPKG_DIR=$(mktemp -d /tmp/autopkg-XXXX) 59 | git clone https://github.com/autopkg/autopkg "$AUTOPKG_DIR" 60 | AUTOPKG="$AUTOPKG_DIR/Code/autopkg" 61 | 62 | # Add the recipes repo containing Puppet/Facter 63 | "${AUTOPKG}" repo-add recipes 64 | 65 | # Redirect AutoPkg cache to a temp location 66 | defaults write com.github.autopkg CACHE_DIR -string "$(mktemp -d /tmp/autopkg-cache-XXX)" 67 | 68 | if [ "${PUPPET_VERSION}" != "none" ]; then 69 | # Hide all users from the loginwindow with uid below 500, which will include the puppet user 70 | defaults write /Library/Preferences/com.apple.loginwindow Hide500Users -bool YES 71 | PUPPET_DMG=$(get_dmg Puppet.download "${PUPPET_VERSION}") 72 | install_dmg "Puppet" "${PUPPET_DMG}" 73 | fi 74 | 75 | if [ "${PUPPET_AGENT_VERSION}" != "none" ]; then 76 | # Hide all users from the loginwindow with uid below 500, which will include the puppet user 77 | defaults write /Library/Preferences/com.apple.loginwindow Hide500Users -bool YES 78 | PUPPET_AGENT_DMG=$(get_dmg Puppet-Agent.download "${PUPPET_AGENT_VERSION}") 79 | install_dmg "Puppet Agent" "${PUPPET_AGENT_DMG}" 80 | fi 81 | 82 | if [ "${FACTER_VERSION}" != "none" ]; then 83 | FACTER_DMG=$(get_dmg Facter.download "${FACTER_VERSION}") 84 | install_dmg "Facter" "${FACTER_DMG}" 85 | fi 86 | 87 | if [ "${HIERA_VERSION}" != "none" ]; then 88 | HIERA_DMG=$(get_dmg Hiera.download "${HIERA_VERSION}") 89 | install_dmg "Hiera" "${HIERA_DMG}" 90 | fi 91 | 92 | 93 | # Clean up 94 | rm -rf "${PUPPET_DMG}" "${FACTER_DMG}" "${HIERA_DMG}" "${PUPPET_AGENT_DMG}" "${AUTOPKG_DIR}" "~/Library/AutoPkg" 95 | 96 | defaults delete com.github.autopkg 97 | -------------------------------------------------------------------------------- /scripts/shrink.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | OSX_VERS=$(sw_vers -productVersion | awk -F "." '{print $2}') 4 | 5 | # Turn off hibernation and get rid of the sleepimage 6 | pmset hibernatemode 0 7 | rm -f /var/vm/sleepimage 8 | 9 | # Stop the pager process and drop swap files. These will be re-created on boot. 10 | # Starting with El Cap we can only stop the dynamic pager if SIP is disabled. 11 | if [ "$OSX_VERS" -lt 11 ] || $(csrutil status | grep -q disabled); then 12 | launchctl unload /System/Library/LaunchDaemons/com.apple.dynamic_pager.plist 13 | sleep 5 14 | fi 15 | rm -rf /private/var/vm/swap* 16 | 17 | # VMware Fusion specific items 18 | if [ -e .vmfusion_version ] || [[ "$PACKER_BUILDER_TYPE" == vmware* ]]; then 19 | # Shrink the disk 20 | /Library/Application\ Support/VMware\ Tools/vmware-tools-cli disk shrink / 21 | fi 22 | -------------------------------------------------------------------------------- /scripts/support/arc4random.py: -------------------------------------------------------------------------------- 1 | '''py-arc4random 2 | 3 | Basic python 2.7/3 implementation of OpenBSDs arc4random PRNG. 4 | 5 | https://github.com/rolandshoemaker/py-arc4random 6 | 7 | Examples 8 | >>> import arc4random 9 | >>> arc4random.rand() 10 | 2057591911 11 | >>> arc4random.randrange(50,100) 12 | 75 13 | >>> arc4random.randsample(0,1, 10) 14 | [1, 1, 0, 1, 1, 0, 1, 1, 1, 1] 15 | ''' 16 | 17 | import random 18 | 19 | def rand(): 20 | key = random.sample(range(256), 256) # something 21 | seeds = _RC4PRGA(_RC4keySchedule(key)) 22 | return (seeds[0]<<24)|(seeds[1]<<16)|(seeds[2]<<8)|seeds[3] 23 | 24 | def randrange(x, y=None): 25 | if y: 26 | return (rand()%((y-x)+1))+x 27 | else: 28 | return rand()%(x+1) 29 | 30 | def randsample(Rmin, Rmax, size): 31 | sample = [] 32 | for i in range(size): 33 | sample.append((rand()%((Rmax-Rmin)+1))+Rmin) 34 | return sample 35 | 36 | def _RC4keySchedule(key): 37 | sbox = list(range(256)) 38 | x = 0 39 | keySize = len(key) 40 | for i in sbox: 41 | x = (x+i+key[i%keySize])%256 42 | _swap(sbox, i, x) 43 | return sbox 44 | 45 | def _RC4PRGA(state): 46 | x, y = 0, 0 47 | seeds = [] 48 | # Discard first 1536 bytes of the keystream according to RFC4345 as they may reveal information 49 | # about key used (a set of these keys could reveal information about the source for our key) 50 | for i in range((1536//4)+4): 51 | x = (x+1)%256 52 | y = (y+state[x])%256 53 | _swap(state, x, y) 54 | if i >= (1536//4): 55 | seeds.append(state[(state[x]+state[y])%256]) 56 | return seeds 57 | 58 | def _swap(listy, n1, n2): 59 | listy[n1], listy[n2] = listy[n2], listy[n1] -------------------------------------------------------------------------------- /scripts/support/generatehash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import sys 4 | import shadowhash 5 | import base64 6 | 7 | if __name__ == "__main__": 8 | if len(sys.argv) == 2: 9 | hash = shadowhash.generate(sys.argv[1]) 10 | print base64.b64encode(hash) 11 | -------------------------------------------------------------------------------- /scripts/support/pbkdf2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | pbkdf2 4 | ~~~~~~ 5 | This module implements pbkdf2 for Python. It also has some basic 6 | tests that ensure that it works. The implementation is straightforward 7 | and uses stdlib only stuff and can be easily be copy/pasted into 8 | your favourite application. 9 | Use this as replacement for bcrypt that does not need a c implementation 10 | of a modified blowfish crypto algo. 11 | Example usage: 12 | >>> pbkdf2_hex('what i want to hash', 'the random salt') 13 | 'fa7cc8a2b0a932f8e6ea42f9787e9d36e592e0c222ada6a9' 14 | How to use this: 15 | 1. Use a constant time string compare function to compare the stored hash 16 | with the one you're generating:: 17 | def safe_str_cmp(a, b): 18 | if len(a) != len(b): 19 | return False 20 | rv = 0 21 | for x, y in izip(a, b): 22 | rv |= ord(x) ^ ord(y) 23 | return rv == 0 24 | 2. Use `os.urandom` to generate a proper salt of at least 8 byte. 25 | Use a unique salt per hashed password. 26 | 3. Store ``algorithm$salt:costfactor$hash`` in the database so that 27 | you can upgrade later easily to a different algorithm if you need 28 | one. For instance ``PBKDF2-256$thesalt:10000$deadbeef...``. 29 | :copyright: (c) Copyright 2011 by Armin Ronacher. 30 | :license: BSD, see LICENSE for more details. 31 | """ 32 | # https://github.com/mitsuhiko/python-pbkdf2 33 | 34 | import hmac 35 | import hashlib 36 | from struct import Struct 37 | from operator import xor 38 | from itertools import izip, starmap 39 | 40 | 41 | _pack_int = Struct('>I').pack 42 | 43 | 44 | def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): 45 | """Like :func:`pbkdf2_bin` but returns a hex encoded string.""" 46 | return pbkdf2_bin(data, salt, iterations, keylen, hashfunc).encode('hex') 47 | 48 | 49 | def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): 50 | """Returns a binary digest for the PBKDF2 hash algorithm of `data` 51 | with the given `salt`. It iterates `iterations` time and produces a 52 | key of `keylen` bytes. By default SHA-1 is used as hash function, 53 | a different hashlib `hashfunc` can be provided. 54 | """ 55 | hashfunc = hashfunc or hashlib.sha1 56 | mac = hmac.new(data, None, hashfunc) 57 | def _pseudorandom(x, mac=mac): 58 | h = mac.copy() 59 | h.update(x) 60 | return map(ord, h.digest()) 61 | buf = [] 62 | for block in xrange(1, -(-keylen // mac.digest_size) + 1): 63 | rv = u = _pseudorandom(salt + _pack_int(block)) 64 | for i in xrange(iterations - 1): 65 | u = _pseudorandom(''.join(map(chr, u))) 66 | rv = starmap(xor, izip(rv, u)) 67 | buf.extend(rv) 68 | return ''.join(map(chr, buf))[:keylen] 69 | 70 | 71 | def test(): 72 | failed = [] 73 | def check(data, salt, iterations, keylen, expected): 74 | rv = pbkdf2_hex(data, salt, iterations, keylen) 75 | if rv != expected: 76 | print 'Test failed:' 77 | print ' Expected: %s' % expected 78 | print ' Got: %s' % rv 79 | print ' Parameters:' 80 | print ' data=%s' % data 81 | print ' salt=%s' % salt 82 | print ' iterations=%d' % iterations 83 | print 84 | failed.append(1) 85 | 86 | # From RFC 6070 87 | check('password', 'salt', 1, 20, 88 | '0c60c80f961f0e71f3a9b524af6012062fe037a6') 89 | check('password', 'salt', 2, 20, 90 | 'ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957') 91 | check('password', 'salt', 4096, 20, 92 | '4b007901b765489abead49d926f721d065a429c1') 93 | check('passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 94 | 4096, 25, '3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038') 95 | check('pass\x00word', 'sa\x00lt', 4096, 16, 96 | '56fa6aa75548099dcc37d7f03425e0c3') 97 | # This one is from the RFC but it just takes for ages 98 | ##check('password', 'salt', 16777216, 20, 99 | ## 'eefe3d61cd4da4e4e9945b3d6ba2158c2634e984') 100 | 101 | # From Crypt-PBKDF2 102 | check('password', 'ATHENA.MIT.EDUraeburn', 1, 16, 103 | 'cdedb5281bb2f801565a1122b2563515') 104 | check('password', 'ATHENA.MIT.EDUraeburn', 1, 32, 105 | 'cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837') 106 | check('password', 'ATHENA.MIT.EDUraeburn', 2, 16, 107 | '01dbee7f4a9e243e988b62c73cda935d') 108 | check('password', 'ATHENA.MIT.EDUraeburn', 2, 32, 109 | '01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86') 110 | check('password', 'ATHENA.MIT.EDUraeburn', 1200, 32, 111 | '5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13') 112 | check('X' * 64, 'pass phrase equals block size', 1200, 32, 113 | '139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1') 114 | check('X' * 65, 'pass phrase exceeds block size', 1200, 32, 115 | '9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a') 116 | 117 | raise SystemExit(bool(failed)) 118 | 119 | 120 | if __name__ == '__main__': 121 | test() -------------------------------------------------------------------------------- /scripts/support/plistutils.py: -------------------------------------------------------------------------------- 1 | '''plist utility functions''' 2 | 3 | from Foundation import NSPropertyListSerialization 4 | from Foundation import NSPropertyListXMLFormat_v1_0 5 | from Foundation import NSPropertyListBinaryFormat_v1_0 6 | 7 | class FoundationPlistException(Exception): 8 | """Basic exception for plist errors""" 9 | pass 10 | 11 | 12 | def write_plist(dataObject, pathname=None, plist_format=None): 13 | ''' 14 | Write 'rootObject' as a plist to pathname or return as a string. 15 | ''' 16 | if plist_format == 'binary': 17 | plist_format = NSPropertyListBinaryFormat_v1_0 18 | else: 19 | plist_format = NSPropertyListXMLFormat_v1_0 20 | 21 | plistData, error = ( 22 | NSPropertyListSerialization. 23 | dataFromPropertyList_format_errorDescription_( 24 | dataObject, plist_format, None)) 25 | if plistData is None: 26 | if error: 27 | error = error.encode('ascii', 'ignore') 28 | else: 29 | error = "Unknown error" 30 | raise FoundationPlistException(error) 31 | if pathname: 32 | if plistData.writeToFile_atomically_(pathname, True): 33 | return 34 | else: 35 | raise FoundationPlistException( 36 | "Failed to write plist data to %s" % filepath) 37 | else: 38 | return plistData 39 | -------------------------------------------------------------------------------- /scripts/support/set_kcpassword.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Port of Gavin Brock's Perl kcpassword generator to Python, by Tom Taylor 4 | # . 5 | # Perl version: http://www.brock-family.org/gavin/perl/kcpassword.html 6 | 7 | import sys 8 | import os 9 | 10 | def kcpassword(passwd): 11 | # The magic 11 bytes - these are just repeated 12 | # 0x7D 0x89 0x52 0x23 0xD2 0xBC 0xDD 0xEA 0xA3 0xB9 0x1F 13 | key = [125,137,82,35,210,188,221,234,163,185,31] 14 | key_len = len(key) 15 | 16 | passwd = [ord(x) for x in list(passwd)] 17 | # pad passwd length out to an even multiple of key length 18 | r = len(passwd) % key_len 19 | if (r > 0): 20 | passwd = passwd + [0] * (key_len - r) 21 | 22 | for n in range(0, len(passwd), len(key)): 23 | ki = 0 24 | for j in range(n, min(n+len(key), len(passwd))): 25 | passwd[j] = passwd[j] ^ key[ki] 26 | ki += 1 27 | 28 | passwd = [chr(x) for x in passwd] 29 | return "".join(passwd) 30 | 31 | if __name__ == "__main__": 32 | passwd = kcpassword(sys.argv[1]) 33 | fd = os.open('/etc/kcpassword', os.O_WRONLY | os.O_CREAT, 0o600) 34 | file = os.fdopen(fd, 'w') 35 | file.write(passwd) 36 | file.close() 37 | -------------------------------------------------------------------------------- /scripts/support/shadowhash.py: -------------------------------------------------------------------------------- 1 | '''Functions for generating ShadowHashData''' 2 | 3 | import hashlib 4 | 5 | import arc4random 6 | import pbkdf2 7 | 8 | import plistutils 9 | 10 | 11 | def make_salt(saltlen): 12 | '''Generate a random salt''' 13 | salt = '' 14 | for char in arc4random.randsample(0, 255, saltlen): 15 | salt += chr(char) 16 | return salt 17 | 18 | 19 | def generate(password): 20 | '''Generate a ShadowHashData structure as used by macOS 10.8+''' 21 | iterations = arc4random.randrange(30000, 50000) 22 | salt = make_salt(32) 23 | keylen = 128 24 | try: 25 | entropy = hashlib.pbkdf2_hmac( 26 | 'sha512', password, salt, iterations, dklen=keylen) 27 | except AttributeError: 28 | # old Python, do it a different way 29 | entropy = pbkdf2.pbkdf2_bin( 30 | password, salt, iterations=iterations, keylen=keylen, 31 | hashfunc=hashlib.sha512) 32 | 33 | data = {'SALTED-SHA512-PBKDF2': {'entropy': buffer(entropy), 34 | 'iterations': iterations, 35 | 'salt': buffer(salt)}, 36 | } 37 | return plistutils.write_plist(data, plist_format='binary') 38 | -------------------------------------------------------------------------------- /scripts/system-update.sh: -------------------------------------------------------------------------------- 1 | if [ "$UPDATE_SYSTEM" != "true" ] && [ "$UPDATE_SYSTEM" != "1" ]; then 2 | exit 3 | fi 4 | 5 | echo "Downloading and installing system updates..." 6 | softwareupdate -i -a 7 | -------------------------------------------------------------------------------- /scripts/vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | date > /etc/box_build_time 3 | OSX_VERS=$(sw_vers -productVersion | awk -F "." '{print $2}') 4 | 5 | # Set computer/hostname 6 | COMPNAME=macos-10-${OSX_VERS} 7 | scutil --set ComputerName ${COMPNAME} 8 | scutil --set HostName ${COMPNAME}.vagrantup.com 9 | 10 | # Packer passes boolean user variables through as '1', but this might change in 11 | # the future, so also check for 'true'. 12 | if [ "$INSTALL_VAGRANT_KEYS" = "true" ] || [ "$INSTALL_VAGRANT_KEYS" = "1" ]; then 13 | echo "Installing vagrant keys for $USERNAME user" 14 | mkdir "/Users/$USERNAME/.ssh" 15 | chmod 700 "/Users/$USERNAME/.ssh" 16 | curl -L 'https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub' > "/Users/$USERNAME/.ssh/authorized_keys" 17 | chmod 600 "/Users/$USERNAME/.ssh/authorized_keys" 18 | chown -R "$USERNAME" "/Users/$USERNAME/.ssh" 19 | fi 20 | 21 | # Create a group and assign the user to it 22 | dseditgroup -o create "$USERNAME" 23 | dseditgroup -o edit -a "$USERNAME" "$USERNAME" 24 | -------------------------------------------------------------------------------- /scripts/vmware.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Globbing here: VMware Fusion >= 8.5.4 includes a second 4 | # 'darwinPre15.iso' for any OS X guests pre-10.11 5 | TOOLS_PATH=$(find "/Users/$USERNAME/" -name '*darwin*.iso' -print) 6 | 7 | # VMware Fusion specific items 8 | if [ -e .vmfusion_version ] || [[ "$PACKER_BUILDER_TYPE" == vmware* ]]; then 9 | if [ ! -e "$TOOLS_PATH" ]; then 10 | echo "Couldn't locate uploaded tools iso at $TOOLS_PATH!" 11 | exit 1 12 | fi 13 | 14 | TMPMOUNT=`/usr/bin/mktemp -d /tmp/vmware-tools.XXXX` 15 | hdiutil attach "$TOOLS_PATH" -mountpoint "$TMPMOUNT" 16 | 17 | INSTALLER_PKG="$TMPMOUNT/Install VMware Tools.app/Contents/Resources/VMware Tools.pkg" 18 | if [ ! -e "$INSTALLER_PKG" ]; then 19 | echo "Couldn't locate VMware installer pkg at $INSTALLER_PKG!" 20 | exit 1 21 | fi 22 | 23 | echo "Installing VMware tools.." 24 | installer -pkg "$TMPMOUNT/Install VMware Tools.app/Contents/Resources/VMware Tools.pkg" -target / 25 | 26 | # This usually fails 27 | hdiutil detach "$TMPMOUNT" 28 | rm -rf "$TMPMOUNT" 29 | rm -f "$TOOLS_PATH" 30 | 31 | # Point Linux shared folder root to that used by OS X guests, 32 | # useful for the Hashicorp vmware_fusion Vagrant provider plugin 33 | mkdir /mnt 34 | ln -sf /Volumes/VMware\ Shared\ Folders /mnt/hgfs 35 | fi 36 | -------------------------------------------------------------------------------- /scripts/xcode-cli-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [[ ! "$INSTALL_XCODE_CLI_TOOLS" =~ ^(true|yes|on|1|TRUE|YES|ON])$ ]]; then 4 | exit 5 | fi 6 | 7 | # Get and install Xcode CLI tools 8 | OSX_VERS=$(sw_vers -productVersion | awk -F "." '{print $2}') 9 | 10 | # on 10.9+, we can leverage SUS to get the latest CLI tools 11 | if [ "$OSX_VERS" -ge 9 ]; then 12 | # create the placeholder file that's checked by CLI updates' .dist code 13 | # in Apple's SUS catalog 14 | touch /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress 15 | # find the CLI Tools update 16 | PROD=$(softwareupdate -l | grep "\*.*Command Line" | tail -n 1 | awk -F"*" '{print $2}' | sed -e 's/^ *//' | tr -d '\n') 17 | # install it 18 | softwareupdate -i "$PROD" --verbose 19 | rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress 20 | 21 | # on 10.7/10.8, we instead download from public download URLs, which can be found in 22 | # the dvtdownloadableindex: 23 | # https://devimages.apple.com.edgekey.net/downloads/xcode/simulators/index-3905972D-B609-49CE-8D06-51ADC78E07BC.dvtdownloadableindex 24 | else 25 | [ "$OSX_VERS" -eq 7 ] && DMGURL=http://devimages.apple.com.edgekey.net/downloads/xcode/command_line_tools_for_xcode_os_x_lion_april_2013.dmg 26 | [ "$OSX_VERS" -eq 8 ] && DMGURL=http://devimages.apple.com.edgekey.net/downloads/xcode/command_line_tools_for_osx_mountain_lion_april_2014.dmg 27 | 28 | TOOLS=clitools.dmg 29 | curl "$DMGURL" -o "$TOOLS" 30 | TMPMOUNT=`/usr/bin/mktemp -d /tmp/clitools.XXXX` 31 | hdiutil attach "$TOOLS" -mountpoint "$TMPMOUNT" 32 | if [ "$OSX_VERS" -eq 7 ]; then 33 | # using '-allowUntrusted' because Lion CLI tools are so old Apple never built another 34 | # package that doesn't have an expired CA cert. (Expired February 15, 2015) 35 | installer -pkg "$(find $TMPMOUNT -name '*.mpkg')" -allowUntrusted -target / 36 | else 37 | installer -pkg "$(find $TMPMOUNT -name '*.mpkg')" -target / 38 | fi 39 | hdiutil detach "$TMPMOUNT" 40 | rm -rf "$TMPMOUNT" 41 | rm "$TOOLS" 42 | exit 43 | fi 44 | -------------------------------------------------------------------------------- /veewee/NOTICE.erb: -------------------------------------------------------------------------------- 1 | OS X doesn't provide kickstart-like functionality in its installer, so the 2 | template instead includes a shell script that will prepare an ISO such that it 3 | runs an automated install. This makes use of built-in support that exists to 4 | support NetInstall images built with Apple's System Image Utility. 5 | 6 | The script is located at: 7 | 8 | definitions/<%= definition_name %>/prepare_veewee_iso/prepare_veewee_iso.sh, 9 | 10 | and can be passed either the path to an "Install [OS X].app" or the 11 | InstallESD.dmg within. The output DMG is saved to a new file in veewee's 'iso' 12 | directory unless an alternate output path is given. The file is saved in the form: 13 | 14 | OSX_InstallESD_{OS_VERS}_{OS_BUILD}.dmg 15 | 16 | Depending on the host and guest OS versions, additional tools may need to be 17 | either downloaded from Apple or extracted from the source installer image, and 18 | this will take place automatically. 19 | 20 | Once the ISO is prepared, its filename will be automatically set in the 21 | definition.rb file. 22 | 23 | For more details see comments in the script. 24 | -------------------------------------------------------------------------------- /veewee/definition.rb: -------------------------------------------------------------------------------- 1 | Veewee::Definition.declare({ 2 | :cpu_count => '1', 3 | :memory_size=> '2048', 4 | :disk_size => '40000', 5 | :disk_format => 'VDI', 6 | :hostiocache => 'off', 7 | # Currently supported os_type_ids: Darwin_10_7, Darwin_10_7_64, Darwin_10_8_64 8 | :os_type_id => 'Darwin_10_8_64', 9 | # iso_file must be the output of the prepare_veewee_iso.sh script included with this template, 10 | # which modifies an installer downloaded from the Mac App Store for a fully-automated install 11 | :iso_file => "%OSX_ISO%", 12 | :iso_download_timeout => "1000", 13 | :boot_wait => "60", 14 | :ssh_login_timeout => "10000", 15 | :ssh_user => "vagrant", 16 | :ssh_password => "vagrant", 17 | :ssh_key => "", 18 | :ssh_host_port => "7222", 19 | :ssh_guest_port => "22", 20 | :sudo_cmd => "echo '%p'|sudo -S sh '%f'", 21 | :shutdown_cmd => "shutdown -h now", 22 | :postinstall_files => [ 23 | "vagrant.sh", # Vagrant config (vagrant user, keys) 24 | "vmware.sh", # VMware config 25 | "xcode-cli-tools.sh", # Xcode CLI tools 26 | "chef-omnibus.sh", # Chef Omnibus install 27 | "puppet.sh", # Puppet install from Hashicorp's puppet-boostrap repo 28 | ], 29 | :postinstall_timeout => "10000" 30 | }) 31 | --------------------------------------------------------------------------------