<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Tech Talk & Mind Dumps]]></title><description><![CDATA[Thoughts, stories and ideas.]]></description><link>https://blog.grayw.co.uk/</link><image><url>https://blog.grayw.co.uk/favicon.png</url><title>Tech Talk &amp; Mind Dumps</title><link>https://blog.grayw.co.uk/</link></image><generator>Ghost 4.4</generator><lastBuildDate>Fri, 23 Feb 2024 02:09:33 GMT</lastBuildDate><atom:link href="https://blog.grayw.co.uk/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Packaging & Deploying FortiClient EMS with Jamf]]></title><description><![CDATA[How to re-package and deploy FortiClient EMS using Jamf Pro and other MDM solutions.]]></description><link>https://blog.grayw.co.uk/packaging-deploying-forticlient-ems-with-jamf/</link><guid isPermaLink="false">62b19fa9b70baa41e183e0ee</guid><category><![CDATA[apple]]></category><category><![CDATA[jamf]]></category><category><![CDATA[macos]]></category><category><![CDATA[forticlient]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Fri, 05 Aug 2022 15:13:37 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/08/shubham-dhage-gC_aoAjQl2Q-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/08/shubham-dhage-gC_aoAjQl2Q-unsplash.jpg" alt="Packaging &amp; Deploying FortiClient EMS with Jamf"><p>If you&apos;ve read my <a href="https://blog.grayw.co.uk/obtaining-the-forticlient-offline-installer-for-distribution/">previous post on FortiClient</a>, you&apos;ll already be aware that I&apos;m a great fan of their top-tier methods for deploying this particular application &#x2026;</p><p>Well, it was only a matter of time before we had to delve deeper in to this topic. Yes, that&apos;s right, EMS. Here&apos;s to another can of rotting worms.</p><p>In this episode of how to rapidly lose more hair, we&apos;ll go through packaging and deploying FortiClient EMS 7.x with Jamf Pro.</p><hr><h2 id="create-the-installation-package">Create the Installation Package</h2><p>First up, you&apos;re going to need a generated package from your EMS service, which will come as a .dmg.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-1.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="621" height="523" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/forticlient-ems-image-1.png 600w, https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-1.png 621w"></figure><p>After <a href="https://blog.grayw.co.uk/packaging-deploying-uniflow-smartclient-for-macos/">finding hidden config files in another application I was packaging</a>, I&apos;ve learned the hard way to double-check this mounted volume. What do we have?</p><pre><code>10244  3 Jun 17:48 .DS_Store
2048  3 Jun 17:48 .fseventsd
266257984  3 Jun 17:48 Install.mpkg
258  3 Jun 17:48 Technical Documentation.webloc
2048  3 Jun 17:48 Uninstall.app
16485  3 Jun 17:48 background.jpg
2048  3 Jun 17:48 fctdata</code></pre><p>Looks like we&apos;ve got a hidden folder in here with a load of executables, so we&apos;ll need to include that in the re-package, otherwise I&apos;m sure something will break.</p><p>Under <strong>fctdata</strong>, it looks like we&apos;ve got our configuration files, so we&apos;ll certainly need to include them, too. </p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-2.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="988" height="296" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/forticlient-ems-image-2.png 600w, https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-2.png 988w" sizes="(min-width: 720px) 720px"></figure><p>All in all, I think it&apos;s best we re-package this whole thing to ensure we don&apos;t miss anything at all. There are a few ways we can do this, but I&apos;ll stick with Composer, as we&apos;ll be pushing with Jamf.</p><p>As we need to grab some hidden files, we can&apos;t just drag and drop, so create a folder within the /tmp/ folder.</p><pre><code>sudo mkdir /tmp/forticlient-ems</code></pre><p>Now, copy the contents of the mounted DMG over to our tmp directory.</p><pre><code>sudo cp -r /Volumes/FortiClient /tmp/forticlient-ems
sudo cp -r /Volumes/FortiClient/.fseventsd /tmp/forticlient-ems</code></pre><pre><code>/tmp/forticlient-ems

.fseventsd
Install.mpkg
Technical Documentation.webloc
Uninstall.app
background.jpg
fctdata</code></pre><p>Fantastic, that&apos;s the contents of the file in the temporary directory. Open this location in finder, and fire up Composer. We can then drag that in to the window in readiness to create a pkg.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-4.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1508" height="696" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/forticlient-ems-image-4.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/06/forticlient-ems-image-4.png 1000w, https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-4.png 1508w" sizes="(min-width: 720px) 720px"></figure><p>We&apos;ll also create a postinstall script to run the .mpkg installer.</p><pre><code>sudo installer -pkg /tmp/forticlient-ems/Install.mpkg -target /</code></pre><p>With that done, we can <strong>Build as PKG</strong>. Then we need to test it and make sure it installs the application as expected as if we were installing it as the end user.</p><h2 id="testing-the-custom-package">Testing the Custom Package</h2><p>For this first bit, we&apos;ll keep it simple, sign in to our VM as a standard user, and enter some admin credentials when prompted to install the PKG.</p><p>Yep, that worked!</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-5.png" width="1695" height="1123" loading="lazy" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/forticlient-ems-image-5.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/06/forticlient-ems-image-5.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/06/forticlient-ems-image-5.png 1600w, https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-5.png 1695w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-6.png" width="1695" height="1123" loading="lazy" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/forticlient-ems-image-6.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/06/forticlient-ems-image-6.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/06/forticlient-ems-image-6.png 1600w, https://blog.grayw.co.uk/content/images/2022/06/forticlient-ems-image-6.png 1695w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>As you can see, it comes with an absolute barrage of pop-ups that a non-admin user is never going to be able to resolve, and the FortiClient tray icon is present.</p><p>If we check our FortiClient Console, we can see that it&apos;s connected and managed by FortiClient Cloud, and also has our remote access details pre-filled, and is just waiting for our login. Fantastic, that means all the custom settings have worked, too.</p><p>That&apos;s the easy part, complete. Time to revert the snapshot to our clean working base, and work through the next part. Hiding those pop-ups and pre-approving the bits we need to.</p><hr><h1 id="configuration-profile">Configuration Profile</h1><p>With the application installing, let&apos;s work on hiding all these pop-ups and allowing permissions to things.</p><p>The first helpful link in this section:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://community.jamf.com/t5/jamf-pro/quot-fortitray-quot-would-like-to-add-vpn-configurations/m-p/255234/highlight/true#M236834"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Re: &#x201C;FortiTray&#x201D; would like to add VPN configurations</div><div class="kg-bookmark-description">Heya, sorry for the late reply, I finally figured this out. To avoid the VPN popup configuration, we set a dummy VPN configuration that will be used by Forticlient on runtime : Nothing else is checked, make sure that the&#xA0;Identifier and&#xA0;Provider Bundle Identifier are set to &#x201C;com.fortinet.forticlient&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://community.jamf.com/html/@341C36E148083396DBCB6E6A9C18E572/assets/favicon.ico" alt="Packaging &amp; Deploying FortiClient EMS with Jamf"><span class="kg-bookmark-author">Jamf Nation</span><span class="kg-bookmark-publisher">e672e508-80b8-4 New Contributor II</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://community.jamf.com/t5/image/serverpage/avatar-name/mexican/avatar-theme/candy/avatar-collection/food/avatar-display-size/profile/version/2?xdesc=1.0" alt="Packaging &amp; Deploying FortiClient EMS with Jamf"></div></a></figure><p>So, first up is removing that &quot;Permission required for VPN&quot; pop up. This requires a VPN configuration profile. Let&apos;s create a new Configuration Profile, and set a VPN payload.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-vpn.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1373" height="1546" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-configprofile-vpn.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-configprofile-vpn.png 1000w, https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-vpn.png 1373w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Configuration Profile - VPN Payload</figcaption></figure><p>Scope it, test it. This removes the VPN pop-up. One down, three more to go. Let&apos;s tackle the System Extension warning next.</p><h2 id="system-extension">System Extension</h2><p>We need to get information about that system extension. The easiest way to do this is to check what extensions are active before, and after manually approving the extension. Don&apos;t forget to roll your snapshot back again once you&apos;ve checked ;)!</p><pre><code>systemextensionsctl list
1 extension(s)
--- com.apple.system_extension.network_extension
enabled	active	teamID	bundleID (version)	name	[state]
*	*	AH4XFXJ7DK	com.fortinet.forticlient.macos.vpn.nwextension (1.5.0/B20220228)	vpnprovider	[activated enabled]</code></pre><p>Great, now we know what system extension we&apos;re approving. Let&apos;s add a new payload to our configuration profile.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-sysext.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1386" height="486" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-configprofile-sysext.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-configprofile-sysext.png 1000w, https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-sysext.png 1386w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Configuration Profile - System Extension Payload</figcaption></figure><p>With that tested, we no longer get the &quot;FortiTray would like permission to create VPN profiles&quot;. What&apos;s next? Well, all sorts.</p><h2 id="security-privacy-permissions">Security &amp; Privacy Permissions</h2><p>Trawling through the Fortinet documentation, I finally found a &quot;Special Notices&quot; section, which provides a list of services that need permissions allowing.</p><p><a href="http://docs.fortinet.com/document/forticlient/7.0.5/macos-release-notes/223986/special-notices">http://docs.fortinet.com/document/forticlient/7.0.5/macos-release-notes/223986/special-notices</a></p><p>Right then, let&apos;s get started.</p><p>We need to sign our life away for the following components:</p><ul><li>fcaptmon</li><li>fctservctl</li><li>fctservctl2</li><li>fmon</li><li>fmon2</li><li>FortiClient</li><li>FortiGuardAgent</li></ul><p>Time to add a new PPPC payload to our configuration profile:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-pppc.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1369" height="2798" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-configprofile-pppc.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-configprofile-pppc.png 1000w, https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-pppc.png 1369w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Configuration Profile - PPPC Payload</figcaption></figure><p>Phew, that was a long one! Here&apos;s all of that in a way you can copy.</p><pre><code>/Library/Application\ Support/Fortinet/FortiClient/bin/fcaptmon

identifier fcaptmon and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = AH4XFXJ7DK


//

/Library/Application Support/Fortinet/FortiClient/bin/fctservctl2

identifier fctservctl2 and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = AH4XFXJ7DK

//

/Library/Application\ Support/Fortinet/FortiClient/bin/fmon

identifier fmon and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = AH4XFXJ7DK

//

com.fortinet.forticlient.macos.antivirus

anchor apple generic and identifier &quot;com.fortinet.forticlient.macos.antivirus&quot; and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = AH4XFXJ7DK)

//

com.fortinet.FortiClient

identifier &quot;com.fortinet.FortiClient&quot; and anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = AH4XFXJ7DK</code></pre><p>You&apos;ll notice that fmon2 is missing from this list. Well, it&apos;s not. No matter how many times I tried to get fmon2 approved, I received warnings about needing to allow access. Turns out that I actually needed to approve com.fortinet.forticlient.macos.antivirus - not fmon2. Thanks for wasting a week there, Fortinet!</p><p>That&apos;s our VPN, PPPC, and System Extension pop-ups dealt with. Let&apos;s try to tackle that certificate warning.</p><h2 id="certificates">Certificates</h2><p>This had me boggled for a few days, until a moment of clarity in the shower (it&apos;s where my <em>best</em> thinking happens). Honestly, not sure why it took me this long.</p><p>I approved the certificate prompt on the test VM, then exported them from the Keychain.</p><p>I found we needed two certificates from here. <strong>support.cer</strong>, and <strong>fortinet-ca2.cer</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-keychain.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="882" height="538" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-configprofile-keychain.png 600w, https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-keychain.png 882w" sizes="(min-width: 720px) 720px"><figcaption>Keychain Access showing the Forticlient Certificates</figcaption></figure><p>As this contains certificates which could be revoked, or need to be changed at different intervals to the other settings, I created a new profile with a Certificate payload.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-certs.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1373" height="1436" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-configprofile-certs.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-configprofile-certs.png 1000w, https://blog.grayw.co.uk/content/images/2022/08/fems-configprofile-certs.png 1373w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Configuration Profile - Certificate Payload</figcaption></figure><p>After testing this, I found I had to restart the machine for the certificate trust to be in place. I added a restart option to the installation policy, and no more pop-ups!</p><p>That&apos;s two configuration profiles, with a total of 4 payloads between them.</p><h2 id="wrap-up">Wrap-up</h2><p>After endless testing with the VM, I deployed it to a few different machines, across a couple of different macOS versions, to ensure that it behaved as expected.</p><p>I <strong>HATE</strong> anything to do with FortiClient. It&apos;s always such a pain.</p><p>Let&apos;s see what we have for this. </p><ol><li>A custom package with the contents from the pre-packaged DMG</li></ol><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-composer.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1420" height="561" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-composer.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-composer.png 1000w, https://blog.grayw.co.uk/content/images/2022/08/fems-composer.png 1420w" sizes="(min-width: 720px) 720px"></figure><p>2. &#xA0;A computer policy to install the package - scoped to staff machines, and made available in Self Service (don&apos;t want to be pushing this out with an enforced restart).</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-profile-install.png" class="kg-image" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" loading="lazy" width="1631" height="848" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-profile-install.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-profile-install.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/08/fems-profile-install.png 1600w, https://blog.grayw.co.uk/content/images/2022/08/fems-profile-install.png 1631w" sizes="(min-width: 720px) 720px"></figure><p>3. &#xA0;Two Configuration Profiles, scoped to our staff machines.</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-profile-one.png" width="1645" height="850" loading="lazy" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-profile-one.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-profile-one.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/08/fems-profile-one.png 1600w, https://blog.grayw.co.uk/content/images/2022/08/fems-profile-one.png 1645w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/08/fems-profile-two.png" width="1645" height="850" loading="lazy" alt="Packaging &amp; Deploying FortiClient EMS with Jamf" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/08/fems-profile-two.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/08/fems-profile-two.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/08/fems-profile-two.png 1600w, https://blog.grayw.co.uk/content/images/2022/08/fems-profile-two.png 1645w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>We can finally move this out of test, and close the job off!</p><p>I hope this helped some of you out there.</p><p></p><p>-- </p><p>Photo by <a href="https://unsplash.com/@theshubhamdhage?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Shubham Dhage</a> on <a href="https://unsplash.com/s/photos/network?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Packaging & Deploying uniFLOW SmartClient for macOS]]></title><description><![CDATA[<p></p><p>When it comes to installing applications for end users at scale, especially for those on macOS, not every developer plays the game. The combination of packaging/zipping/goodness knows what is vast.</p><p>uniFLOW is no exception when it comes to SmartClient. A .pkg, inside an .iso, with hidden files.</p><p>I&</p>]]></description><link>https://blog.grayw.co.uk/packaging-deploying-uniflow-smartclient-for-macos/</link><guid isPermaLink="false">62bd6166b70baa41e183e1d8</guid><category><![CDATA[macos]]></category><category><![CDATA[jamf]]></category><category><![CDATA[uniflow]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Thu, 30 Jun 2022 12:58:19 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/06/devon-janse-van-rensburg-Y_2niQFOJpo-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/06/devon-janse-van-rensburg-Y_2niQFOJpo-unsplash.jpg" alt="Packaging &amp; Deploying uniFLOW SmartClient for macOS"><p></p><p>When it comes to installing applications for end users at scale, especially for those on macOS, not every developer plays the game. The combination of packaging/zipping/goodness knows what is vast.</p><p>uniFLOW is no exception when it comes to SmartClient. A .pkg, inside an .iso, with hidden files.</p><p>I&apos;m writing this as a reminder on how I did this, as I need to deploy an update to the client.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/06/blog-pdsmartclient-1.png" class="kg-image" alt="Packaging &amp; Deploying uniFLOW SmartClient for macOS" loading="lazy" width="722" height="453" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/blog-pdsmartclient-1.png 600w, https://blog.grayw.co.uk/content/images/2022/06/blog-pdsmartclient-1.png 722w" sizes="(min-width: 720px) 720px"></figure><hr><p>The first mistake I made, when I did this with the original deployment, was not checking for hidden config files within the .iso (&#x2026; no words). I was deploying the .pkg and couldn&apos;t figure out why the client wasn&apos;t connecting to the backend.</p><p>On further inspection, there&apos;s a &quot;.tenantconfig.plist&quot; file inside the .iso file with the .pkg. Because of course there is.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/06/blog-pdsmartclient-2.png" class="kg-image" alt="Packaging &amp; Deploying uniFLOW SmartClient for macOS" loading="lazy" width="730" height="433" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/blog-pdsmartclient-2.png 600w, https://blog.grayw.co.uk/content/images/2022/06/blog-pdsmartclient-2.png 730w" sizes="(min-width: 720px) 720px"></figure><p>My next deployment attempt was to pop both these files in to a new .pkg using Composer, and then run a <em>postinstall</em> script to install the .pkg. </p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/06/blog-pdsmartclient-3.png" class="kg-image" alt="Packaging &amp; Deploying uniFLOW SmartClient for macOS" loading="lazy" width="1508" height="696" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/06/blog-pdsmartclient-3.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/06/blog-pdsmartclient-3.png 1000w, https://blog.grayw.co.uk/content/images/2022/06/blog-pdsmartclient-3.png 1508w" sizes="(min-width: 720px) 720px"></figure><pre><code class="language-Bash">sudo installer -pkg /tmp/uniflowclient/SmartClientforMac.pkg -target /</code></pre><p>This resolved the issue.</p><p>Not rocket science, but as I said, I wrote this to remind myself, and hopefully save someone else 5 minutes.</p><hr><p>Photo by <a href="https://unsplash.com/@huntleytography?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Devon Janse van Rensburg</a> on <a href="https://unsplash.com/s/photos/mac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Fully Automated Lab iMac Deployment with Jamf Pro & ADE: Part 6 - Post Series Summary]]></title><description><![CDATA[In this summary, we review the whole process of onboarding a Mac in a zero touch workflow.]]></description><link>https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-6-post-series-summary/</link><guid isPermaLink="false">62014c0cb70baa41e183d75d</guid><category><![CDATA[apple]]></category><category><![CDATA[Jamf Deployment Series]]></category><category><![CDATA[macos]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Tue, 08 Mar 2022 17:54:03 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/03/quaritsch-photography-m2zuB8DqwyM-unsplash-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/03/quaritsch-photography-m2zuB8DqwyM-unsplash-1.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"><p></p><h2 id="introduction">Introduction</h2><hr><p>Firstly, apologies. I didn&apos;t manage to get this post out on Thursday as I had planned. A few things got in the way. I began writing, but didn&apos;t finish.</p><hr><p>Well, I can&apos;t believe we&apos;ve finally come to the end of this post series. This is the first time I&apos;ve truly tried to give something back to the MacAdmins and Jamf community that has helped me so much over the years. Without which, most of what I&apos;ve done would not have been possible.</p><p>Adjusting to this role has been challenging. I have never been a true fan of Apple, or their products. I&apos;m still not entirely convinced they have a perfect role within an enterprise style environment, either. However, the ease and speed at which I can deploy iPhones and iPads, and have them under a decent level of management, has been spectacular. And, with Apple heading down this route for macOS devices, there may be a future in it. Especially with declarative management. This post series was a gentle nod to the fact that it seems to be getting closer.</p><p>Adjusting to writing these posts has also been challenging, but a great learning opportunity. By the fourth part, I felt I had settled in to the writing a little better. The first three felt, somewhat, rushed in comparison. I&apos;ve still got a lot that I can improve upon, but that&apos;s what it&apos;s all about, after all.</p><h2 id="series-summary">Series Summary</h2><p>If you haven&apos;t followed the posts each week, I don&apos;t blame you. This will be a great chance to figure out what&apos;s been going on, and what was achieved across all of them. I&apos;ll provide a link to each post with a short summary.</p><hr><h3 id="part-oneprerequisites-and-prestage">Part One - Prerequisites and PreStage</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-dep-part-1/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage</div><div class="kg-bookmark-description">In this first post of the series, we cover Apple School Manager, Jamf ADE, and the initial PreStage settings.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"></div></a></figure><p>The first post was a boring, but somewhat important part in this series. In it, we lay the groundwork and cover the prerequisites required to make this all work.</p><p>The three main components are:</p><ul><li>Apple School Manager</li><li>Jamf Pro</li><li>macOS Big Sur or later</li></ul><p>In it, we prepare the workflow to take the device from ordering, to PreStage via Automated Device Enrolment (DEP).</p><hr><h3 id="part-twofrom-prestage-to-auto-logon">Part Two - From PreStage to Auto Logon</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-2/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon</div><div class="kg-bookmark-description">In the second post of the series, we cover the creation of scripts, smart groups, policies, and extension attributes for the auto logon stage.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-1.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"></div></a></figure><p>This is quite a big part of the project. We cover the creation of Smart Groups, Scripts, Extension Attributes, and Policies.</p><p>Using these building blocks, we are able to take a device from the default log on screen of PreStage, and automatically log it in with our newly created account.</p><hr><h3 id="part-threepreparing-for-deployment">Part Three - Preparing for Deployment</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-3-preparing-for-application-deployment/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment</div><div class="kg-bookmark-description">With most of the basic policies in place, we start creating the foundations for deploying applications using DEPNotify.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-3.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"></div></a></figure><p>This one lays the groundwork for part four. Here, we make use of further Smart Groups, Scripts, Policies, Extension Attributes, and DEPNotify. </p><hr><h3 id="part-fourdeploying-applications">Part Four - Deploying Applications</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-4-deploying-our-applications/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications</div><div class="kg-bookmark-description">We make use of Inventory Preload, Smart Groups, and Policies to deploy our applications with DEPNotify.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-4.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"></div></a></figure><p>In part four, this is where we really start to bring the whole thing to life, and see the results of all the hard work that was done in the last three parts, making this whole thing possible.</p><p>Yet again, more Smart Groups, Scripts, Policies, and one of my favourite features, Inventory Preload.</p><hr><h3 id="part-fivegetting-it-user-ready">Part Five - Getting it User Ready</h3><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-5-user-ready/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready</div><div class="kg-bookmark-description">We take our final step on this fully automated journey, making the device user ready with NoMAD.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/03/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary"></div></a></figure><p>And finally, part five. This was it. The culmination of months of planning, testing, stress, confusion, and severe hair loss.</p><p>We create the final few Smart Groups, Policies, Scripts, and make use of NoMAD, and NoMAD Login to make our device truly user ready. </p><p>With all this in place, we can do a full zero touch setup on our brand-new iMac.</p><ul><li>Assign it to our MDM server in Apple School Manager</li><li>Use Inventory Preload to provide Jamf some information</li><li>Plug it in</li></ul><p>Once it&apos;s plugged in, it will automate its entire setup process, installing all our applications inside 2 hours. Completely ready for use.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-lab-zero-touch-policies-overview-1.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary" loading="lazy" width="1199" height="1410" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-lab-zero-touch-policies-overview-1.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-lab-zero-touch-policies-overview-1.png 1000w, https://blog.grayw.co.uk/content/images/2022/03/blog-lab-zero-touch-policies-overview-1.png 1199w" sizes="(min-width: 720px) 720px"></figure><p>As you can see, our <strong>** Lab - Zero Touch - Onboarding - DEPNotify **</strong> &#xA0;ran for 1 hour, 33 minutes, 12 seconds. Sixty-nine minutes of that was Adobe.</p><p>Without it, you&apos;re looking at a fully managed and useable device within 15-20 minutes. Start to finish.</p><p>If you&apos;ve read this part, you may notice that Office 365 is missing. This was down to a typo in the script of the trigger name. It takes about 5.5 to 8 minutes.</p><hr><h2 id="overview-graphic">Overview Graphic</h2><p>Below is an incredibly rough workflow overview graphic attempting to show this whole process. I won&apos;t lie, I&apos;m not overly happy with it, but I think it serves its purpose well enough.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/Lab-iMac.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 6 - Post Series Summary" loading="lazy" width="1680" height="1240" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/Lab-iMac.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/Lab-iMac.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/03/Lab-iMac.png 1600w, https://blog.grayw.co.uk/content/images/2022/03/Lab-iMac.png 1680w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="a-video-summary">A Video Summary</h2><p>OK, that&apos;s enough waffle. What does this actually look like when it happens? Well, here&apos;s a video showing the process. </p><p>I have trimmed out parts of the video to make it shorter (who wants to watch almost 2 hours of this?). &#xA0;But the whole process is there. This was not a professional trim job!</p><figure class="kg-card kg-embed-card"><iframe src="https://player.vimeo.com/video/685967458?h=79ee3adb2a&amp;app_id=122963" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen title="macOS - Zero Touch - Lab Deployment"></iframe></figure><hr><h2 id="thanks-contacts">Thanks &amp; Contacts</h2><p>As I mentioned at the start. None of this would have been possible without the incredible communities that I&apos;m a member of. First and foremost, to <a href="https://macadmins.org">MacAdmins.org</a>. Their Slack channels provide access to some incredibly talented people who are always friendly and willing to help. There is an absolute treasure trove of information in there if you search for it.</p><p>Next, the <a href="https://community.jamf.com">Jamf Community</a>. If you have a question about something Jamf, it&apos;s probably already been answered here in some way.</p><p>And also a big thank you to Scripting OSX for the post mentions in the weekly round-up. Seeing that made my day, thank you!</p><p><a href="https://scriptingosx.com/">https://scriptingosx.com/</a></p><p>If you want to get in touch with me, you can find me in various places. The MacAdmins Slack for one (#uk)! However, the quickest and easiest place is over on the <a href="https://fosstodon.org/@gray">Fosstodon.org</a> Mastodon instance, or Matrix: <a href="https://matrix.to/#/@grayw:opensuse.org">@grayw:opensuse.org</a></p><p>The post Photo for this post series was by <a href="https://unsplash.com/@quaritsch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Quaritsch Photography</a> on <a href="https://unsplash.com/s/photos/imac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Fully Automated Lab iMac Deployment with Jamf Pro & ADE: Part 5 - User Ready]]></title><description><![CDATA[We take our final step on this fully automated journey, making the device user ready with NoMAD.]]></description><link>https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-5-user-ready/</link><guid isPermaLink="false">62014be9b70baa41e183d759</guid><category><![CDATA[apple]]></category><category><![CDATA[macos]]></category><category><![CDATA[Jamf Deployment Series]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Tue, 01 Mar 2022 15:43:02 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/03/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/03/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><p></p><h2 id="intro">Intro</h2><p>Wow, I can&apos;t believe we&apos;re here. The final stage of this workflow. All that&apos;s left to do is make the device ready for the user. What a journey! If you&apos;ve made it this far, congratulations. Reading my waffle is not easy. However, if you&apos;re just jumping in, you may want to go back and read the other posts in the series - especially the first. There are some prerequisites that you really shouldn&apos;t skip. Alternatively, you could wait until Thursday, when a full summary post goes up.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/tag/jamf-deployment-series/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Jamf Deployment Series - Tech Talk &amp; Mind Dumps</div><div class="kg-bookmark-description">Over the course of this blog post series, we will cover the whole process of automating the setup of iMac&#x2019;s in a multi-user lab environment.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-2.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><p>Let&apos;s get on with the show.</p><h2 id="previously">Previously</h2><p>In part 4, we covered creating some more Smart Groups, filling out our DEPN script with the rest of the application policies we wanted to deploy, and making use of the Inventory Preload feature.</p><p>In this post, we&apos;re going to go over the last few lines of our DEPN script to get them into a user ready state.</p><pre><code class="language-Bash"># ## Send updated inventory for Smart Groups and check for any remaining scoped policies
DEPNotify &quot;Command: MainText: Installing Room Specific Requirements&quot;
DEPNotify &quot;Status: Update inventory record.&quot;
jamfCommand recon
sleep 3
jamfCommand policy
sleep 3
/usr/bin/defaults write /Library/Preferences/co.uk.grayw.blog onboardingComplete -string &quot;Complete&quot;
#
## Prepare for Final Usage
DEPNotify &quot;Status: Install NoMAD &amp; NoMAD Login&quot;
jamfCommand labzt-nomad
DEPNotify &quot;Status: Removing Auto Logon&quot;
jamfCommand labzt-remove-autologon</code></pre><p>Let&apos;s work from top to bottom.</p><h2 id="onboarding-complete">Onboarding Complete</h2><p>There are certainly improvements we could throw in here, and perhaps this isn&apos;t the perfect method, but I want to know when a device has reached the end of this process. To do that, I&apos;m going to write out to a plist file.</p><pre><code class="language-Bash">/usr/bin/defaults write /Library/Preferences/co.uk.grayw.blog onboardingComplete -string &quot;Complete&quot;</code></pre><p>There&apos;s a reason I&apos;m not doing this before the recon command. We don&apos;t want other policies running during that jamfCommand policy line, and interfering with our onboarding process. We&apos;ll let it get those on the next check in once we&apos;re done.</p><p>To make scoping of generic policies easier, we&apos;re going to make use of this by creating another Extension Attribute.</p><h3 id="extension-attribute">Extension Attribute</h3><p>We&apos;ll create the Extension Attribute as a script that looks like this.</p><pre><code class="language-Bash">#!/bin/bash

status=`defaults read /Library/Preferences//Library/Preferences/ac.uk.grayw.blog onboardingComplete`
if [ &quot;$status&quot; == &quot;Complete&quot; ]; then
    echo &quot;&lt;result&gt;Onboarding Complete&lt;/result&gt;&quot;
elif [ &quot;$status&quot; != &quot;Complete&quot; ]; then
    echo &quot;&lt;result&gt;Onboarding Not Complete&lt;/result&gt;&quot;
fi</code></pre><p>In Jamf, that will look like this:</p><p><strong>Display Name:</strong> Onboarding Status<br><strong>Description:</strong> Checks to see if the onboarding defaults string is set to complete<br><strong>Data Type:</strong> String<br><strong>Inventory Display:</strong> Extension Attributes<br><strong>Input Type:</strong> Script</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-extensionattribute-onboardingcomplete.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="2000" height="1218" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-extensionattribute-onboardingcomplete.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-extensionattribute-onboardingcomplete.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/03/blog-extensionattribute-onboardingcomplete.png 1600w, https://blog.grayw.co.uk/content/images/size/w2400/2022/03/blog-extensionattribute-onboardingcomplete.png 2400w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Extension Attribute Screen</figcaption></figure><p>Great, now we have another attribute to work with, we need to actually do something with it.</p><h3 id="smart-group">Smart Group</h3><p>Let&apos;s create another Smart Group based on this new attribute.</p><p><strong>Display Name: </strong>Devices - Lab - Onboarding Complete<br><strong>Criteria:</strong> Onboarding Status <strong>IS</strong> Onboarding Complete</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-smartgroup-onboardingcomplete.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="2000" height="1148" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-smartgroup-onboardingcomplete.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-smartgroup-onboardingcomplete.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/03/blog-smartgroup-onboardingcomplete.png 1600w, https://blog.grayw.co.uk/content/images/2022/03/blog-smartgroup-onboardingcomplete.png 2296w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Smart Group Screen</figcaption></figure><p>Done. Now we can scope any future policies to this group knowing that they have very likely finished doing what we <em>need</em> them to do first, and that they are in a certain state. You could also base other Smart Groups off of this, too. Perhaps you only want room specific software to install after this whole process is complete so that the users can just get on with the basics? Clone this, and add an <em>AND</em> &#xA0;clause with the room information. You then have a group based on the room AND that they need to have completed the onboarding.</p><p>Right, let&apos;s tackle the final two policies.</p><h2 id="installing-nomad-nomad-login">Installing NoMAD &amp; NoMAD Login</h2><p>Our lab machines are authenticating against Active Directory. They are not bound, and haven&apos;t been for some time. Quite frankly, it&apos;s a nightmare. To get around that issue, I make use of <a href="https://nomad.menu/">NoMAD</a> and <a href="https://nomad.menu/">NoMAD Login</a>.</p><p>If you haven&apos;t heard of these, it&apos;s worth taking a look. However, keep in mind that Jamf acquired them back in 2018: <a href="https://www.jamf.com/resources/press-releases/jamf-acquires-nomad-the-leading-solution-for-streamlining-mac-authentication-and-account-management/">https://www.jamf.com/resources/press-releases/jamf-acquires-nomad-the-leading-solution-for-streamlining-mac-authentication-and-account-management/</a> - Since then, nothing much has really happened development-wise, as they&apos;re pushing the Jamf Connect (paid licence) angle.</p><p>Not all hope is lost yet. Work is underway with NoMAD 2 with all sorts of quality of life improvements, and feature updates. <strong>It is not production ready</strong>.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://gitlab.com/Mactroll/nomad2"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Joel Rennich / nomad2</div><div class="kg-bookmark-description">NoMAD the second major version.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://gitlab.com/assets/touch-icon-ipad-retina-8ebe416f5313483d9c1bc772b5bbe03ecad52a54eba443e5215a22caed2a16a2.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">GitLab</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://gitlab.com/uploads/-/system/project/avatar/23411132/NoMAD_Logo.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><p>For now, we&apos;ll continue to use the versions available from nomad.menu. </p><p>The first thing we&apos;ll need to do is download and get the PKGs on to your distribution point(s). I&apos;ll leave that part up to you. But, if you want to know how I&apos;m syncing HTTPS DP&apos;s, let me know. I&apos;ll write something up.</p><p>My only suggestion is to number these packages. You want to install them in a specific order.</p><ul><li>1.NoMAD.pkg</li><li>2.NoMAD-LaunchAgent.pkg</li><li>3.NoMADLoginAD{version}.pkg</li></ul><p>I have found that not installing in this order can lead to unexpected behaviour.</p><h3 id="policy">Policy</h3><p>With the files in place, we now need to create the policy. The settings for mine are as follows:</p><p><strong>Display Name:</strong> Lab - Zero Touch - Install - NoMAD<br><strong>Category:</strong> Lab - Onboarding - General<br><strong>Trigger: </strong> Custom: labzt-nomad<br><strong>Execution Frequency: </strong>Once Per Computer</p><p>When you add the packages to the policy, they should install in order, like this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-policies-nomad.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1704" height="1356" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-policies-nomad.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-policies-nomad.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/03/blog-policies-nomad.png 1600w, https://blog.grayw.co.uk/content/images/2022/03/blog-policies-nomad.png 1704w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Policy Packages Screen</figcaption></figure><p>We&apos;ll scope this to our <em>Devices - Lab Auto Advance - Zero Touch</em> Smart Group to ensure that it runs when called from the DEPN script.</p><h3 id="configuration-profile">Configuration Profile</h3><p>The other really important part you&apos;re going to need to create for NoMAD is a Configuration Profile, and ensure that it is scoped to these devices. Again, the best option here is to our <em>Devices - Lab Auto Advance - Zero Touch</em> Smart Group.</p><p>Due to the obscene number of settings, and environment variations, I can&apos;t even begin to tell you what you should have set in here. What I would suggest is to make use of the Profile Creator tool.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/ProfileCreator/ProfileCreator"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - ProfileCreator/ProfileCreator: macOS app to create standard or customized configuration profiles.</div><div class="kg-bookmark-description">macOS app to create standard or customized configuration profiles. - GitHub - ProfileCreator/ProfileCreator: macOS app to create standard or customized configuration profiles.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">ProfileCreator</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/a5207c30275dafc46700f3e4f329fb0ae667566e602a273ef84443278eb493a3/ProfileCreator/ProfileCreator" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><p>Use this to create a payload for both NoMAD <strong>and</strong> NoMAD Login. This has been working for me in this way for 5 or so years, back when these devices were running through Open Directory. Way before our Jamf times, and before I was part of this team, but somehow still had some say in how the Mac&apos;s behaved &#x2026; (I&apos;m beginning to feel like I&apos;ve been set up).</p><p>However, there is an alternative. <strong>I have not tested this method yet</strong>.</p><p>Jamf has greatly improved the ability to import custom schemas for external applications within their configuration profile creation tool. And there are schemas out there for both of these applications.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/Jamf-Custom-Profile-Schemas/ProfileManifestsMirror/blob/main/manifests/ManagedPreferencesApplications/com.trusourcelabs.NoMAD.json"><div class="kg-bookmark-content"><div class="kg-bookmark-title">ProfileManifestsMirror/com.trusourcelabs.NoMAD.json at main &#xB7; Jamf-Custom-Profile-Schemas/ProfileManifestsMirror</div><div class="kg-bookmark-description">Jamf JSON schema manifests automatically generated from ProfileCreator manifests (https://github.com/ProfileCreator/ProfileManifests) - ProfileManifestsMirror/com.trusourcelabs.NoMAD.json at main &#xB7;...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">Jamf-Custom-Profile-Schemas</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/abbfef7af27728467d4fc87e523500e60ff436c1585c5794f662fc0c80ff0a75/Jamf-Custom-Profile-Schemas/ProfileManifestsMirror" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/Jamf-Custom-Profile-Schemas/ljcacioppo-schemas/blob/main/menu.nomad.login.ad.json"><div class="kg-bookmark-content"><div class="kg-bookmark-title">ljcacioppo-schemas/menu.nomad.login.ad.json at main &#xB7; Jamf-Custom-Profile-Schemas/ljcacioppo-schemas</div><div class="kg-bookmark-description">Custom schemas for Jamf Pro. Contribute to Jamf-Custom-Profile-Schemas/ljcacioppo-schemas development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">Jamf-Custom-Profile-Schemas</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/1ac556953e32b453f46dd59a8ccb653c1b864595449d22383f69fd1e12c5ae3a/Jamf-Custom-Profile-Schemas/ljcacioppo-schemas" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><p>I plan on migrating to this method, as it will make it far easier to update/change settings in the profile on the fly. Currently, it&apos;s stuck as a single uploaded .mobileconfig that I created years ago. If you&apos;d like to see a post on this testing, let me know. But for now, here&apos;s a quick screenshot of not even a quarter of the settings made available through the custom schema.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-configprofile-nomadloginschema.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="2000" height="5380" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-configprofile-nomadloginschema.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-configprofile-nomadloginschema.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/03/blog-configprofile-nomadloginschema.png 1600w, https://blog.grayw.co.uk/content/images/2022/03/blog-configprofile-nomadloginschema.png 2200w" sizes="(min-width: 720px) 720px"></figure><p>OK, that&apos;s NoMAD dealt with. On to the final part of the puzzle.</p><h2 id="remove-auto-logon">Remove Auto Logon</h2><p>With all our applications installed, all we have left to do is tell the device to remove the auto logon entry. Otherwise, it&apos;s just going to keep signing in as our default user. This may actually be useful to you, but not for me, and not what we&apos;re trying to achieve here.</p><p>As you can see from the DEPN script segment at the start of this post, we&apos;re calling the &quot;labzt-remove-autologon&quot; policy trigger. So yes, we&apos;re creating another policy!</p><h3 id="policy-1">Policy</h3><p>Cast your mind back to part two of the series.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-2/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon</div><div class="kg-bookmark-description">In the second post of the series, we cover the creation of scripts, smart groups, policies, and extension attributes for the auto logon stage.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-1.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><p>We made use of the AutoLogin script from brunerd.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/brunerd/macAdminTools/blob/main/Jamf/scripts/setAutoLogin.jamf.sh"><div class="kg-bookmark-content"><div class="kg-bookmark-title">macAdminTools/setAutoLogin.jamf.sh at main &#xB7; brunerd/macAdminTools</div><div class="kg-bookmark-description">Tools for the MacAdmin. Contribute to brunerd/macAdminTools development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">brunerd</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/d4ed58fd55789edde8c92eec45050e328e8a754ccc957e1ee30cfd1988ae9699/brunerd/macAdminTools" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready"></div></a></figure><p>If we take a look at lines 15-17 of that script, we can see that by passing a blank value, it will disable auto login for us.</p><pre><code class="language-Bash">#JAMF script parameters are shifted +3
#provide a username, if blank will disable autologin
USERNAME=&quot;${4}&quot;</code></pre><p>We&apos;ve already got the script in place with the parameter options. This makes our policy pretty simple.</p><p><strong>Display Name:</strong> 20. Lab - Zero Touch - Onboarding - Remote Auto Logon<br><strong>Category: </strong>Lab - Onboarding - General<br><strong>Trigger:</strong> Custom: labzt-remove-autologon<br><strong>Execution Frequency:</strong> Once per Computer</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-policy-removeautolgon-general.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1223" height="784" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-policy-removeautolgon-general.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-policy-removeautolgon-general.png 1000w, https://blog.grayw.co.uk/content/images/2022/03/blog-policy-removeautolgon-general.png 1223w" sizes="(min-width: 720px) 720px"></figure><p>Next, we&apos;ll configure the Scripts option to run our <em>Lab - Zero Touch - Onboarding - Set-or-Remove Auto Logon</em> with blank values for Username and Password.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-policy-removeautolgon-scripts.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1230" height="358" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-policy-removeautolgon-scripts.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-policy-removeautolgon-scripts.png 1000w, https://blog.grayw.co.uk/content/images/2022/03/blog-policy-removeautolgon-scripts.png 1230w" sizes="(min-width: 720px) 720px"></figure><p>And finally, we&apos;ll configure the Restart Options to <em>Restart Immediately</em>.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-policy-removeautolgon-restartoptions.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1155" height="516" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-policy-removeautolgon-restartoptions.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-policy-removeautolgon-restartoptions.png 1000w, https://blog.grayw.co.uk/content/images/2022/03/blog-policy-removeautolgon-restartoptions.png 1155w" sizes="(min-width: 720px) 720px"></figure><p>As for scoping, we&apos;ll scope this to <em>Devices - Auto Logon Enabled</em> Smart Group. This will ensure that it can only run on devices that have fallen in to this Smart Group.</p><p>And that&apos;s it, we&apos;re done.</p><h2 id="summary">Summary</h2><p>Not much was really done in this post, but it was an important final step in getting the device in to a user ready state. With all of these steps together, we can now assign a device to our <strong>Lab - DEP MDM</strong> server in Apple School Manager, have it automatically assigned to the PreStage in Jamf. If we include some Inventory Preload information, we can tell it about asset tags, rooms, and all sorts (<em><strong>optional but heavily recommended</strong></em>).</p><p>Turn the device on, and watch it go! </p><p>We can now take a device from this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/03/iMac_BNIB.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="640" height="480" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/iMac_BNIB.png 600w, https://blog.grayw.co.uk/content/images/2022/03/iMac_BNIB.png 640w"><figcaption>Apple iMac - Brand New in Box</figcaption></figure><p>Through this:</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-lab-zero-touch-policies-overview.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1199" height="1410" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-lab-zero-touch-policies-overview.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-lab-zero-touch-policies-overview.png 1000w, https://blog.grayw.co.uk/content/images/2022/03/blog-lab-zero-touch-policies-overview.png 1199w" sizes="(min-width: 720px) 720px"></figure><p>Where all the person on the other end sees is this:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/03/fullscreen_setup.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1298" height="759" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/fullscreen_setup.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/fullscreen_setup.png 1000w, https://blog.grayw.co.uk/content/images/2022/03/fullscreen_setup.png 1298w" sizes="(min-width: 720px) 720px"><figcaption>Shameless steal from https://github.com/jamf/DEPNotify-Starter/blob/master/example-img/fullscreen_setup.png because I forgot to take a screenshot while it was running.</figcaption></figure><p>Which, after an automatic restart, leads us to:</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/03/blog-nomadlogin.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 5 - User Ready" loading="lazy" width="1623" height="995" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/03/blog-nomadlogin.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/03/blog-nomadlogin.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/03/blog-nomadlogin.png 1600w, https://blog.grayw.co.uk/content/images/2022/03/blog-nomadlogin.png 1623w" sizes="(min-width: 720px) 720px"></figure><p>A fully automated Zero Touch Lab iMac Deployment with Apple School Manager and Jamf (and a boat load of policies, scripts, packages, and if you&apos;re in to it, prayer).</p><p>We are successfully deploying machines like this, across multiple sites. In a live environment, not just test.</p><h2 id="closing">Closing</h2><p>I truly hope that you have found this series useful, and that it has given you some ideas for your own environment. I&apos;d really love to hear from you about what you&apos;re doing, or how I can improve my process, too.</p><p>You can find me in a few places online.</p><ol><li><a href="https://fosstodon.org/@gray">Mastodon</a></li><li>MacAdmins Slack (#uk / #lab-environments / and more &#x2026;)</li><li>Matrix <a href="https://matrix.to/#/@grayw:opensuse.org">@grayw:opensuse.org</a></li></ol><p>Yep, I think that will do it.</p><p>And again, thank you so much for following along and reading. Don&apos;t forget to check back on Thursday (or use RSS ;)) for the full summary post.</p><hr><p>The post Photo for this post series was by <a href="https://unsplash.com/@quaritsch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Quaritsch Photography</a> on <a href="https://unsplash.com/s/photos/imac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Fully Automated Lab iMac Deployment with Jamf Pro & ADE: Part 4 - Deploying our Applications]]></title><description><![CDATA[We make use of Inventory Preload, Smart Groups, and Policies to deploy our applications with DEPNotify.]]></description><link>https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-4-deploying-our-applications/</link><guid isPermaLink="false">62014bceb70baa41e183d755</guid><category><![CDATA[apple]]></category><category><![CDATA[macos]]></category><category><![CDATA[Jamf Deployment Series]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Thu, 24 Feb 2022 16:30:00 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-4.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-4.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications"><p>Welcome to the fourth post in this blog series. Much like the opening&apos;s for the previous posts, I&apos;d suggest you read the others first if you haven&apos;t already, as we&apos;re building towards something. You can read them here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/tag/jamf-deployment-series/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Jamf Deployment Series - Tech Talk &amp; Mind Dumps</div><div class="kg-bookmark-description">Over the course of this blog post series, we will cover the whole process of automating the setup of iMac&#x2019;s in a multi-user lab environment.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-2.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications"></div></a></figure><p>For this post, we&apos;re going to finish building out the rest of the application policies we need for deployment. Then, create some more smart groups, and make use of a feature in Jamf Pro that very rarely gets mentioned, but quite possibly one of my favourite. Let&apos;s start with that!</p><h2 id="inventory-preload">Inventory Preload</h2><p>This is a feature of Jamf Pro that I very rarely see get a mention, possibly because it&apos;s just not that fun. But, used right, in combination with Smart Groups and Extension Attributes, you can really cut down on your work here.</p><p>You can read more about it here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/10.36.0/jamf-pro/documentation/Inventory_Preload.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Inventory Preload - Jamf Pro Documentation | Jamf</div><div class="kg-bookmark-description">The Inventory Preload setting allows you to upload computer and mobile device inventory data before devices are enrolled. The preloaded data will be applied to computers and mobile devices when ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/10.36.0/jamf-pro/documentation/images/Icon_Pro_Settings.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications"></div></a></figure><p>But, quite simply, it allows us to pre-populate Jamf with information about a device before it&apos;s enrolled (and after, too!).</p><p>When deploying lab style environments, you&apos;re very likely deploying to different rooms, buildings, or clusters that require different things. Building up different onboarding workflows for all of these would be a nightmare. We want to automate things as much as possible! So, that&apos;s what we&apos;re going to do. For my environment, I make heavy usage of the Room, and custom Usage Type attributes.</p><p>If we had a new order of 100 devices coming in, destined for 3 different rooms, would you want to manually assign that room information AFTER enrolment along with the usage type? Of course not. That would also completely break our fully automated workflow!</p><p>Instead, we&apos;ll make use of Inventory Preload by creating a CSV file with the following information:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Serial Number</th>
<th>Device Type</th>
<th>Asset Tag</th>
<th>Room</th>
<th>EA Usage Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>12345678</td>
<td>Computer</td>
<td>BLOG01</td>
<td>Room 01</td>
<td>Lab</td>
</tr>
<tr>
<td>12345687</td>
<td>Computer</td>
<td>BLOG02</td>
<td>Room 02</td>
<td>Lab</td>
</tr>
<tr>
<td>12345876</td>
<td>Computer</td>
<td>BLOG03</td>
<td>Room 03</td>
<td>Lab</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>We&apos;re now telling Jamf, as soon as you see that serial number, I want you to assign the Asset Tag, Room, and (Extension Attribute) Usage Type to that device. Once this is assigned, it will also automatically update our Smart Groups.</p><p>If you don&apos;t use asset tags, you can leave that out, but you can instantly see how powerful this simple tool can be. For a full list of default values, take a look at the documentation, but extension attributes are fully useable by prefixing the column with EA and then the name.</p><p>With this data available, we can now make use of some Smart Groups that are based on Rooms to deploy different applications, printers, or other settings as part of the whole onboarding process. </p><p>As I said, it&apos;s not very fun, but I love it. Especially as it gives us the ability to make bulk changes to existing devices, too. Want to move a batch of devices to another room? Easy, just create a CSV with their serial number and add the information you want to change. They&apos;ll see these changes on their next check-in.</p><h2 id="smart-groups">Smart Groups</h2><p>Let&apos;s build out some more Smart Groups based on the information provided in our Inventory Preload.</p><p>In our preload example, we told Jamf we&apos;re adding devices to Room 01 - 03. Let&apos;s create Smart Groups with the correct criteria.</p><ul><li><strong>Display Name</strong>: <em>Blog - Room 01</em></li><li><strong>Criteria</strong>: <em>Room</em> / <strong>Operator</strong>: <em>is</em> / <strong>Value</strong>: <em>Room 01</em></li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/02/CE800DC9-D7A3-4FD8-A28E-6367223C7E27.jpeg" width="2000" height="664" loading="lazy" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/CE800DC9-D7A3-4FD8-A28E-6367223C7E27.jpeg 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/CE800DC9-D7A3-4FD8-A28E-6367223C7E27.jpeg 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/02/CE800DC9-D7A3-4FD8-A28E-6367223C7E27.jpeg 1600w, https://blog.grayw.co.uk/content/images/2022/02/CE800DC9-D7A3-4FD8-A28E-6367223C7E27.jpeg 2188w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/02/7538811C-1DAC-4215-9F22-717A9489BC1D.jpeg" width="2000" height="583" loading="lazy" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/7538811C-1DAC-4215-9F22-717A9489BC1D.jpeg 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/7538811C-1DAC-4215-9F22-717A9489BC1D.jpeg 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/02/7538811C-1DAC-4215-9F22-717A9489BC1D.jpeg 1600w, https://blog.grayw.co.uk/content/images/2022/02/7538811C-1DAC-4215-9F22-717A9489BC1D.jpeg 2178w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Jamf Pro - New Smart Computer Group</figcaption></figure><p>We&apos;d repeat that for the other rooms. Now, as soon as our first device completes enrolment, Jamf will update the computer record with &quot;Room 01&quot;, and it will be added to our smart group, and so on.</p><p>When it comes to Extension Attributes and Smart Groups, let your imagination run wild! But don&apos;t go too crazy, otherwise you could end up with policy scope spaghetti.</p><h2 id="application-policies">Application Policies</h2><p>Jamf is preloaded with our data, we&apos;ve got Smart Groups for our rooms. All we need to do now is finish building out our policies for the applications / settings required for each of these locations and scope them to the relevant smart groups.</p><h3 id="general-deployment">General Deployment</h3><p>Our General Deployment will cover the basics that all our Lab machines are going to need. We&apos;ll scope these policies to Lab - Auto Advance. Scoping them this way ensures that any device coming through this PreStage will be eligible for them. I feel that this is safe to do, as they&apos;re all configured to run using a custom trigger. The only time they&apos;re going to run is if we tell them to.</p><h4 id="system">System</h4><ul><li>Install Rosetta 2 (Scoped to Smart Group if device has Apple Silicon)</li><li>Set Device Time Zone and NTP Servers</li><li>Set Device Name (BLOG-%SERIALNUMBER%)</li></ul><h4 id="browsers">Browsers</h4><ul><li>Microsoft Edge</li><li>Mozilla Firefox</li><li>Google Chrome</li></ul><h4 id="office-applications">Office Applications</h4><ul><li>Microsoft Office 365 (Inc. Teams)</li><li>Remove Outlook for Lab Machines </li><li>Keka</li></ul><h4 id="media-applications">Media Applications</h4><ul><li>VLC</li><li>Audacity</li><li>Handbrake</li></ul><h4 id="adobe">Adobe</h4><ul><li>Cache the installer for Adobe Creative Cloud 2022</li><li>Install Adobe Creative Cloud 2022</li></ul><p>There&apos;s a reason for the caching. I&apos;ve found that if you cache the installation files, you get far fewer failures than you would if you leave it to it. It can also cut hours off the installation time. </p><h3 id="specific-room-requirements">Specific Room Requirements</h3><p>With all the general requirements have met. You can start looking at any individual room needs. Ensuring that anything here is scoped to the relevant Room 0x Smart Group.</p><h4 id="room-01">Room 01</h4><ul><li>Room 01 Printer</li><li>Scanner Drivers</li><li>Extra Specialist Software</li></ul><h4 id="room-02">Room 02</h4><ul><li>Room 02 Printer</li><li>Extra Specialist Software</li></ul><p>And so on. As the device moves through the script, it will query Jamf to ask if there&apos;s any other relevant policies that are outstanding. So these will need to be configured to run on recurring check-in. Just make sure you&apos;re only running them once per computer (or whatever that policy requires).</p><h2 id="combining-with-previous-stages">Combining with Previous Stages</h2><p>We&apos;ve built up quite a number of moving parts if you look back through the posts, and it&apos;s easy to get lost in this forest. So let&apos;s take a step back and look at this in order.</p><ul><li>Ordered devices, which are automatically added to Apple School Manager by supplier</li><li>Assigned the devices to our MDM &quot;server&quot; of Lab-DEP</li><li>ASM syncs with Jamf and assigns them to the matching Lab - DEP setting under Automated Device Enrolment</li><li>The Lab - Auto Advance PreStage automatically scopes and assigns them to itself based on the settings we configured</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/blog-prestage-settings-reminder.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications" loading="lazy" width="1638" height="298" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/blog-prestage-settings-reminder.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/blog-prestage-settings-reminder.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/02/blog-prestage-settings-reminder.png 1600w, https://blog.grayw.co.uk/content/images/2022/02/blog-prestage-settings-reminder.png 1638w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - PreStage Settings</figcaption></figure><ul><li>We preload Jamf with Inventory Preload to give it more information on each of the devices</li><li>Smart Groups are in place to automatically update based on the device information provided in the preload</li><li>There are policies scoped to a Smart Group based on the PreStage that will tell the device to automatically log in with our local admin account created during PreStage</li><li>We then scope further policies to another Smart Group based on whether a device has automatic login enabled</li><li>Power on the device, and it makes its way through setup using Auto Advance</li><li>Once one of these devices logs in, the policy will trigger and install DEPNotify, and then kick off the script we configured to install our policies using a full screen style feedback</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/depn-default-screen.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications" loading="lazy" width="768" height="518" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/depn-default-screen.png 600w, https://blog.grayw.co.uk/content/images/2022/02/depn-default-screen.png 768w" sizes="(min-width: 720px) 720px"><figcaption>DEPNotify Screenshot</figcaption></figure><ul><li>By adding some Jamf Recon calls within this script at certain points, we ensure that devices are keeping Jamf up to date with information as it goes</li><li>Once the general policies complete, we have further policies scoped to specific rooms that we can call on towards the end of the script with a Jamf Policy check</li></ul><p>Here&apos;s a GitHub Gist of what this script could look like following our path and examples:</p><!--kg-card-begin: html--><script src="https://gist.github.com/GrahamWilliams-DMT/4534a0088dc32a5c42463c54e8546997.js"></script><!--kg-card-end: html--><p>As you can see, we&apos;re working through our application policies, installing all the general software. Once they are done, we make a final recon call back to Jamf to update the inventory, and ensure any smart groups are up-to-date. Once that&apos;s done, we run a policy check to see if there are any other policies remaining. In our case, this is where the different room policies would run based on the smart groups. So, any in the &quot;Blog - Room 01&quot; group would run the policies scoped to those, etc.</p><p>On line 62, you&apos;ll notice that I&apos;m writing out an &quot;<strong>onboardingComplete&quot;</strong> string to a custom plist file. And then, lines 67 - 70 we install NoMAD and remove the auto login we&apos;ve configured.</p><p>We&apos;ll cover these last bits in the next post, where we concentrate on getting the device &quot;User Ready&quot;.</p><h2 id="summary">Summary</h2><p>This post covers preloading information in to Jamf, allowing us to sort our machines in rooms, buildings, custom groups, and many other ways. </p><p>Creating Smart Groups to make use of this information before Jamf even sees the device.</p><p>Categorising our application policies, and adding them to the script.</p><p>And finally, putting all those steps together makes you realise how much work goes in to something like this. </p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/beaduck.jpg" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 4 - Deploying our Applications" loading="lazy" width="500" height="281"></figure><p>From an external appearance, it seems as though things are swimming along easily. Underneath, there are things swishing left, right, up, down, and possibly in some directions that may not even be considered directions. But, that&apos;s why we have jobs. Make the seemingly impossible requests look easy.</p><p>We&apos;re almost done with this series of posts, and I hope you&apos;ve enjoyed them and that they&apos;ve been useful. The next post will cover the last few pieces of the puzzle in getting the device in to a true user ready state. </p><p>And then, the finale. The summary post, where we&apos;ll step back and marvel at the monstrosity that has been born. I&apos;ll also be creating a high-level flow chart style overview on how this whole thing fits together.</p><hr><p>The post Photo for this post series was by <a href="https://unsplash.com/@quaritsch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Quaritsch Photography</a> on <a href="https://unsplash.com/s/photos/imac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Fully Automated Lab iMac Deployment with Jamf Pro & ADE: Part 3 - Preparing for Application Deployment]]></title><description><![CDATA[With most of the basic policies in place, we start creating the foundations for deploying applications using DEPNotify.]]></description><link>https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-3-preparing-for-application-deployment/</link><guid isPermaLink="false">62014b9cb70baa41e183d751</guid><category><![CDATA[apple]]></category><category><![CDATA[Jamf Deployment Series]]></category><category><![CDATA[macos]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Tue, 22 Feb 2022 10:00:00 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-3.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-3.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><p></p><p>Hello, and welcome to post three in this blog post series of deploying iMac&apos;s in a Lab environment. If you haven&apos;t already, I&apos;d urge you to seriously consider going back over the first two posts, which lays the groundwork for getting here. Without it, things may not go to plan. Go on, off you trot.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/tag/jamf-deployment-series/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Jamf Deployment Series - Tech Talk &amp; Mind Dumps</div><div class="kg-bookmark-description">Over the course of this blog post series, we will cover the whole process of automating the setup of iMac&#x2019;s in a multi-user lab environment.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-2.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"></div></a></figure><p>Back?</p><p>Today, We&apos;re going to concentrate on preparing our environment for deploying the applications we need. There are a few ways we could do this. But, as this is all about full automation, we&apos;re going big, and with style!</p><p>DEPNotify has been part of my Staff on-boarding workflow for quite a while. It&apos;s a pretty &quot;simple&quot; method, and mostly used as feedback for the user to see what&apos;s happening. Although, you can do some incredibly cool stuff with it. So, where the hell do I start?</p><hr><h2 id="depnotify-package">DEPNotify Package</h2><p>Let&apos;s begin by getting the DEPNotify application, and making the package available on our Distribution Points. Grab the latest version from the GitLab repo and add it to your relevant DP&apos;s:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://gitlab.com/Mactroll/DEPNotify/-/releases"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Releases &#xB7; Joel Rennich / DEPNotify</div><div class="kg-bookmark-description">GitLab.com</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://gitlab.com/assets/touch-icon-ipad-retina-8ebe416f5313483d9c1bc772b5bbe03ecad52a54eba443e5215a22caed2a16a2.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><span class="kg-bookmark-author">GitLab</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://gitlab.com/assets/gitlab_logo-7ae504fe4f68fdebb3c2034e36621930cd36ea87924c11ff65dbcb8ed50dca58.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"></div></a></figure><p></p><h2 id="depnotify-script">DEPNotify Script</h2><p>Next, we&apos;re going to add the script required to bring all of this together in the background. Or, as Chad from Rocketman Tech infers - making the magic happen.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.rocketman.tech/post/creating-magic-with-endpoint-provisioning-part-1-the-ta-da-of-depnotify"><div class="kg-bookmark-content"><div class="kg-bookmark-title">CREATING MAGIC WITH ENDPOINT PROVISIONING PART 1 &#x2013; THE &#x201C;TA DA!&#x201D; OF DEPNOTIFY</div><div class="kg-bookmark-description">&#x201C;Sometimes magic is just someone spending more time on something than anyone else might reasonably expect.&#x201D;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;~Teller Magicians spend a lot of time perfecting skills that if, done right, can&#x2019;t be seen by their audiences. I&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://static.wixstatic.com/media/afb580_09a59d0aed3344099dcdd54c1ccf8233~mv2.png/v1/fill/w_32%2Ch_32%2Clg_1%2Cusm_0.66_1.00_0.01/afb580_09a59d0aed3344099dcdd54c1ccf8233~mv2.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><span class="kg-bookmark-author">Rocketman</span><span class="kg-bookmark-publisher">Chad Lawson</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://static.wixstatic.com/media/cceff6_eb36d31643d0409b80aad0df7c6832a5~mv2.png/v1/fit/w_812%2Ch_562%2Cal_c/file.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"></div></a></figure><p>Previously, I&apos;ve always used the DEPNotify-Starter resources from Jamf&apos;s GitHub repo. But this time, I&apos;m going to make use of the script by Rocketman:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/Rocketman-Tech/Onboarding-With-DEPNotify"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - Rocketman-Tech/Onboarding-With-DEPNotify: Create an end user-based provisioning workflow for Jamf deployments with a DEPNotify interface.</div><div class="kg-bookmark-description">Create an end user-based provisioning workflow for Jamf deployments with a DEPNotify interface. - GitHub - Rocketman-Tech/Onboarding-With-DEPNotify: Create an end user-based provisioning workflow f...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">Rocketman-Tech</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/179b293238577a167b6af9808be5e2f58c5a3461947c93d6e0e400866956624b/Rocketman-Tech/Onboarding-With-DEPNotify" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"></div></a></figure><p>Just like before, we&apos;ll add the script to Jamf, and configure the options to make it easier in the future. In 6 months, are you going to remember what&apos;s supposed to go in &quot;Parameter 5&quot;? If so, kudos, I&apos;m lucky if I can remember week to week.</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-script-depn-general.png" width="1590" height="550" loading="lazy" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-script-depn-general.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-script-depn-general.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-script-depn-general.png 1590w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-script-depn-options.png" width="1595" height="573" loading="lazy" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-script-depn-options.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-script-depn-options.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-script-depn-options.png 1595w" sizes="(min-width: 720px) 720px"></div></div></div></figure><p>In its current state, the script isn&apos;t really very helpful for us. We need to tell it what we want it to do. In this case, we want to install specific pieces of software. To do that, we&apos;re going to need policies for it to call on. I&apos;ve got one or two that are needed &#x2026;</p><p>But, let&apos;s go through a few. In keeping with the example workflow within the DEPNotify script, we&apos;ll start with some Machine Configuration.</p><p>To keep the test times short, I&apos;m going to use two of my smaller scripts. The first is to set the device name to our standards, and the second to configure the time zone and NTP settings on the device. In the DEPNotify script, that would look like this:</p><pre><code class="language-Bash">## EXAMPLE WORKFLOW
##
# ## Machine Configuration
DEPNotify &quot;Command: MainText: Configuring System Settings&quot;
DEPNotify &quot;Status: Setting Computer Name&quot;
jamfCommand labzt-onboarding-setdevicename
DEPNotify &quot;Status: Setting Computer Time Zone to UK&quot;
jamfCommand labzt-onboarding-settimezonentp</code></pre><p>But what about something a bit more substantial. Well, as we begin to build out what we need to deploy, we keep adding to the script:</p><pre><code class="language-Bash">## EXAMPLE WORKFLOW
##
# ## Machine Configuration
DEPNotify &quot;Command: MainText: Configuring System Settings&quot;
DEPNotify &quot;Status: Setting Computer Name&quot;
jamfCommand labzt-onboarding-setdevicename
DEPNotify &quot;Status: Setting Computer Time Zone to UK&quot;
jamfCommand labzt-onboarding-settimezonentp
#
# ## Browsers
DEPNotify &quot;Status: Installing Microsoft Edge&quot;
DEPNotify &quot;Command: MainText: Starting software deployment.\n\nThis process can take up to 3-4 hours to complete.&quot;
jamfCommand labzt-onboarding-edge
sleep 3
#
# ## Office Applications
DEPNotify &quot;Status: Installing Microsoft Office 365&quot;
jamfCommand labzt-onboarding-office365
sleep 3
#</code></pre><p>We just keep building it up until we&apos;ve got everything we require. While we&apos;re testing, let&apos;s keep it to these few simple jobs. We don&apos;t want to be waiting for hours on each test.</p><p>Now we have the script performing a few actions, we need to actually make use of it. Policy time!</p><h2 id="onboarding-policy">Onboarding Policy</h2><p>Let&apos;s create the policy to actually make use of this script. Again, this section is going to be image heavy, as it&apos;s much easier to convey all the things we&apos;re doing.</p><p>First, we&apos;ll configure the General tab. We&apos;re starting the policy name with number &quot;10&quot;. Policies run in an alphanumerical order. </p><hr><h2 id="policy-naming-tangent">Policy Naming Tangent</h2><p>You can read a discussion on this on the Jamf Nation Community forum:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://community.jamf.com/t5/jamf-pro/policy-execution-order/m-p/192182"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Policy Execution Order</div><div class="kg-bookmark-description">Hello everyone, hope you all are well. I would like to get policies to execute in order. I would like one policy to run last no matter what. I have been working on this huge project with DEP Notify. I made a post about it a little while ago. Do you guys know how to get policies to run in a specific &#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://community.jamf.com/html/@341C36E148083396DBCB6E6A9C18E572/assets/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><span class="kg-bookmark-author">Jamf Nation</span><span class="kg-bookmark-publisher">kadams Contributor</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://community.jamf.com/legacyfs/online/avatars/e0c176bbb6b648d5834db7a33146d8e6.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"></div></a></figure><p>It&apos;s also covered here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.rocketman.tech/post/creating-magic-with-endpoint-provisioning-part-3-the-policies"><div class="kg-bookmark-content"><div class="kg-bookmark-title">CREATING MAGIC WITH ENDPOINT PROVISIONING PART 3 &#x2013; THE POLICIES</div><div class="kg-bookmark-description">&#x201C;Any sufficiently advanced technology is indistinguishable from magic.&#x201D;&#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x200E;&#x200F;&#x200F;&#x200E; &#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://static.wixstatic.com/media/afb580_09a59d0aed3344099dcdd54c1ccf8233~mv2.png/v1/fill/w_32%2Ch_32%2Clg_1%2Cusm_0.66_1.00_0.01/afb580_09a59d0aed3344099dcdd54c1ccf8233~mv2.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"><span class="kg-bookmark-author">Rocketman</span><span class="kg-bookmark-publisher">Chad Lawson</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://static.wixstatic.com/media/afb580_0363bcba1e6146059b66404bd8afb768~mv2.jpeg/v1/fit/w_640%2Ch_360%2Cal_c%2Cq_80/file.jpeg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment"></div></a></figure><hr><h2 id="back-to-the-policy">Back to the Policy</h2><p>Anyway, back to it.</p><p>We want this policy to run at login (see where we&apos;re going with this and the auto login?). We&apos;re also going to have this run at a recurring check-in, just in case it fails to run immediately. There&apos;s also a custom event trigger. You never know. One of the joys of deploying Apple devices is the unpredictability!</p><p>We only want this to run once per computer, especially with recurring check-in selected. Don&apos;t want to be attempting to install all the software every check-in!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-001.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment" loading="lazy" width="1446" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/blog-jds-part3-001.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/blog-jds-part3-001.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-001.png 1446w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Create Computer Policy - General Tab</figcaption></figure><p>So, what is it that we want this policy to actually do? First, we&apos;ll need to have it install the DEPNotify package for us.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-002.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment" loading="lazy" width="1461" height="358" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/blog-jds-part3-002.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/blog-jds-part3-002.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-002.png 1461w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Computer Policy - Package Tab</figcaption></figure><p>Then, we want it to run the script to actually do something with DEPNotify. We need to set the priority to <strong>After</strong>. This will make sure the script only runs once the package installs. If we try to run it <strong>Before</strong>, it&apos;s not going to do anything as DEPNotify won&apos;t be there. </p><p>We&apos;re also going to pass the <strong>-fullScreen</strong> option, as we don&apos;t want users walking up to the devices and using them while we&apos;re trying to deploy software. We&apos;ll also set the title.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-003.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment" loading="lazy" width="1466" height="497" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/blog-jds-part3-003.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/blog-jds-part3-003.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-003.png 1466w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Policy Script Tab</figcaption></figure><p>Finally, we want to scope this to the Smart Group we created in the previous post (<strong>Devices - Auto Logon Enabled</strong>) that contains devices that have auto logon enabled. This ensures that we&apos;re only targeting devices that are automatically logging on. We do not want this deploying to loads of machines when a user logs in by mistake.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-004.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 3 - Preparing for Application Deployment" loading="lazy" width="1944" height="426" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/blog-jds-part3-004.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/blog-jds-part3-004.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/02/blog-jds-part3-004.png 1600w, https://blog.grayw.co.uk/content/images/2022/02/blog-jds-part3-004.png 1944w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Policy Scope Tab</figcaption></figure><hr><h2 id="summary">Summary</h2><p>While I could probably add more to this post, I think this would be a suitable break point. The next part would make this feel too long.</p><p>What did we do in this post?</p><ul><li>We&apos;ve downloaded DEPNotify and added it to our Distribution Points</li><li>Added the script that will deploy our software</li><li>Made changes to the script to actually do something</li><li>Created a new policy that will install DEPNotify, and run the DEPNotify script to install the software. This will happen at logon, or, at the next convenient check-in, if it&apos;s unable to run immediately.</li></ul><p>If you were to run a machine through this process right now as it is, it would still give great results. You could assign it to your Lab - DEP MDM server in ASM, plug it in, and have it at the desktop with Office and Edge installed without having to put your hands on it.</p><p>In Part 4, we&apos;re going to go through the rest of our policies, and really build it out to do everything we need it to do. Along with some additional tips on really making the most of Smart Groups and another feature within Jamf Pro ;)!</p><p>Part 5, we&apos;ll complete the last few additions to finish it all off, making the device user ready.</p><p>And finally, Part 6, we&apos;ll go through a bit of a summary of everything we&apos;ve done and look at how the whole process hangs together from a high level overview. If I receive any feedback, I&apos;ll also probably go through that here.</p><hr><p>The post Photo for this post series was by <a href="https://unsplash.com/@quaritsch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Quaritsch Photography</a> on <a href="https://unsplash.com/s/photos/imac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Fully Automated Lab iMac Deployment with Jamf Pro & ADE: Part 2 - PreStage to Auto Logon]]></title><description><![CDATA[In the second post of the series, we cover the creation of scripts, smart groups, policies, and extension attributes for the auto logon stage.]]></description><link>https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-ade-part-2/</link><guid isPermaLink="false">620122b4b70baa41e183d612</guid><category><![CDATA[jamf]]></category><category><![CDATA[apple]]></category><category><![CDATA[Jamf Deployment Series]]></category><category><![CDATA[macos]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Thu, 17 Feb 2022 12:00:00 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-1.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash-1.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><p><br>Hello and welcome to the second post in this series. If you haven&apos;t already read the first, I&apos;d suggested you go back and read it. It&apos;s quite important, as it covers some prerequisites for getting this workflow up and running. Skipping parts may result in something not working. Go on, we&apos;ll wait for you: </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-dep-part-1/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage</div><div class="kg-bookmark-description">In this first post of the series, we cover Apple School Manager, Jamf ADE, and the initial PreStage settings.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://blog.grayw.co.uk/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><span class="kg-bookmark-author">Tech Talk &amp; Mind Dumps</span><span class="kg-bookmark-publisher">Gray</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"></div></a></figure><p>In this post, I was going to cover the creation of all the scripts and smart groups. However, by the time I&apos;d finished just one part, it felt like enough. So, this post will cover the creation of the scripts, smart groups, and extension attributes required to take us to the auto logon stage of our setup.</p><p>Enjoy!</p><hr><h2 id="auto-advance-prestage-smart-group">Auto Advance PreStage Smart Group</h2><p>If you&apos;re new to Jamf, you may not fully appreciate the usefulness of Smart Groups &#x2026; yet! They can make life so much easier. For us, they&apos;re going to be incredibly helpful in ensuring we scope policies to exactly where we want them to go, and at the right time. If you haven&apos;t already, I&apos;d suggest you read up on Smart Groups:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/10.34.0/jamf-pro/documentation/Smart_Groups.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Smart Groups - Jamf Pro Documentation | Jamf</div><div class="kg-bookmark-description">Jamf Pro allows you to create smart groups for managed computers, mobile devices, or users. You can create smart groups based on one or more inventory attributes. To avoid issues, smart group criteria ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/10.34.0/jamf-pro/documentation/images/Icon_Pro_Computers.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"></div></a></figure><p>Anyway, the first Smart Group we&apos;re going to make use of is one that contains devices that are enrolled via the <strong>Lab - Auto Advance</strong> PreStage method.</p><p>An image speaks a thousand words, so we&apos;re going to be making heavy use of them in this post!</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-labauto-general.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1591" height="372" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-smartgroup-labauto-general.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-smartgroup-labauto-general.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-labauto-general.png 1591w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Smart Group Creation Settings</figcaption></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-labauto-criteria.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1589" height="318" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-smartgroup-labauto-criteria.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-smartgroup-labauto-criteria.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-labauto-criteria.png 1589w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Smart Group Creation Criteria</figcaption></figure><p>Any devices that come through that PreStage will now be automatically added to this Smart Group. OK, great. Now, we&apos;re going to want to do something with this group, otherwise what&apos;s the point.</p><hr><h2 id="auto-logon-script">Auto Logon Script</h2><p>One of the major issues I&apos;ve been facing up to this stage has been giving the local technician decent feedback about how the software installation has been going. So far, I&apos;ve just been running policies in the background while the device has been seemingly idle at the log on window. This is not overly helpful for them unless they&apos;re checking the policy history. I wanted more, and it&apos;s taken a few months to get to this stage. But the answer was &quot;simple&quot;.</p><p>We&apos;re going to have the device automatically sign in as our local admin user that was created as part of our PreStage settings. Unfortunately, this doesn&apos;t seem to be an option in Jamf Pro right now, which I think is incredibly short-sighted. You&apos;re allowed to skip account creation, but then not sign in &#x2026; Might be worthy of a feature request.</p><p>To achieve our goal, we&apos;re going to make use of this:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/brunerd/macAdminTools/blob/main/Jamf/scripts/setAutoLogin.jamf.sh"><div class="kg-bookmark-content"><div class="kg-bookmark-title">macAdminTools/setAutoLogin.jamf.sh at main &#xB7; brunerd/macAdminTools</div><div class="kg-bookmark-description">Tools for the MacAdmin. Contribute to brunerd/macAdminTools development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">brunerd</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/d4ed58fd55789edde8c92eec45050e328e8a754ccc957e1ee30cfd1988ae9699/brunerd/macAdminTools" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"></div></a></figure><p>Which I discovered over on brunderd&apos;s blog here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.brunerd.com/blog/2021/08/24/automating-automatic-login-for-macos/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Automating automatic login for macOS &#x2013; brunerd</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><span class="kg-bookmark-author">brunerd</span></div></div></a></figure><p>From here, we&apos;re going to need to add this script to our Jamf Pro instance, and configure the options (makes it easier in future if you name these now). After that, we&apos;re going to create a policy to actually do something with the script, and scope it to our <strong>Devices - Lab - Auto Advance</strong> smart group that we created earlier.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-scripts-setautologon-script.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1582" height="878" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-scripts-setautologon-script.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-scripts-setautologon-script.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-scripts-setautologon-script.png 1582w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Adding the script</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-scripts-setautologon-options.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1570" height="377" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-scripts-setautologon-options.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-scripts-setautologon-options.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-scripts-setautologon-options.png 1570w" sizes="(min-width: 720px) 720px"><figcaption>Jamf Pro - Adding the script parameter labels</figcaption></figure><p>So here, we&apos;ve added the script to Jamf, and then updated the parameter labels to Username and Password. This will make it much easier to remember what does what when it comes to creating the policy.</p><h2 id="auto-logon-policy">Auto Logon Policy</h2><p>Next, we&apos;ll create a new computer policy. I&apos;ve prefixed this with a &quot;00.&quot; in an attempt to help ensure that it is the very first thing that runs when it tells Jamf that the enrolment is complete.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-policy-autologon-general-1.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1592" height="798" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-policy-autologon-general-1.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-policy-autologon-general-1.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-policy-autologon-general-1.png 1592w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Pro - Auto Logon Script Policy General Tab</figcaption></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-policy-autologon-script.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1594" height="499" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-policy-autologon-script.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-policy-autologon-script.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-policy-autologon-script.png 1594w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Pro - Auto Logon Script Policy Script Tab</figcaption></figure><p>Above, you can see where those parameter labels come in to play. </p><p>Below, we&apos;ve configured the device to automatically restart as soon as the script completes. This is important, as we&apos;ve just configured the device to automatically login as this user on startup, not when the script runs.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-policy-autologon-restart.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1587" height="588" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-policy-autologon-restart.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-policy-autologon-restart.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-policy-autologon-restart.png 1587w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Pro - Auto Logon Script Policy Restart Tab</figcaption></figure><p>With those two parts done, we now have a policy that will run at enrolment complete for any device that comes through that PreStage. It will run the script to enable the auto logon at startup. Now we need to make use of this new-found freedom.</p><h2 id="auto-logon-extension-attribute">Auto Logon Extension Attribute</h2><p>Next, we&apos;re going to want to know when a device has auto logon enabled. Without knowing this, we can&apos;t really act on it reliably. I think the easiest option here is to make use of Extension Attributes:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/10.34.0/jamf-pro/documentation/Computer_Extension_Attributes.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Computer Extension Attributes - Jamf Pro Documentation | Jamf</div><div class="kg-bookmark-description">Extension attributes allow you to collect extra inventory information. Extension attribute values are populated using an input type, which can be any of the following: Text field Pop-up menu Script ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/10.34.0/jamf-pro/documentation/images/Icon_Pro_Settings.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"></div></a></figure><p>We&apos;re going to create an extension attribute that runs a script on the device to see if auto logon in enabled, then return a status to Jamf. Once that information is back, we can act on it.</p><p>We can achieve this by making use of the incredibly helpful answer from <strong>thoule</strong> in the Jamf Community:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://community.jamf.com/t5/jamf-pro/smart-group-based-on-policy/m-p/125555/highlight/true#M114672"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Re: Smart Group based on policy</div><div class="kg-bookmark-description">Your smart group needs to identify the difference between a computer that has run your script and one that hasent. If nothing jumps out at you, then I tend to create a plist as part of my script defaults write /Library/Preferences/com.toddco.plist scriptWasRun -bool YES Then an extension attribute t&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://community.jamf.com/html/@341C36E148083396DBCB6E6A9C18E572/assets/favicon.ico" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><span class="kg-bookmark-author">Jamf Nation</span><span class="kg-bookmark-publisher">Sobchak Contributor</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://community.jamf.com/t5/image/serverpage/avatar-name/avatar-2/avatar-theme/candy/avatar-collection/Hermes_Default/avatar-display-size/profile/version/2?xdesc=1.0" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"></div></a></figure><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-extensionattribute-autologon.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1590" height="722" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-extensionattribute-autologon.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-extensionattribute-autologon.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-extensionattribute-autologon.png 1590w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Pro - Creating the extension attribute for checking Auto Logon</figcaption></figure><p>With a few tweaks, we can check for our entry on the login window to see if the result matches our local admin username, then return a result based on that. We knew it was writing out to this file on line 108 of <strong>brunerd&apos;s </strong>login script.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/brunerd/macAdminTools/blob/9146c633b16534b2436727faea6de8df9395f7a4/Jamf/scripts/setAutoLogin.jamf.sh#L108"><div class="kg-bookmark-content"><div class="kg-bookmark-title">macAdminTools/setAutoLogin.jamf.sh at 9146c633b16534b2436727faea6de8df9395f7a4 &#xB7; brunerd/macAdminTools</div><div class="kg-bookmark-description">Tools for the MacAdmin. Contribute to brunerd/macAdminTools development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">brunerd</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/d4ed58fd55789edde8c92eec45050e328e8a754ccc957e1ee30cfd1988ae9699/brunerd/macAdminTools" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon"></div></a></figure><p>If we check the Extension Attributes page of the device that we have deployed with this policy scoped to it, this looks to be true:</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-extensionattributes-record.jpg" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1185" height="86" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-extensionattributes-record.jpg 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-extensionattributes-record.jpg 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-extensionattributes-record.jpg 1185w" sizes="(min-width: 720px) 720px"></figure><p>On reflection, I probably could have shortened this to simply return &quot;Enabled&quot; or &quot;Disabled&quot;.</p><h2 id="auto-logon-status-smart-group">Auto Logon Status Smart Group</h2><p><em>Phew</em>, we&apos;ve come a long way already, but there&apos;s still so much left to do. Now that we&apos;ve got devices telling us if they have Auto Logon Enabled or not, we need to do something with that information.</p><p>I want to scope our next set of policies to run on devices that are sat there waiting on the desktop for us. It&apos;s important that this happens at the desktop due to the way DEPNotify displays things. This will be more obvious later. For now, let&apos;s create a Smart Group based on this extension attribute.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-autologonattribute-criteria.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1599" height="292" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-smartgroup-autologonattribute-criteria.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-smartgroup-autologonattribute-criteria.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-autologonattribute-criteria.png 1599w" sizes="(min-width: 1200px) 1200px"><figcaption>Jamf Pro - Creating a Smart Computer Group that checks for our Auto Logon Status extension attribute</figcaption></figure><p>Here&apos;s the Smart Group with the criteria, checking that the Auto Logon Status is Auto Logon Enabled. Any machine that is returning that information to Jamf will now automatically be made a member of this Smart Group. If we check that, we can indeed see that the device that is waiting at the desktop is indeed a member:</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-autologon-results.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 2 - PreStage to Auto Logon" loading="lazy" width="1584" height="229" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-smartgroup-autologon-results.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-smartgroup-autologon-results.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-smartgroup-autologon-results.png 1584w" sizes="(min-width: 720px) 720px"></figure><hr><h2 id="summary">Summary</h2><p>We&apos;ve actually covered quite a lot of ground in this post, and I think that adding any more to it would actually be detrimental. Especially with my scattered way of thinking.</p><p>For this post, we&apos;ve created a PreStage Smart Group to make use of the PreStage setup from the previous post. Created a script and a policy to automatically log on our local administrator created during PreStage Enrolment. And, created an extension attribute that runs a small shell script to query a machine to see if auto login is enabled, adding it to a new Smart Group if that is true.</p><p><em>Phew! </em>If we add the previous achievements to this one, we&apos;ve actually done quite a lot:</p><ul><li>Configured Apple School Manager for devices to be automatically added by suppliers</li><li>Added MDM servers to Apple School Manager</li><li>Added Automated Device Enrolment settings to Jamf to line up with ASM</li><li>Created a PreStage Enrolment task that will automatically assign devices to it that are assigned to our <strong>Lab - DEP</strong> from ASM. When turned on, these devices will then make use of the Auto Advance feature in macOS Big Sur or later to automatically run through the setup screens. Jamf will create a local administrator account for us, and skip the rest of the account creation process. </li><li>Created a script that will automatically log our new local administrator account in.</li><li>Created a policy for that script that will run once enrolment is completed, and scoped that only to machines that come through our <strong>Lab - Auto Advance</strong> PreStage.</li><li>Created an Extension Attribute that will query the machine to see if our auto login is enabled and active.</li><li>Created a Smart Group that will automatically add devices to itself if that extension attribute comes back as our true statement.</li></ul><p>At this stage, we can now have a device taken out of the box, plugged in and automatically sat waiting at the desktop within about 5 minutes. All we need to do now is push software at it. Easy &#x2026; right? Oh Apple, you little tinker.</p><p>Again, if you have any feedback or suggestions on improving this process, I&apos;d love to hear from you. You can usually find me lurking in the MacAdmins Slack in various channels: macadmins.slack.com</p><p>This blog post series image Photo is by <a href="https://unsplash.com/@quaritsch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Quaritsch Photography</a> on <a href="https://unsplash.com/s/photos/imac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Fully Automated Lab iMac Deployment with Jamf Pro & ADE: Part 1 - ASM to PreStage]]></title><description><![CDATA[In this first post of the series, we cover Apple School Manager, Jamf ADE, and the initial PreStage settings.]]></description><link>https://blog.grayw.co.uk/fully-automated-lab-imac-deployment-with-jamf-pro-dep-part-1/</link><guid isPermaLink="false">6200dac0b70baa41e183d585</guid><category><![CDATA[apple]]></category><category><![CDATA[jamf]]></category><category><![CDATA[Jamf Deployment Series]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Tue, 15 Feb 2022 15:59:39 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/02/quaritsch-photography-m2zuB8DqwyM-unsplash.jpg" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"><p>Welcome to part one of my blog post series about deploying lab (multi-user) iMac&#x2019;s in a fully automated way, from box to &#x2026; bench? I&#x2019;m not sure how many posts this is going to be split down to yet.</p><p>I can&#x2019;t guarantee that this is the perfect way of doing it, but it&#x2019;s what I&#x2019;m doing right now. Not only that, but I&#x2019;d love to hear your feedback in areas where this could be improved. There are a lot of moving parts, so if you&#x2019;re following along, I&#x2019;d advise you wait for all parts to be published, go through it, and plan it out for your environment rather than just blindly following.</p><p>In this first post, I&#x2019;m going to detail some prerequisites and the setup required to make any of this possible.</p><p>I&#x2019;m mostly writing this for myself, because, if I ever have to try to put any of this back together again, I&#x2019;m going to need notes. This has been the culmination of a lot of learning, and many, many mistakes over the past few years that I&#x2019;ve been working in this team.</p><h2 id="pre-requisites">Pre-Requisites<br></h2><h3 id="apple">Apple</h3><p>The very first thing you&#x2019;re going to need in place is either Apple Business Manager, or Apple School Manager. This whole process relies upon Automated Device Enrolment (previously known as Device Enrolment Program (DEP)). Without it, this entire automated process falls at the starting gate.</p><p>Your devices will need to be running <strong>macOS Bug Sur or later</strong>.</p><h3 id="mdm-jamf-pro">MDM (Jamf Pro)</h3><p>The next thing you&#x2019;re going to need (at least if you&#x2019;re following along with me), is Jamf Pro. I&#x2019;ll be working from a self-hosted installation, but I see no reason as to why this wouldn&#x2019;t work with the Jamf Pro Cloud offering. If I get around to it, I may actually try it from the Beta Cloud service, too.</p><p>Within Jamf Pro, you&#x2019;re going to need to have at least one HTTPS distribution point configured to ensure the machines can download the required packages when prompted.</p><p>You can read through the Jamf documentation on setting these up here, so I won&apos;t cover these in-depth configs: </p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/10.34.0/jamf-pro/documentation/File_Share_Distribution_Points.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">File Share Distribution Points - Jamf Pro Documentation | Jamf</div><div class="kg-bookmark-description">A server with an AFP or SMB share can be used as a file share distribution point. Before you can use a file share distribution point with Jamf Pro , you must set up the distribution point and add it ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/10.34.0/jamf-pro/documentation/images/Icon_Pro_Settings.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"></div></a></figure><h3 id="network-infrastructure">Network Infrastructure</h3><p>And, of course, none of this will work without a properly configured network with DHCP, and a clear shot out to all the required Apple and Jamf endpoints.</p><p>Due to the myriad of network equipment and personal configurations out there, I&#x2019;m not going to cover this section at all within any of these posts.</p><p>You can find all the network requirements for Apple services within their documentation here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://support.apple.com/en-gb/HT210060"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Use Apple products on enterprise networks</div><div class="kg-bookmark-description">Find out which hosts and ports are required to use your Apple products on enterprise networks.</div><div class="kg-bookmark-metadata"><span class="kg-bookmark-author">Apple Support</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://support.apple.com/library/content/dam/edam/applecare/images/en_US/mac_apps/itunes/divider.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"></div></a></figure><p>And, similarly, you can find all the network requirements for Jamf Pro here:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/security/jamf-pro-security-overview/Network_Ports_Used_by_Jamf_Pro.html"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Network Ports Used by Jamf Pro - Jamf Pro Security Overview | Jamf</div><div class="kg-bookmark-description">The complete list of required ports depends on the specific services and features that are enabled in a particular environment. The following ports are required to support basic functionality in all ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/security/jamf-pro-security-overview/images/en_JamfLogo-ProductDocumentation-EN.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"></div></a></figure><p>I appreciate that not all Apple Admins have networking experience, so if this is the case, I suggest you discuss this with your relevant networking team to ensure that this will be possible for you.</p><p>Right, that&#x2019;s it. On with the rest of the show.</p><hr><h2 id="apple-businessschool-manager-setup">Apple Business/School Manager Setup</h2><p>As I&#x2019;ve already mentioned, one major part of this will be the configuration of your A(B/S)M account.</p><p><strong>Step 1: Ensure that your suppliers are correctly setup to add devices to your account.</strong></p><p>When you order a device from your supplier, you&#x2019;re going to want to make sure that they add it to your <strong>ASM</strong> account. I&#x2019;m going to refer to it as ASM from here, as that&#x2019;s what I&#x2019;m using - but I believe they are interchangeable.</p><p>To do this, you&#x2019;re going to need their Apple account details and add them to your approved suppliers.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/asm-reselllers.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="923" height="257" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/asm-reselllers.png 600w, https://blog.grayw.co.uk/content/images/2022/02/asm-reselllers.png 923w" sizes="(min-width: 720px) 720px"><figcaption>Screenshot of the resellers active on our Apple School Manager account (names and numbers redacted).</figcaption></figure><p>Once this is done, whenever you order your devices, they will be automatically assigned to your ASM account. Like so:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/asm-order-device-assigned.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="923" height="755" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/asm-order-device-assigned.png 600w, https://blog.grayw.co.uk/content/images/2022/02/asm-order-device-assigned.png 923w" sizes="(min-width: 720px) 720px"><figcaption>Screenshot of devices being assigned by a reseller (names and order information redacted).</figcaption></figure><p>Without this, your devices will not be available for ADE (DEP) style enrolment in to Jamf Pro, which will make it impossible for later steps.</p><p><strong>Step 2: Set up your MDM servers</strong></p><p>Next, you&#x2019;re going to want to make sure that add your Jamf Pro service as an MDM server.</p><p>During our original setup and mandatory kick-start induction, this was originally set up as just one MDM server, with all devices being automatically assigned to it. It wasn&#x2019;t until I spotted something in an Apple Event screenshot almost a year and a half later that I even considered this next part to be a thing. Once I tried it, it really opened my eyes and completely changed the way I was doing things within Jamf.</p><p>Despite it being one single Jamf Pro instance, you can see here that I&#x2019;ve configured multiple MDM servers.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/asm-mdm-servers.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="490" height="376"><figcaption>Screenshot of the configured MDM &quot;servers&quot; within ASM (device counts redacted).</figcaption></figure><p>Above, you can also see that I have configured a &#x201C;Jamf - Lab DEP&#x201D; server. Again, this is still pointing to the single Jamf Pro instance.</p><p>All will become clear when you see the Jamf side of this. But simply, any device that&#x2019;s an iPad will be automatically assigned to the iPad server, and an iPhone to the iPhone server. Quite self-explanatory.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/asm-default-device-assignment.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="923" height="339" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/asm-default-device-assignment.png 600w, https://blog.grayw.co.uk/content/images/2022/02/asm-default-device-assignment.png 923w" sizes="(min-width: 720px) 720px"><figcaption>Screenshot of the default device assignment settings in ASM.</figcaption></figure><hr><h2 id="jamf-pro-setup">Jamf Pro Setup</h2><p>I will not be teaching you how to configure this in this blog post series, as Jamf already has this documented:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/10.35.0/jamf-pro/documentation/Automated_Device_Enrollment_Integration.html#concept-367"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Automated Device Enrollment Integration - Jamf Pro Documentation | Jamf</div><div class="kg-bookmark-description">Enrollment is the process of adding computers and mobile devices to Jamf Pro . This establishes a connection between the computers and mobile devices and the Jamf Pro server. The Automated Device ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/10.35.0/jamf-pro/documentation/images/Icon_Pro_Settings.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"></div></a></figure><p>I am not taking responsibility for you messing up your server configurations and breaking everything ;).</p><p>Anyway, once this is configured, you will have your respective Automated Device Enrolment settings on the Jamf Pro side, too:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-ade-settings.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="1601" height="376" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-ade-settings.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-ade-settings.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/02/jamf-ade-settings.png 1600w, https://blog.grayw.co.uk/content/images/2022/02/jamf-ade-settings.png 1601w" sizes="(min-width: 720px) 720px"><figcaption>Screenshot of the ADE settings within Jamf (device numbers redacted).</figcaption></figure><p>As you can see, mine (almost) mirror that of the ASM side, making it obvious what they&#x2019;re for. I have no idea of the skill level of the person that may need to manage this after me, so the kindest thing to do is to try to make it obvious what I&#x2019;m trying to achieve.</p><p><strong>Step 3: Set up your PreStage Environment</strong></p><p>With the server connections in place between ASM and Jamf, we can now start to configure our PreStage environment for the Lab devices. For a deeper dive on this topic, I&#x2019;d suggest reading the Jamf documentation.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.jamf.com/10.35.0/jamf-pro/documentation/Computer_PreStage_Enrollments.html?hl=prestage"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Computer PreStage Enrollments - Jamf Pro Documentation | Jamf</div><div class="kg-bookmark-description">A PreStage enrollment allows you to create enrollment configurations and sync them to Apple. This enables you to enroll new computers with Jamf Pro , reducing the amount of time and interaction it ...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.jamf.com/favicon-194x194.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"><span class="kg-bookmark-author">Jamf</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.jamf.com/10.35.0/jamf-pro/documentation/images/Icon_Pro_Computers.png" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage"></div></a></figure><p>The important part of this feature that we&#x2019;re going to be making use of, though, is Auto Advance. This is key to the full automation side. It&#x2019;s also why your devices need to be on Big Sur or later, and you need to have working DHCP.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/apple-autoadvance-video.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="1028" height="608" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/apple-autoadvance-video.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/apple-autoadvance-video.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/apple-autoadvance-video.png 1028w" sizes="(min-width: 720px) 720px"><figcaption>Apple: WWDC Content: <a href="https://developer.apple.com/videos/play/wwdc2020-10639/?time=164">https://developer.apple.com/videos/play/wwdc2020-10639/?time=164</a></figcaption></figure><p>We&#x2019;ll now configure our PreStage to do what we need it to do. Another key part here is to ensure that you&#x2019;re automatically assigning devices from the <strong>Lab DEP</strong> server to this PreStage.</p><p>By doing this, anything that you assign to the <strong>Lab DEP</strong> server within <strong>ASM</strong> is going to be automatically synced with Jamf Pro&#x2019;s Automated Device Enrolment configuration. In turn, automatically assigned to this PreStage, ensuring that you don&#x2019;t have to do any manual assignments. Just assign them once at the high level, and you&#x2019;re done. It&#x2019;s worth noting here that if you change the assigned MDM server in <strong>ASM</strong>, that&#x2019;s going to change in Jamf, too. So, if your device is no longer going to be used in a Lab, move it!</p><p>We&apos;re going to have the PreStage automatically create a local administrator, and then skip the account creation process.</p><p>Here are a couple of screenshots of all the settings I&#x2019;ve configured for this PreStage. As you can see, I&#x2019;ve also added a few extra bits that will make my life a little easier. You may, or may not, need to do this depending on your environment.</p><p>Note that I&apos;ve selected the Lab - DEP ADE instance, and chose to automatically assign new devices.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-prestage-general-1.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="1185" height="2025" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-prestage-general-1.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-prestage-general-1.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-prestage-general-1.png 1185w" sizes="(min-width: 720px) 720px"><figcaption>Jamf PreStage - General Settings</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-prestage-account-settings-1.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="1187" height="660" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-prestage-account-settings-1.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-prestage-account-settings-1.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-prestage-account-settings-1.png 1187w" sizes="(min-width: 720px) 720px"><figcaption>Jamf PreStage - Account Settings</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/02/jamf-prestage-config-profiles-1.png" class="kg-image" alt="Fully Automated Lab iMac Deployment with Jamf Pro &amp; ADE: Part 1 - ASM to PreStage" loading="lazy" width="1208" height="60" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/jamf-prestage-config-profiles-1.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/jamf-prestage-config-profiles-1.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/jamf-prestage-config-profiles-1.png 1208w" sizes="(min-width: 720px) 720px"><figcaption>Jamf PreStage - Configuration Profiles</figcaption></figure><p>With all these steps completed, we&#x2019;re now in a great position to move on to the next part.</p><h2 id="summary">Summary</h2><p>I think that&#x2019;s enough for the first post in this series. The second post will head in to configuring the scripts, packages, and policies required to make the next stage possible.</p><p>But to sum up. In this post, we&#x2019;ve covered the basic requirements needed for this to be possible like Apple School Manger ordering, along with how I&#x2019;ve configured DEP on both the <strong>ASM</strong> and <strong>Jamf</strong> sides. And also the <strong>PreStage</strong> setup required to make the next steps possible.</p><p>As I said at the beginning, this is mostly for me for if I ever need to try to re-create this again (I hope not). But, if it helps someone else out there too, then that&#x2019;s a bonus.</p><p>The post Photo for this post series was by <a href="https://unsplash.com/@quaritsch?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Quaritsch Photography</a> on <a href="https://unsplash.com/s/photos/imac?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[Obtaining the FortiClient Offline Installer for macOS Distribution]]></title><description><![CDATA[<p>When trying to download the installer for FortiClient, you&apos;ll find that they only provide an oh-so-helpful &quot;Online Installer&quot;. In which, we find their &quot;FortiClientUpdate.app&quot;. Which is also about as much use as a chocolate heat-sink as it never seems able to install updates</p>]]></description><link>https://blog.grayw.co.uk/obtaining-the-forticlient-offline-installer-for-distribution/</link><guid isPermaLink="false">6203a5b4b70baa41e183d7e8</guid><category><![CDATA[macos]]></category><category><![CDATA[forticlient]]></category><category><![CDATA[jamf]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Wed, 09 Feb 2022 12:19:02 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/02/lukas-hellebrand-E1S962T0g5c-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/02/lukas-hellebrand-E1S962T0g5c-unsplash.jpg" alt="Obtaining the FortiClient Offline Installer for macOS Distribution"><p>When trying to download the installer for FortiClient, you&apos;ll find that they only provide an oh-so-helpful &quot;Online Installer&quot;. In which, we find their &quot;FortiClientUpdate.app&quot;. Which is also about as much use as a chocolate heat-sink as it never seems able to install updates anyway.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/forticlient-appupdatedmg.png" class="kg-image" alt="Obtaining the FortiClient Offline Installer for macOS Distribution" loading="lazy" width="988" height="504" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/forticlient-appupdatedmg.png 600w, https://blog.grayw.co.uk/content/images/2022/02/forticlient-appupdatedmg.png 988w" sizes="(min-width: 720px) 720px"></figure><p>Deploying this will get us absoloutely nowhere. We need the offline installer which can be obtained by running this from terminal to show us where it downloads the temporary file to.</p><p>To do this, you&apos;re going to need to make sure that FortiClient isn&apos;t already installed on the device, otherwise I&apos;ve found that it fails to download the &quot;update&quot;. If you have, make sure you run the FortiClient Uninstaller.app from your Applications folder first.</p><ul><li>Drag the .app file somewhere, desktop, downloads, whatever tickles.</li><li>Ctrl + Click &gt; Show Package Contents</li><li>Under Contents, Ctrl + Click macOS, and open a Terminal Window/Tab at that location</li></ul><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/forticlient-updateapp-openterminal.png" class="kg-image" alt="Obtaining the FortiClient Offline Installer for macOS Distribution" loading="lazy" width="880" height="549" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/forticlient-updateapp-openterminal.png 600w, https://blog.grayw.co.uk/content/images/2022/02/forticlient-updateapp-openterminal.png 880w" sizes="(min-width: 720px) 720px"></figure><ul><li>Run the FortiClientUpdate until it prompts you to &quot;Install&quot;. Then stop here:</li></ul><pre><code class="language-Bash">gray@fenrir MacOS % sudo ./FortiClientUpdate </code></pre><p>At the bottom of the terminal, you should see the location where it has downloaded the offline installer</p><pre><code class="language-Bash">[update:INFO] fcn_upgrade:562 Download FortiClient Connect successfully. Copy it to /var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/fctupdate/FortiClient.dmg</code></pre><p>Copy this file to wherever makes life easier for you. I usually go for downloads.</p><pre><code>sudo cp /var/folders/zz/zyxvpxvq6csfxvn_n0000000000000/T/fctupdate/FortiClient.dmg ~/Downloads/</code></pre><p>When you open this DMG, you&apos;ll have access to the .mpkg</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/forticlient-offlineinstaller-dmg.png" class="kg-image" alt="Obtaining the FortiClient Offline Installer for macOS Distribution" loading="lazy" width="665" height="552" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/forticlient-offlineinstaller-dmg.png 600w, https://blog.grayw.co.uk/content/images/2022/02/forticlient-offlineinstaller-dmg.png 665w"></figure><p>I&apos;ve noticed a number of issues when attempting to download .mpkg files from Jamf ditribution points on Monterey. I have no idea why, the error is simply that the file does not exist. Big Sur devices still seem to be able to download it just fine (same policy, same file, same DP).</p><p>I took the simple option here and renamed the file to FortiClient_{version}.pkg. It doesn&apos;t seem to have had any odd effects. As far as I can tell, there&apos;s no reason for it to be an .mpkg, as there aren&apos;t other .pkg&apos;s within it that it&apos;s trying to install.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2022/02/forticlient-suspiciouspackage.png" class="kg-image" alt="Obtaining the FortiClient Offline Installer for macOS Distribution" loading="lazy" width="1309" height="828" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/02/forticlient-suspiciouspackage.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/02/forticlient-suspiciouspackage.png 1000w, https://blog.grayw.co.uk/content/images/2022/02/forticlient-suspiciouspackage.png 1309w" sizes="(min-width: 720px) 720px"></figure><hr><p>This wasn&apos;t the first time I&apos;d had to look up how to get this offline installer, but my original source no longer seemed to be available. I did find my saviour in AlmightyBob this time around:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://community.fortinet.com/t5/Fortinet-Forum/FortiClient-VPN-on-MacOS-Monterey-error-code-121/m-p/29064/highlight/true#"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Re: FortiClient VPN on MacOS Monterey - error code: -121</div><div class="kg-bookmark-description">ok, so i got it to work but had to jump through some serious rings of fire to get it installed (since we switched to forticlient i&#x2019;ve had to do this before. I&#x2019;ve never been a fan of forticlient for this reason, its just easier when our hardware can update to the latest client and we can just downlo&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://community.fortinet.com/html/@8972BC546BD31AB16BECA16D7565FCA0/assets/favicon.ico" alt="Obtaining the FortiClient Offline Installer for macOS Distribution"><span class="kg-bookmark-author">Fortinet</span><span class="kg-bookmark-publisher">davidsteelerose New Contributor</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://community.fortinet.com/t5/image/serverpage/avatar-name/headphones/avatar-theme/candy/avatar-collection/tech/avatar-display-size/message/version/2?xdesc=1.0" alt="Obtaining the FortiClient Offline Installer for macOS Distribution"></div></a></figure><p>As Fortinet are obviously deleting these posts when they get around to it, I thought I&apos;d better write it down myself this time.</p><p>Post Photo by <a href="https://unsplash.com/@aguynamedlukas?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Lukas Hellebrand</a> on <a href="https://unsplash.com/s/photos/macos?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>]]></content:encoded></item><item><title><![CDATA[100 Days of SwiftUI - Day 5]]></title><description><![CDATA[<p>Hello, and welcome to day 5. If you&apos;re jumping in here, head on over to: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.uk/tag/100daysofswiftui/</a> </p><p>Follow along from the beginning (or jump in wherever you like, whatever nibbles your biscuit).</p><h2 id="workspace">Workspace</h2><p>Today we&apos;ll be heading back over to the</p>]]></description><link>https://blog.grayw.co.uk/100-days-of-swiftui-day-5/</link><guid isPermaLink="false">61d080e6b70baa41e183d51f</guid><category><![CDATA[100DaysofSwiftUI]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Sat, 01 Jan 2022 16:38:26 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2022/01/100daysofswiftui-day5-header.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2022/01/100daysofswiftui-day5-header.png" alt="100 Days of SwiftUI - Day 5"><p>Hello, and welcome to day 5. If you&apos;re jumping in here, head on over to: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.uk/tag/100daysofswiftui/</a> </p><p>Follow along from the beginning (or jump in wherever you like, whatever nibbles your biscuit).</p><h2 id="workspace">Workspace</h2><p>Today we&apos;ll be heading back over to the MacBook and Xcode, even though I certainly prefer the feel of Swift Playgrounds 4 so far.</p><h2 id="day-5overview">Day 5 - Overview</h2><p>Today we&apos;re going to be looking at if/else/else if, and a few other bits and pieces.</p><h2 id="check-if-a-condition-is-true-or-false">Check if a condition is true or false</h2><pre><code class="language-Swift">//
// How to check a true/false condition
//

var someCondition = true

if someCondition {
    print(&quot;Do Something&quot;)
}

let score = 85

if score &gt; 80 { // &gt; is a comparison operator is score greater than 80
    print(&quot;You pass!&quot;)
}

let speed = 88
let percentage = 85
let age = 18

if speed &gt;= 88 {
    print(&quot;Where we&apos;re going, we don&apos;t need roads.&quot;)
}

if percentage &lt; 85 {
    print(&quot;Sorry, you failed the test&quot;)
}

if age &gt;= 18 {
    print(&quot;You&apos;re eligible to vote&quot;)
}

// The comparison operators also work well with strings

let ourName = &quot;Dave Lister&quot;
let friendName = &quot;Arnold Rimmer&quot;

if ourName &lt; friendName {
    print(&quot;It&apos;s \(ourName) vs \(friendName)&quot;)
}

if ourName &gt; friendName {
    print(&quot;It&apos;s \(friendName) vs \(ourName)&quot;)
}


//

var numbers = [1, 2, 3]
numbers.append(4)

if numbers.count &gt; 3 {
    numbers.remove(at: 0)
}

print(numbers)

// We can also check equality

let country = &quot;Canada&quot;

if country == &quot;Australia&quot; {
    print(&quot;G&apos;day!&quot;)
}

let name = &quot;Swifty&quot;

if name != &quot;Anonymous&quot; {
    print(&quot;Welcome, \(name)&quot;)
}


var username = &quot;swifty22&quot;

if username == &quot;&quot; {
    username = &quot;Anonymous&quot;
}

print(&quot;Welcome, \(username)&quot;)

// Not an efficient way of doing it. If the value has hundreds of characters, it will count them all.
if username.count == 0 {
    username = &quot;Anonymous&quot;
}

// A more efficient, and easier way is to use isEmpty

if username.isEmpty {
    username = &quot;Anonymous&quot;
}

// Comparisons can also be done on enums (done on order in case list)
enum Sizes: Comparable {
    case small
    case medium
    case large
}

let first = Sizes.small
let second = Sizes.large

print(first &lt; second)</code></pre><h2 id="how-to-check-multiple-conditions">How to check multiple conditions</h2><pre><code class="language-Swift">//
// How to check multiple conditions
//
if someCondition {
    print(&quot;This will run if the condition is true&quot;)
} else {
    print(&quot;This will run if the condition is false&quot;)
}

// We can use else if&apos;s - but this can make code more complex
let a: Bool = true
let b: Bool = false

if a {
    print(&quot;Code to run if a is true&quot;)
} else if b {
    print(&quot;Code to run if a is false but b is true&quot;)
} else {
    print(&quot;Code to run if both a and b are false&quot;)
}

// We can use &amp;&amp; (and)
let temp = 25

if temp &gt; 20 &amp;&amp; temp &lt; 30 {
    print(&quot;It&apos;s a nice day.&quot;)
}

let userAge = 14
let hasParentalConsent = true

// We can use || (or)
if userAge &gt;= 18 || hasParentalConsent {
    print(&quot;You can buy the game&quot;)
}

enum transportOptions {
    case airplane, helicopter, bike, car, escooter
}

let transport = transportOptions.bike

if transport == .airplane || transport == .helicopter {
    print(&quot;Let&apos;s fly!&quot;)
} else if transport == .bike {
    print(&quot;I hope there&apos;s a bike path&quot;)
} else if transport == .car {
    print(&quot;Time to get stuck in traffic&quot;)
} else {
    print(&quot;I&apos;m going to hire a scooter now&quot;)
}

// If mixing &amp;&amp; and || - it&apos;s good practice to use parentheses to make the result clearer
if (isOwner == true &amp;&amp; isEditingEnabled) || isAdmin == true {
    print(&quot;You can delete this post&quot;)
}
</code></pre><ul><li>Conditions Review Test: 12/12</li><li>Combining Conditions Review Test: 12/12</li></ul><h2 id="how-to-use-switch-statements-to-check-multiple-conditions">How to use switch statements to check multiple conditions</h2><p>When should I use switch or if?</p><ul><li>switch statements must be exhaustive, so we must use a case block for every possible value to check, or have a default case (if using an enum, you don&#x2019;t need default). So you may miss a case while using if/else</li></ul><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2022/01/100daysofswiftui-switchsatement-error.png" class="kg-image" alt="100 Days of SwiftUI - Day 5" loading="lazy" width="1657" height="874" srcset="https://blog.grayw.co.uk/content/images/size/w600/2022/01/100daysofswiftui-switchsatement-error.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2022/01/100daysofswiftui-switchsatement-error.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2022/01/100daysofswiftui-switchsatement-error.png 1600w, https://blog.grayw.co.uk/content/images/2022/01/100daysofswiftui-switchsatement-error.png 1657w" sizes="(min-width: 1200px) 1200px"><figcaption>What happens if your switch statement isn&apos;t exhaustive, or contains a duplicate case</figcaption></figure><ul><li>When using switch to check a value for multiple possible results, that value will only be read once. If will read it multiple times.</li></ul><pre><code class="language-Swift">enum Weather {
    case sun, rain, wind, snow, unknown
}

let forcast = Weather.sun

// Using Switch to check conditions
// By using a switch - Swift knows that we need to check all possible cases (exhaustive), and there can&apos;t be duplicates
switch forcast {
case .sun:
    print(&quot;It should be a nice day&quot;)
case .rain:
    print(&quot;Pack an umbrella&quot;)
case .wind:
    print(&quot;Wear something warm&quot;)
case .snow:
    print(&quot;School is cancelled&quot;)
case .unknown:
    print(&quot;Our forcast generator is broken&quot;)
}

// We can use a default case to avoid trying to achieve the impossible of coming up with every single possible match

let place = &quot;Metropolis&quot;

switch place {
case &quot;Gotham&quot;:
    print(&quot;You&apos;re Batman!&quot;)
case &quot;Mega-City One&quot;:
    print(&quot;You&apos;re Judge Dredd&quot;)
default:
    print(&quot;Who are you?&quot;)
}

// Fallthrough can be used to allow multiple cases to be run but is rarely used in Swift

let day = 5
print(&quot;My true love game to me ...&quot;)

switch day {
case 5:
    print(&quot;5 golden rings&quot;)
    fallthrough
case 4:
    print(&quot;4 calling birds&quot;)
    fallthrough
case 3:
    print(&quot;3 french hens&quot;)
    fallthrough
case 2:
    print(&quot;2 turtle doves&quot;)
    fallthrough
default:
    print(&quot;A partridge in a pear tree&quot;)
}
</code></pre><p>Switch Statements Review Test: 6/6</p><h2 id="how-to-use-the-ternary-conditional-operator-for-quick-tests">How to use the ternary conditional operator for quick tests</h2><p>The ternary operator lets us choose from one of two results based on a condition in a concise way.</p><p>It has become important in SwiftUI, so is a key thing to know about.</p><pre><code class="language-Swift">// 2 + 5 - The + is a binary operator (they operate on two pieces of input)
// The is a ternary operator (it operates on three pieces of input). Let&apos;s us check a condition and then send back one value, or another value depending on the condition

let age = 18
let canVote = age &gt;= 18 ? &quot;Yes&quot; : &quot;No&quot;
print(canVote)

// age &gt;= 18 - The condition to be checked
// If true - Send back yes
// If false - Send back no

// Similar condensed form of if/else
// W T F - What? / True / False @scottmichaud

let hour = 23
print(hour &lt; 12 ? &quot;It&apos;s before noon&quot; : &quot;It&apos;s after noon&quot;)

// Check an array
let names = [&quot;Jayne&quot;, &quot;Kayleigh&quot;, &quot;Mal&quot;]
let crewCount = names.isEmpty ? &quot;No one&quot; : &quot;\(names.count) people&quot;
print(crewCount)

// With an enum

enum Theme {
    case light, dark
}

// Condition can also be put in parentheses
let theme = Theme.dark
let background = (theme == .dark) ? &quot;black&quot; : &quot;white&quot;
print(background)

</code></pre><p>Ternary Operator Review Test: 12/12</p><h2 id="summary">Summary</h2><p>Today seemed to go on forever, and I was only making notes in the editor as I went, plus a few scrap notes here and there.</p><p>I&apos;m not sure how long I&apos;ll keep blogging about each day, as I really don&apos;t have the time to learn, and to blog. I think I&apos;m painting myself in to a corner again. It&apos;s leaving me with no time to do anything else I&apos;d like to do with the few hours I get in the evening.</p><p>As you can tell, today was practically a copy/paste dump from the code editor, which seems kind of pointless and boring for a reader.</p><p>I&apos;ll see how the next few days go, and make a decision.</p><p>What are your thoughts?</p>]]></content:encoded></item><item><title><![CDATA[100 Days of SwiftUI - Day 4]]></title><description><![CDATA[<p>Hello! And here we are at day 4 of the hackingwithswift.com 100 Days of SwiftUI course. As always, if you need to catch up with the rest of the posts, you can do so over here: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.uk/tag/100daysofswiftui/</a> - If you&apos;re enjoying</p>]]></description><link>https://blog.grayw.co.uk/100-days-of-swiftui-day-4/</link><guid isPermaLink="false">61ceb647b70baa41e183d4b3</guid><category><![CDATA[100DaysofSwiftUI]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Fri, 31 Dec 2021 17:43:13 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2021/12/887F5E85-B857-482B-B2EE-4C245F8DC7BA.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2021/12/887F5E85-B857-482B-B2EE-4C245F8DC7BA.jpeg" alt="100 Days of SwiftUI - Day 4"><p>Hello! And here we are at day 4 of the hackingwithswift.com 100 Days of SwiftUI course. As always, if you need to catch up with the rest of the posts, you can do so over here: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.uk/tag/100daysofswiftui/</a> - If you&apos;re enjoying this series of posts, don&apos;t forget to follow the RSS feed.</p><h2 id="workspace">Workspace</h2><p>The workspace for today will be the iPad with Swift Playgrounds 4. I&apos;m practically fully charged, so we&apos;ll see how this goes.</p><h2 id="how-to-use-type-annotations">How to use type annotations</h2><p>For day 4, we&apos;re going to be looking at the last part of complex data types. This will be type annotations.</p><p>When should type annotations be used:</p><ol><li>When Swift can&apos;t figure out what type should be used</li><li>You want Swift to use a different type from the default</li><li>You don&apos;t want to assign a value yet</li></ol><p>Why use type inference:</p><ol><li>Can make code shorter and easier to read</li><li>Change the type quickly, and easily by changing the initial value</li></ol><p>Type Inference - infers that type is string/int/double/bool because that&apos;s what the initial value is</p><ul><li>Type Annotations Review Test: 6/6</li></ul><p></p><h2 id="summary-complex-data">Summary: Complex Data</h2><ul><li>Arrays store many values, and read them using indicies (zero-based)</li><li>Dictionaries store many values, and read them using keys we specify</li><li>Sets store many values, but we don&apos;t choose their order</li><li>Enums create our own types to specify a range of values</li><li>Swift uses type inference by default to figure out what data we&apos;re storing</li><li>Type annotation allows us to force a particular data type</li></ul><pre><code class="language-Swift">// Swift must always know what data type your constants and variables contain (type safe language)

// Variables &amp; Constants
let surname: String = &quot;Lasso&quot;
let score: Double = 0 // If this used type inference, it would have been an Int
var isAuthenticated: Bool = false

let userName: String
userName = &quot;@gray&quot;

// Arrays
var albums: [String] = [&quot;Red&quot;, &quot;Fearless&quot;]
var teams: [String] = [String]()
var cities: [String] = []
var clues = [String]()

// Dictionaries
var fosstodonUser: [String: String] = [&quot;id&quot;: &quot;@gray&quot;]
var hrData: [Int: String] = [123456: &quot;employeeName&quot;]

// Sets
var fruits: Set&lt;String&gt; = Set([
    &quot;Banana&quot;,
    &quot;Apple&quot;,
    &quot;Peach&quot;,
    &quot;Orange&quot;
])

// enums 
enum UiStyle {
    case light, dark, system
}

var style = UiStyle.dark
style = .system</code></pre><h2 id="checkpoint-2">Checkpoint 2</h2><p>For the second checkpoint, the task was to create an array, and then print the number of items in that array. You also then need to print the number of unique items for that array.</p><p>My solution:</p><pre><code class="language-Swift">let displayNames = [&quot;Batman&quot;, &quot;TheRealJoker&quot;, &quot;TheRealJoker&quot;, &quot;Joker&quot;, &quot;Penguin&quot; ]
let numberOfUsers = displayNames.count
print(numberOfUsers)

let uniqueUsers = Set(displayNames)
print(uniqueUsers)</code></pre><p>The first part to create the array and the count was fine. However, the unique part I couldn&apos;t think how to do straight away. So, I did a little search and got this:</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/6C23B4FC-51D9-4B92-88D8-25FF193EF0B1.jpeg" class="kg-image" alt="100 Days of SwiftUI - Day 4" loading="lazy" width="853" height="1218" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/6C23B4FC-51D9-4B92-88D8-25FF193EF0B1.jpeg 600w, https://blog.grayw.co.uk/content/images/2021/12/6C23B4FC-51D9-4B92-88D8-25FF193EF0B1.jpeg 853w" sizes="(min-width: 720px) 720px"></figure><p>Of course, Sets can&apos;t contain duplicate values. I didn&apos;t need to create it as a new array, so I left that part off.</p><h2 id="summary">Summary</h2><p>Well, that&apos;s the end of day 4. I feel like I should have been able to get the unique items count thing without having to search, but there you go.</p><p>I seem to struggle with this single topic being split across multiple days. By the time I&apos;ve got here, I&apos;m forgetting what was said on day 1. </p>]]></content:encoded></item><item><title><![CDATA[100 Days of SwiftUI - Day 3]]></title><description><![CDATA[<p>Hello, and welcome to day 3 of the 100 Days of SwiftUI blog series where I&apos;m following the hackingwithswift.com course by the same name. If you&apos;ve skipped the first couple of posts, you can go back over all the posts here: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.</a></p>]]></description><link>https://blog.grayw.co.uk/100-days-of-swiftui-day-3/</link><guid isPermaLink="false">61cd8409b70baa41e183d373</guid><category><![CDATA[100DaysofSwiftUI]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Thu, 30 Dec 2021 12:40:02 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day3-header.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day3-header.png" alt="100 Days of SwiftUI - Day 3"><p>Hello, and welcome to day 3 of the 100 Days of SwiftUI blog series where I&apos;m following the hackingwithswift.com course by the same name. If you&apos;ve skipped the first couple of posts, you can go back over all the posts here: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.uk/tag/100daysofswiftui/</a></p><h2 id="workspace">Workspace</h2><p>My workspace for today will be Xcode on the MacBook again. However, I think it&apos;s important to note that while I have opted to use the MacBook once again, I feel that I enjoyed my time in the Playgrounds editor far more.</p><p>It was an incredibly clean, simple interface, and just felt like a far smoother experience than Xcode. I&#x2019;m certain this opinion will change once I get past the very basics, though.</p><h2 id="how-to-store-ordered-data-in-arrays">How to store ordered data in arrays</h2><p>The first part of today will be taking a look at how to store data in arrays.</p><p>Starting with the basics, we create and array much like any other variable or constant. Except this time, we wrap them in [] after the assignment operator =.</p><pre><code class="language-Swift">var goodGuys = [&quot;Two Face&quot;, &quot;The Penguin&quot;, &quot;Joker&quot;]
var badGuys = [&quot;Batman&quot;, &quot;Robin&quot;]

let someLostNumbers = [4, 8, 15, 16, 23, 42]</code></pre><p>When referencing a value in an array, we query its <strong>index</strong>. That is its location within the array. However, we start our count at 0, rather than 1.</p><pre><code class="language-Swift">print(goodGuys[2])
print(someLostNumbers[4])</code></pre><p>We can add data to a variable array by using <strong>.append()</strong></p><pre><code class="language-Swift">badGuys.Append(&quot;Joker?&quot;)</code></pre><p>We need to keep in mind that we can&apos;t add any data we want to an array. If it contains strings, we can&apos;t add an integer. Type safety wants to ensure that an array of strings is an array of strings. An array of integers, remains as integers.</p><p>Attempting to add a string to an array of integers will result in an error. Xcode will even try to give you a hint at what sort of value you should be adding, too.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day3-appendint.jpg" class="kg-image" alt="100 Days of SwiftUI - Day 3" loading="lazy" width="566" height="146"></figure><p>If you do append an erroneus item to an array, the first part of the error does seem to be a little cryptic compared to some others I&apos;ve seen in the past.</p><pre><code class="language-Console">error: Day_003.playground:64:17: error: no exact matches in call to instance method &apos;append&apos;
someLostNumbers.append(&quot;Something&quot;)
                ^

Day_003.playground:64:17: note: found candidate with type &apos;(__owned Int) -&gt; ()&apos;
someLostNumbers.append(&quot;Something&quot;)
                ^

Day_003.playground:64:17: note: found candidate with type &apos;(__owned String) -&gt; ()&apos;
someLostNumbers.append(&quot;Something&quot;)
                ^
</code></pre><p>But, as you work through it, you can kind of see what it&apos;s getting at.</p><p>What happens if you want to create an array, but don&apos;t have anything to put in it yet? Well, this can be done by creating an empty specialised array.</p><pre><code class="language-Swift">var scores = Array&lt;Int&gt;()

// A shorter way
var scores = [Int]()</code></pre><p>If you need to know how many items are in an array, we can use <strong>.count.</strong></p><pre><code class="language-Swift">someLostNumbers.count</code></pre><p>There are various ways to remove items from an array. The first, removing one item at a time by its index. Another way it to remove all of the items. By typing .remove in Xcode, I can also see there are many more options.</p><pre><code class="language-Swift">someLostNumbers.remove(at: 2)
someLostNumbers.removeAll()</code></pre><p>How about checking to see if an array contains something? This can be done using .contains.</p><pre><code class="language-Swift">someLostNumbers.contains(15) // Returns true</code></pre><p>Sorting an array can be done by using <strong>.sorted</strong></p><pre><code class="language-Swift">someLostNumbers.sorted()</code></pre><p>Finally, for, there is also reversing an array. This can be done using <strong>.reversed()</strong>.</p><pre><code class="language-Swift">let foundNumbers = someLostNumbers.reversed()
print(foundNumbers)</code></pre><p>Doing this doesn&apos;t actually return what I&apos;d have expected, but rather something called a ReversedCollection that makes a reference back to the original</p><pre><code class="language-Console">ReversedCollection&lt;Array&lt;Int&gt;&gt;(_base: [4, 8, 15, 16, 23, 42])</code></pre><p>I don&apos;t fully understand how or why this is happening, and will need to look in to this further. It&apos;s not the behaviour I expected. Initially, I can see how it could be seen as more optimal as it&apos;s not having to do the work of changing it all and saving it again.</p><ul><li>Arrays Review Test: 6/6</li></ul><h2 id="storing-and-finding-data-in-dictionaries">Storing and Finding Data in Dictionaries</h2><p>Reading items from an array using their index isn&apos;t always ideal, and could actually cause issues. Swift also has dictionaries, which are based on key/value pairs which let us decide how we want to store the data inside.</p><p>They are created in much the same way as an array, but rather than just being a long list of values, you assign keys.</p><pre><code class="language-Swift">let batmanData = [
    &quot;realName&quot;: &quot;Bruce Wayne&quot;,
    &quot;base&quot;: &quot;Bat Cave&quot;,
    &quot;sidekick&quot;: &quot;Robin&quot;
]</code></pre><p>When trying to return data from the dictionary, Xcode may warn you that you may, or may not, get data back. When it returns the value, it comes back with &quot;Optional&quot;.</p><p>The solution to this is telling Swift to return another value by <a href="https://www.hackingwithswift.com/quick-start/understanding-swift/why-does-swift-have-default-values-for-dictionaries">default</a> if it can&apos;t return something.</p><pre><code class="language-Swift">print(batmanData[&quot;base&quot;])

// If the key doesn&apos;t exist, we can tell Swift to return another value.
print(batmanData[&quot;realName&quot;, default: &quot;Unknown&quot;])
</code></pre><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day3-batmandata.png" class="kg-image" alt="100 Days of SwiftUI - Day 3" loading="lazy" width="1434" height="198" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswiftui-day3-batmandata.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswiftui-day3-batmandata.png 1000w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day3-batmandata.png 1434w" sizes="(min-width: 1200px) 1200px"></figure><p>We can also create other types of dictionaries, such as a string/boolean, or an integer/string.</p><pre><code class="language-Swift">// We can also keep booleans as values - A String/Boolean dictionary
let hasGraduated = [
    &quot;Eric&quot;: false,
    &quot;Maeve&quot;: true,
    &quot;Otis&quot;: false
]

// Or an Integer/String dictionary
var olympics = [
    2012: &quot;London&quot;,
    2016: &quot;Rio&quot;,
    2021: &quot;Tokyo&quot;
]

print(olympics[2012, default: &quot;Unknown&quot;])
</code></pre><p>To create an empty dictionary, it would be done like this:</p><pre><code class="language-Swift">var heights = [String: Int]()

// Add a key and value to the empty dictionary
heights[&quot;Person One&quot;] = 229</code></pre><p>And finally, dictionaries cannot contain duplicate values. If you were to assign a new value, it would simply overwrite the existing.</p><pre><code class="language-Swift">olympics[2021] = &quot;Somewhere Else&quot;</code></pre><ul><li>Dictionary Review Test: 11/12</li></ul><p>Mistakes:</p><p>One of the questions asked if this was valid code. I assumed this was false as I mistakingly thought it was trying to add something to the dictionary. On a second look, I can see that it&apos;s assigning a value to a constant, based on what could be in the dictionary.</p><pre><code>let capitals = [&quot;England&quot;: &quot;London&quot;, &quot;Wales&quot;: &quot;Cardiff&quot;]
let scotlandCapital = capitals[&quot;Scotland&quot;]</code></pre><p></p><h2 id="using-sets-for-fast-data-lookup">Using Sets for Fast Data Lookup</h2><p>As well as arrays, and dictionaries, there is also sets. They work similar to arrays, but they don&apos;t remember the order you added the values, and they don&apos;t allow duplicates.</p><p>So, why would you use this over an array? You may not want to allow duplicates in your data. Sets are also incredibly efficient in how it stores the data, so returning a result by using something like <strong>.contains()</strong> will be far faster than if you used an array.</p><p>Creating a set is done in a similar way to an array. </p><pre><code class="language-Swift">// This creates an array inside the set
var actors = Set([
    &quot;Denzel Washington&quot;,
    &quot;Tom Cruise&quot;,
    &quot;Nicolas Cage&quot;,
    &quot;Samuel L Jackson&quot;
])</code></pre><p>Adding items to a set is done using insert, rather than append.</p><pre><code class="language-Swift">actors.insert(&quot;Angelina Jolie&quot;)</code></pre><p>You can also sort a set, which will return an array with your sorted data.</p><pre><code class="language-Swift">print(actors.sorted())</code></pre><p>For a deeper dive on sets, see <a href="https://www.avanderlee.com/swift/array-vs-set-differences-explained/">https://www.avanderlee.com/swift/array-vs-set-differences-explained/</a></p><ul><li>Sets Review Test: 12/12</li></ul><h2 id="creating-an-using-enums">Creating an Using enums</h2><p>Enum, short for enumeration, is a set of named values. We can define types that contain a specific set of values, such as the days of the week:</p><pre><code class="language-Swift">enum Weekday {
    case monday
    case tuesday
    case wednesday
    case thursday
    case friday
}</code></pre><p>Storing things this way is far more efficient, as Swift stores them in an optimised form.</p><p>It&apos;s also a safer way, ensuring that a wrong value couldn&apos;t be assigned.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswift-day3-enums.png" class="kg-image" alt="100 Days of SwiftUI - Day 3" loading="lazy" width="1434" height="299" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswift-day3-enums.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswift-day3-enums.png 1000w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswift-day3-enums.png 1434w" sizes="(min-width: 1200px) 1200px"></figure><p>Enums Review Test: 12/12</p><p></p><h2 id="summary">Summary</h2><p>That&apos;s it for day 3. The dictionaries review test caught me out on something I should have spotted, but it&apos;s coming up to lunch time and I was starting to make some silly mistakes.</p><p>It&apos;s also taking far, far longer than I wanted to get through the content, and write up a post. Maybe that&apos;s a good thing, as it will reinforce what I&apos;m taking in, but it&apos;s also making me want to stop already ... at day 3.</p>]]></content:encoded></item><item><title><![CDATA[100 Days of SwiftUI - Day 2]]></title><description><![CDATA[<p>Welcome to day 2 of the 100 Days of SwiftUI. If you didn&apos;t read day 1, head on over to the post here: <a href="https://blog.grayw.co.uk/100-days-of-swiftui-day-1/">https://blog.grayw.co.uk/100-days-of-swiftui-day1</a>. But, in short, I&apos;ll be following the 100 Days of SwiftUI from <a href="https://www.hackingwithswift.com/100/swiftui/">hackingwithswift.com</a>, and documenting my</p>]]></description><link>https://blog.grayw.co.uk/100-days-of-swiftui-day-2/</link><guid isPermaLink="false">61cc490cb70baa41e183d24b</guid><category><![CDATA[100DaysofSwiftUI]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Wed, 29 Dec 2021 13:18:02 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-header.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-header.png" alt="100 Days of SwiftUI - Day 2"><p>Welcome to day 2 of the 100 Days of SwiftUI. If you didn&apos;t read day 1, head on over to the post here: <a href="https://blog.grayw.co.uk/100-days-of-swiftui-day-1/">https://blog.grayw.co.uk/100-days-of-swiftui-day1</a>. But, in short, I&apos;ll be following the 100 Days of SwiftUI from <a href="https://www.hackingwithswift.com/100/swiftui/">hackingwithswift.com</a>, and documenting my progress here (for as long as I remember to).</p><p>If you&apos;d like to follow this series, you can do so by following the tag here: <a href="https://blog.grayw.co.uk/tag/100daysofswiftui/">https://blog.grayw.co.uk/tag/100daysofswiftui/</a></p><p>With that out of the way, let&apos;s get down to it!</p><h1 id="simple-data-types-part-2">Simple Data Types: Part 2</h1><p>Today looks like a slightly shorter day than yesterday from the list of topics. It looks like we&apos;re going to cover booleans, and joining strings. There&apos;s also the first checkpoint of the course.</p><p>I&apos;ll also be working from the MacBook today, rather than the iPad. I did notice that having a video playing PiP (Picture in Picture) from Safari, did utilise a vast amount of the battery. I may have to keep the iPad for just lighter reading. We&apos;ll see.</p><p>So, my setup today is using Xcode on macOS in a new Day 2 playground.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-001.png" class="kg-image" alt="100 Days of SwiftUI - Day 2" loading="lazy" width="1715" height="840" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswiftui-day2-001.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswiftui-day2-001.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/100daysofswiftui-day2-001.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-001.png 1715w" sizes="(min-width: 720px) 720px"></figure><h2 id="how-to-store-truth-with-booleans">How to store truth with Booleans</h2><p>Booleans store a value of either <strong>true</strong> or <strong>false</strong>. Apparently, these were named after George Boole, an English mathematician.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://en.wikipedia.org/wiki/George_Boole"><div class="kg-bookmark-content"><div class="kg-bookmark-title">George Boole - Wikipedia</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://en.wikipedia.org/static/apple-touch/wikipedia.png" alt="100 Days of SwiftUI - Day 2"><span class="kg-bookmark-author">Wikimedia Foundation, Inc.</span><span class="kg-bookmark-publisher">Contributors to Wikimedia projects</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://upload.wikimedia.org/wikipedia/commons/c/ce/George_Boole_color.jpg" alt="100 Days of SwiftUI - Day 2"></div></a></figure><p>We can see the basics of this by creating a variable, and then using the <strong>.hasSuffix()</strong> we learned about in day 1, to check if the filename ends with a specific string.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-002.png" class="kg-image" alt="100 Days of SwiftUI - Day 2" loading="lazy" width="1759" height="884" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswiftui-day2-002.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswiftui-day2-002.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/100daysofswiftui-day2-002.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-002.png 1759w" sizes="(min-width: 1200px) 1200px"></figure><p>In this example, we can see that the filename is wales.jpg, and we&apos;re using .hasSuffix() to check to see if it ends with .jpg. This is returning true, because ... it does.</p><p>The same also applies for the second example that 120 is a multiple of 3. I will take that as true, because I am well and truly numerically dyslexic. They make no sense to me at all.</p><h2 id="slight-tangent">Slight Tangent</h2><p>On the note of numbers making no sense, I did learn about the Japanese method of multiplication a few years ago which made far more sense to me than years of anything else that people tried to drum in to my head.</p><p>You can read more about it here: <a href="https://www.whizz.com/en-us/blog/how-the-japanese-multiplication-method-works/">https://www.whizz.com/en-us/blog/how-the-japanese-multiplication-method-works/</a></p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/JAPAN-4.png" class="kg-image" alt="100 Days of SwiftUI - Day 2" loading="lazy" width="791" height="803" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/JAPAN-4.png 600w, https://blog.grayw.co.uk/content/images/2021/12/JAPAN-4.png 791w" sizes="(min-width: 720px) 720px"></figure><h2 id="back-on-track">Back on Track</h2><p>Back to Booleans.</p><p>Unlike strings, integers, and doubles, booleans do not have a selection of operators as this makes little sense. However, there is one, the <strong>!</strong>. This will swap the value, as can be seen in the below screenshot.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-003.png" class="kg-image" alt="100 Days of SwiftUI - Day 2" loading="lazy" width="1759" height="884" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswiftui-day2-003.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswiftui-day2-003.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/100daysofswiftui-day2-003.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-003.png 1759w" sizes="(min-width: 1200px) 1200px"></figure><p>isAuthenticated starts off as false. We then take that and assign it a new value that is &quot;not&quot; the original value, thereby making it true. Doing the same again, swaps it back and saves that value back to the variable.</p><p>Another method is using <strong>.toggle()</strong>. It achieves the same outcome, but with less code. gameOver starts as false, but by typing gameOver.toggle(), we have swapped the value to true.</p><ul><li>Doubles and Booleans Review Test: 6/6</li></ul><h2 id="joining-strings-together">Joining Strings Together</h2><p>The second part of today is about joining strings together. There are two ways of doing this. The first is the simplest option. You can use the <strong>+</strong> operator to join two strings together.</p><p>However, this is not the most efficient way of doing things as this causes Swift to create temporary strings as it adds each additional part to it. A good example of this is the luggageCode variable. It will first add 1 and 2 together to make &quot;12&quot;, then add the 3, to make &quot;123&quot;, and so on.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-004.png" class="kg-image" alt="100 Days of SwiftUI - Day 2" loading="lazy" width="1715" height="840" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswiftui-day2-004.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswiftui-day2-004.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/100daysofswiftui-day2-004.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-004.png 1715w" sizes="(min-width: 1200px) 1200px"></figure><p>Something you&apos;re unable to do here is + a number in to a string. So the below wouldn&apos;t work:</p><pre><code class="language-Swift">let phoneNumber = 123456789
let message = &quot;Give me a call on &quot; + phoneNumber</code></pre><p>However, what you could do is tell Swift to treat the integer as a string instead:</p><pre><code class="language-Swift">let phoneNumber = 123456789
let message = &quot;Give me a call on &quot; + String(phoneNumber)</code></pre><p>This isn&apos;t the best way to do this, which brings us to the second way, which is string interpolation.</p><p>We can drop other values in to a string by using <strong>\(varname)</strong> within it. This is much easier to read and understand.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-005.png" class="kg-image" alt="100 Days of SwiftUI - Day 2" loading="lazy" width="1715" height="840" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/100daysofswiftui-day2-005.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/100daysofswiftui-day2-005.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/100daysofswiftui-day2-005.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/100daysofswiftui-day2-005.png 1715w" sizes="(min-width: 1200px) 1200px"></figure><p>So, for our previous example, we would write it as:</p><pre><code class="language-Swift">let phoneNumber = 123456789
let message = &quot;Give me a call on \(phoneNumber)&quot;</code></pre><p>We can also include things like multiplication inside a string with string interpolation.</p><pre><code class="language-Swift">print(&quot;5 x 5 is \(5 * 5)&quot;)</code></pre><p>For a deeper dive on string interpolation, I should take a look at <a href="https://www.hackingwithswift.com/articles/178/super-powered-string-interpolation-in-swift-5-0">https://www.hackingwithswift.com/articles/178/super-powered-string-interpolation-in-swift-5-0</a></p><ul><li>String Interpolation Review Test: 6/6</li></ul><h2 id="summary-of-simple-data">Summary of Simple Data</h2><ul><li>Swift lets us create constants using <strong>let</strong> and variables with <strong>var</strong>. Constants should be used when possible.</li><li>Swift&apos;s strings contain text. This can be whole words, sentences, paragraphs, or more. These are created by using double quotes at the start and end, or &quot;&quot;&quot; at the start and end on their own line for multi-line strings.</li><li>Storing whole numbers are done with integers - <strong>Int</strong></li><li>Storing decimal numbers is done with double - <strong>Double</strong> which is short for Double Point Floating Numbers. These are not 100% accurate. </li><li>Int and Double&apos;s have a range of arithmetic operators</li><li>You can store simple true or false values with Booleans - <strong>Bool</strong></li><li>String interpolation lets us place data in strings efficiently and easily.</li></ul><h2 id="simple-datacheckpoint-1">Simple Data - Checkpoint 1</h2><p>At checkpoint 1, we&apos;re asked to create a playground that achieves the following:</p><ul><li>Creates a constant holding any temperature in Celsius</li><li>Converts that temperature to Fahrenheit by multiplying by 9, dividing by 5, then adding 32.</li><li>Prints the result, showing both the Celsius and Fharenheit values.</li></ul><p>Below is my solution.</p><pre><code class="language-Swift">let celsius = 13
let fahrenheit = celsius * 9 / 5 + 32
print(&quot;The current temperature is \(celsius), which is \(fahrenheit) in Fahrenheit.&quot;)</code></pre><p>This returns the string:</p><pre><code class="language-Text">The current temperature is 13, which is 55 in Fahrenheit.</code></pre><p>In this checkpoint, I have created two constants. One with an Integer, and the second using the first, using operators to create a second Integer. I then used string interpolation to return a string with data in it.</p><p>How did this differ from the solution in the video? Well, first was that I should have used a decimal number for the temperature. I had no inclination to show .x in the temperature. </p><p>The second was that I could have used Option+Shift+8 to get the &#xB0; symbol to use in the string.</p><p>If I were to re-write it to line up with that, I would do it like this:</p><pre><code>let celsius = 13.0
let fahrenheit = celsius * 9 / 5 + 32
print(&quot;The current temperature is \(celsius)&#xB0;, which is \(fahrenheit)&#xB0;F.&quot;)</code></pre><p>This returns the string:</p><pre><code>The current temperature is 13.0&#xB0;, which is 55.4&#xB0;F.</code></pre><p></p><h2 id="summary">Summary</h2><p>Thanks for following along in with day 2. I hope you&apos;ll join me for day 3.</p>]]></content:encoded></item><item><title><![CDATA[100 Days of SwiftUI - Day 1]]></title><description><![CDATA[<p>In an effort to improve myself, I have decided (after much, much back and forth) to attempt to learn Swift.</p><p>I spend my day managing both Apple and Windows devices. The majority of my time is spent around macOS, and iOS. However, I also spend quite a lot of time</p>]]></description><link>https://blog.grayw.co.uk/100-days-of-swiftui-day-1/</link><guid isPermaLink="false">61cb27d0b70baa41e183d0e2</guid><category><![CDATA[100DaysofSwiftUI]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Tue, 28 Dec 2021 17:04:47 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2021/12/9BAC37B0-8806-4B88-92C8-5A29E1A41FB7.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2021/12/9BAC37B0-8806-4B88-92C8-5A29E1A41FB7.jpeg" alt="100 Days of SwiftUI - Day 1"><p>In an effort to improve myself, I have decided (after much, much back and forth) to attempt to learn Swift.</p><p>I spend my day managing both Apple and Windows devices. The majority of my time is spent around macOS, and iOS. However, I also spend quite a lot of time deploying Windows-based devices too. I&apos;ve also had a few app ideas i&apos;ve been wanting to create for a number of years.</p><p>After much self-debate, I finally decided that Swift would be the best option, as I spend more time in the Apple world than the others these days. Not only will it allow me to create the apps I want, but it could also have a positive effect on the things I&apos;m able to achieve in work, too. Unfortunately, this won&apos;t help me on the Windows side, but, I can also do enough PowerShell to be dangerous, and have tools such as Microsoft Endpoint Manager to make my life easier.</p><p>So, feel free to follow along as I learn SwiftUI, and see what I make, or if I can even make it to day 100. It wouldn&apos;t be the first time I&apos;ve done 100 days of something - check out my 100 Days to Offload post series! <a href="https://blog.grayw.co.uk/tag/100daystooffload/">https://blog.grayw.co.uk/tag/100daystooffload/</a></p><p>As I can already do some basic things with PowerShell, there are going to be parts of this course that are immediately obvious, but I&apos;ll be treating them as if I know nothing. So you&apos;ll need to bare with me (or skip ahead if you&apos;re reading this later on in the course).</p><p>Less blabber, on to the content!</p><h2 id="the-workspace">The Workspace</h2><p>Due to my job role, I&apos;ve got a few devices I can work with here. Both a MacBook, and an iPad (yes, I&apos;m lucky, and grateful). As long as it doesn&apos;t interfere with my day to day work, I&apos;m able to experiement with these devices as I wish.</p><p>With the recent release of Swift Playground 4 - I will probably be using that as much as possible, rather than the full blown Xcode on the Mac. This will allow me to be far more flexible on when and where I&apos;m learning (I&apos;m looking at you, porcelain throne).</p><p>I&apos;ve created a blank plaground to start the basics.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/D4F786A6-D16F-4B85-B7E4-4FDCBED7251B.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/D4F786A6-D16F-4B85-B7E4-4FDCBED7251B.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/D4F786A6-D16F-4B85-B7E4-4FDCBED7251B.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/D4F786A6-D16F-4B85-B7E4-4FDCBED7251B.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/D4F786A6-D16F-4B85-B7E4-4FDCBED7251B.png 2360w" sizes="(min-width: 720px) 720px"></figure><h2 id="variables-constants">Variables &amp; Constants</h2><p>The first main part of day 1, is the creation of variables and constants.</p><p>Variables are created by using the <strong>var</strong> keyword, followed by the name you wish to give the variable, an = sign, and then the content. </p><pre><code class="language-Swift">var greeting = &quot;Hello, Readers!&quot;</code></pre><p>Variables can be re-assigned a new value as often as needed, but you only need to use the <strong>var</strong> keyword once. After that, simply use the name of variable.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/2678D750-9069-4DE7-88B0-8A225F88745F.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/2678D750-9069-4DE7-88B0-8A225F88745F.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/2678D750-9069-4DE7-88B0-8A225F88745F.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/2678D750-9069-4DE7-88B0-8A225F88745F.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/2678D750-9069-4DE7-88B0-8A225F88745F.png 2360w" sizes="(min-width: 720px) 720px"></figure><p>In the above, you can see that I&apos;ve created the greeting variable, and assigned it the value of &quot;Hello, Readers!&quot;. Not quite the typical &quot;Hello, World&quot;, but it&apos;s close enough!</p><p>After that, I create a variable called <strong>catName</strong> and assign it the value of Nuts (yep, that&apos;s the name of one of my cats). I then change the value to the name of my other cat. So, what does this do?</p><p>If I add in a few lines to &quot;print&quot; the variable contents, I can see how they&apos;re returned.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/048BA3C1-9EE0-4B0A-AFBD-3763B0D96B5C.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/048BA3C1-9EE0-4B0A-AFBD-3763B0D96B5C.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/048BA3C1-9EE0-4B0A-AFBD-3763B0D96B5C.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/048BA3C1-9EE0-4B0A-AFBD-3763B0D96B5C.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/048BA3C1-9EE0-4B0A-AFBD-3763B0D96B5C.png 2360w" sizes="(min-width: 720px) 720px"></figure><p>Variables can contain all manner of contents, but we&apos;re sticking with simple examples for now.</p><p>Next up is <strong>Constants</strong>. Their value cannot be changed once assigned. If you attempt to change the value, you will receive a warning, and the code will not run.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/E35BD440-BA46-453F-BEEC-0684A614C48C.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/E35BD440-BA46-453F-BEEC-0684A614C48C.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/E35BD440-BA46-453F-BEEC-0684A614C48C.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/E35BD440-BA46-453F-BEEC-0684A614C48C.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/E35BD440-BA46-453F-BEEC-0684A614C48C.png 2360w" sizes="(min-width: 720px) 720px"></figure><p>When possible, you should use a constant over a variable, as this allows Swift to better optimise your code, and, more importantly, stops you from changing the value by mistake.</p><p>The names of variables and constants are case sensitive, and generally created using the camelCase naming convention.</p><ul><li>Variables Review Test: 6/6</li><li>Constants Review Test: 6/6</li></ul><h2 id="working-with-strings">Working with Strings</h2><p>So far, all of our variables or constants have been <strong>strings</strong>. There are a number of things we can do with them. This is just a couple of very basic examples.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/509629FC-5385-4AF1-A0DD-6497066D4CEB.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/509629FC-5385-4AF1-A0DD-6497066D4CEB.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/509629FC-5385-4AF1-A0DD-6497066D4CEB.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/509629FC-5385-4AF1-A0DD-6497066D4CEB.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/509629FC-5385-4AF1-A0DD-6497066D4CEB.png 2360w" sizes="(min-width: 720px) 720px"></figure><p>This first is using &quot;&quot; within a string. To do this, you need to use the \ symbol to <strong>escape</strong> that character, otherwise Swift will show an error, as it thinks the string has ended, and there is now contents after it that shouldn&apos;t be there.</p><p>Next is a multi-line string. These are rarely used, but should you need to use one, then they start with &quot;&quot;&quot;, with your text on new lines, ended with &quot;&quot;&quot; on the last line.</p><p>You can get the length of a string by using <strong>.count</strong>. In my example, I printed the length of catName, which returned 6. You don&apos;t have to print this directly, it can be assigned to another variable/constant, and then used later.</p><p>Another example is converting the entire string to uppercase by using <strong>.uppercased()</strong></p><p>And finally, you can also check strings to see if they start with (prefix), or end with (suffix) specific words or characters. This is done by using either <strong>.hasPrefix()</strong> or <strong>.hasSuffix()</strong>. Both of my examples return true, as it matches. It&apos;s important to keep in mind that this is case sensitive. So, if my last example was:</p><pre><code class="language-Swift">print(longString.hasSuffix(&quot;INSIDE.&quot;))</code></pre><p>This would return false. As that&apos;s not how the string ends.</p><ul><li>Multi-line String Review Test: 12/12</li></ul><h2 id="working-with-whole-numbers">Working with Whole Numbers</h2><p>Working with integers as variables or constants is just as easy as it is with strings. You use the var/let keywords, followed by the name, and then the value. If the number is really long, you can use <strong>_</strong> to break it up, and make it easier to read. Swift will ignore these, and display the number as it should be.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/CB58F94C-8E71-45E7-9C02-3C8C27A19493.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/CB58F94C-8E71-45E7-9C02-3C8C27A19493.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/CB58F94C-8E71-45E7-9C02-3C8C27A19493.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/CB58F94C-8E71-45E7-9C02-3C8C27A19493.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/CB58F94C-8E71-45E7-9C02-3C8C27A19493.png 2360w" sizes="(min-width: 720px) 720px"></figure><p>You can also do the obvious things, such as addition, subtraction, division, etc. There is also a way of checking to see if a number is a multiple of another, by using <strong>.isMultiple(of: number)</strong>.</p><ul><li>Working with Whole Numbers Review Test: 6/6</li></ul><h2 id="working-with-decimal-numbers">Working with Decimal Numbers</h2><p>Swift also has the ability to work with decimal numbers, also known as floating point, or doubles.</p><p>You can do the same kind of &#xA0;things with these, however, at this very basic level, there are things you need to be aware of, and you may not get the result you were expecting.</p><p>For example, </p><pre><code class="language-Swift">let floatingNumber = 0.1 + 0.2</code></pre><p>This will not return 0.3 as you might expect, as you can see in the below screenshot.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/13039E0F-32F5-49E5-850F-1293C6BBF867.png" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="1390" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/13039E0F-32F5-49E5-850F-1293C6BBF867.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/13039E0F-32F5-49E5-850F-1293C6BBF867.png 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/13039E0F-32F5-49E5-850F-1293C6BBF867.png 1600w, https://blog.grayw.co.uk/content/images/2021/12/13039E0F-32F5-49E5-850F-1293C6BBF867.png 2360w" sizes="(min-width: 720px) 720px"></figure><p>Swift also classes integers and decimals as different types. So, you can&apos;t add an integer to a double, or vice-versa. Unless you tell Swift to treat one number as the other.</p><pre><code class="language-Swift">let numberA = 1
let numberB = 2.0
let numberC = numberA + Int(numberB)</code></pre><p>Type Safety will also step in if you try to change the type of variable. For example, changing a string to an integer.</p><figure class="kg-card kg-image-card"><img src="https://blog.grayw.co.uk/content/images/2021/12/EC788C5F-274A-44BC-8FF5-C801509F9B41.jpeg" class="kg-image" alt="100 Days of SwiftUI - Day 1" loading="lazy" width="2000" height="549" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/12/EC788C5F-274A-44BC-8FF5-C801509F9B41.jpeg 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/12/EC788C5F-274A-44BC-8FF5-C801509F9B41.jpeg 1000w, https://blog.grayw.co.uk/content/images/size/w1600/2021/12/EC788C5F-274A-44BC-8FF5-C801509F9B41.jpeg 1600w, https://blog.grayw.co.uk/content/images/2021/12/EC788C5F-274A-44BC-8FF5-C801509F9B41.jpeg 2360w" sizes="(min-width: 720px) 720px"></figure><p></p><h2 id="summary">Summary</h2><p>That&apos;s the end of the content for day 1 of Hacking with Swift&apos;s 100 Days of SwiftUI. As things progress past these first few basic concepts, I&apos;m sure you&apos;ll start seeing me make stupid mistakes, but hey, isn&apos;t that a part of learning?</p><p>If you&apos;d like to join in, head over to <a href="https://www.hackingwithswift.com/100/swiftui">https://www.hackingwithswift.com/100/swiftui</a> and follow along. </p><p>Hopefully, by making this initial post, it will hold me accountable to making progress each day (Hah!).</p>]]></content:encoded></item><item><title><![CDATA[Adobe Creative Cloud Desktop - Update Server Bypass]]></title><description><![CDATA[With more people working from home, and the AUSST being unstable at best. I look at how to bypass the internal update server and deploy that change to existing clients.]]></description><link>https://blog.grayw.co.uk/adobe-creative-cloud-desktop-ausst-bypass/</link><guid isPermaLink="false">6135d58eb70baa41e183ce72</guid><category><![CDATA[adobe]]></category><category><![CDATA[ausst]]></category><category><![CDATA[macos]]></category><category><![CDATA[windows]]></category><category><![CDATA[transfer]]></category><dc:creator><![CDATA[Gray]]></dc:creator><pubDate>Mon, 06 Sep 2021 15:27:42 GMT</pubDate><media:content url="https://blog.grayw.co.uk/content/images/2021/09/ausst.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.grayw.co.uk/content/images/2021/09/ausst.png" alt="Adobe Creative Cloud Desktop - Update Server Bypass"><p>If you&apos;ve ever had the misfortune of having to manage the deployment of Adobe applications at scale, then you&apos;ll most likely be well aware of the Adobe Update Server (AUS) and the Adobe Update Server Setup Tool (AUSST).</p><blockquote>What is AUSST<br><br>In an enterprise environment, you usually have many users who require various Adobe apps and updates. Each user is required to download and install the apps individually. Allowing all your end users to individually download and install apps from the Adobe servers will consume a sizeable amount of network bandwidth in your organization.<br><br>To address the issue of network bandwidth consumption, Adobe provides Adobe Update Server Setup Tool (AUSST). AUSST enables you to centralize the download of Adobe apps and updates to a server location. &#xA0;After implementing AUSST, you can redirect your end users to download the Adobe apps and updates from the internal update server. This way, a single download from the Adobe servers is required for each app or update.<br><br>&#x2013; Shamelessly copied from <a href="https://helpx.adobe.com/enterprise/using/update-server-setup-tool.html">https://helpx.adobe.com/enterprise/using/update-server-setup-tool.html</a></blockquote><p>Sounds like a really useful tool, right? Wrong. If it&apos;s not something you&apos;ve ever had to deal with, continue to pray you never have to lay hands on it. The Adobe forums are plagued with people asking why it does things, and why it doesn&apos;t. There are countless threads where Adobe &quot;support&quot; answers once or twice, suggests you contact them directly, and then proceed to ignore you for eternity. It provides an intermittent, unreliable service at best. I guess they&apos;re keeping it in line with all of their other products and services ...</p><p></p><h2 id="the-problem">The Problem</h2><p>As with most companies, recent times dictated that many of our employees began working from home. This now meant that all of those devices that were configured to contact our internal update server for Adobe updates were now hammering back over the VPN resulting in the following:</p><p>Internal Update Server - Downloads updates (if you&apos;re lucky)<br>Client requests update<br>Overrides point to internal server<br>Client connects to internal server over VPN<br>Client downloads update from internal server</p><p>On an internal network without the VPN, this would be fine. But now we&apos;re just wasting resources. Not only that, but if your internal server is having a moment, then your clients aren&apos;t going to receive the updates, as they won&apos;t query Adobe directly.</p><p>And that server moment is precisely the thing that&apos;s happened. Again. Despite the permissions being correct, and the content being available on the server, it&apos;s returning 404&apos;s and 401&apos;s to the client requests. My only remaining option is to completely rebuild the server and start from scratch. But I&apos;ve got clients that need updates now to mitigate CVE&apos;s that affect us.</p><p></p><h2 id="the-solution">The Solution</h2><p>So, what can you do when your internal server isn&apos;t available? Well, according to Adobe, you make sure you tick a little box when creating the overrides on the admin dashboard - <a href="https://helpx.adobe.com/lt/enterprise/using/update-server-setup-tool.html">https://helpx.adobe.com/lt/enterprise/using/update-server-setup-tool.html</a> - &quot;What happens if your internal update server is down&quot; (because who wants anchor links ...)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.grayw.co.uk/content/images/2021/09/pref-bypass-internal-update-server.png" class="kg-image" alt="Adobe Creative Cloud Desktop - Update Server Bypass" loading="lazy" width="1012" height="644" srcset="https://blog.grayw.co.uk/content/images/size/w600/2021/09/pref-bypass-internal-update-server.png 600w, https://blog.grayw.co.uk/content/images/size/w1000/2021/09/pref-bypass-internal-update-server.png 1000w, https://blog.grayw.co.uk/content/images/2021/09/pref-bypass-internal-update-server.png 1012w" sizes="(min-width: 720px) 720px"><figcaption>Preferences screenshot from the Adobe Help Page</figcaption></figure><p>Well, that seems simple enough. But wait a minute, how are my existing clients going to know about this change if the package has already been installed (package and overrides were created by someone else). I&apos;m not being prompted to change the configuration on the server, because it&apos;s &quot;down&quot;, and the overrides file on the machine is telling it to get the updates from adobeupdates.yourdomain.com (please note: this is probably a really, awful idea, especially if it&apos;s available externally without a VPN).</p><p>After ticking that box, I diffed the override XML. To my complete lack of surprise, nothing had changed. I generated a new installer package, and after searching for far too long, I finally found the reference to the bypass.</p><p>Hiding in a file named Binary.optionXML, there&apos;s a section with the following:</p><pre><code class="language-xml">&lt;feature&gt;
  &lt;name&gt;AdobeFallbackForAUSST&lt;/name&gt;
  &lt;enabled&gt;true&lt;/enabled&gt;
&lt;/feature&gt;</code></pre><p>All I needed to do next, was find out where this particular block needed to be added. After more searching and file content indexing, I found that the other settings stored with this block are in the ServiceConfig.xml file. This can be found in the following location</p><p>	Windows: C:\Program Files (x86)\Common Files\Adobe\OOBE\Configs\<br>	macOS: /Library/Application Support/Adobe/OOBE/Configs/</p><p>Insert the fallback block in to this file somewhere and save it. It will probably look a little something like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-xml">&lt;config&gt;
    &lt;panel&gt;
        &lt;name&gt;AppsPanel&lt;/name&gt;
        &lt;visible&gt;true&lt;/visible&gt;
    &lt;/panel&gt;
    &lt;panel&gt;
        &lt;name&gt;FilesPanel&lt;/name&gt;
        &lt;masked&gt;false&lt;/masked&gt;
    &lt;/panel&gt;
    &lt;panel&gt;
        &lt;name&gt;MarketPanel&lt;/name&gt;
        &lt;masked&gt;false&lt;/masked&gt;
    &lt;/panel&gt;
    &lt;feature&gt;
        &lt;name&gt;SelfServeInstalls&lt;/name&gt;
        &lt;enabled&gt;true&lt;/enabled&gt;
    &lt;/feature&gt;
    &lt;feature&gt;
        &lt;name&gt;BrowserBasedAuthentication&lt;/name&gt;
        &lt;enabled&gt;false&lt;/enabled&gt;
    &lt;/feature&gt;
    &lt;feature&gt;
        &lt;name&gt;SelfServePluginsInstall&lt;/name&gt;
        &lt;enabled&gt;false&lt;/enabled&gt;
    &lt;/feature&gt;
    &lt;feature&gt;
          &lt;name&gt;AdobeFallbackForAUSST&lt;/name&gt;
          &lt;enabled&gt;true&lt;/enabled&gt;
    &lt;/feature&gt;
&lt;/config&gt;</code></pre><figcaption>ServiceConfig.xml with the fallback block at the bottom</figcaption></figure><p>After you&apos;ve made this change, you&apos;ll need to restart the machine. Restarting the Creative Cloud Desktop application didn&apos;t seem to have any affect. Probably due to the disgusting number of processes it holds open in the background, despite being &quot;Quit&quot;. Trying to end all of these manually just isn&apos;t worth the effort.</p><p></p><h2 id="deploying-the-fix">Deploying the Fix</h2><p>If this works for you in test, then the only thing left to do is to deploy the change to your devices. I felt that this would be easier than re-creating packages and deploying those out, due to the vast difference in file sizes. You should always consider that some users may have bandwidth / usage caps. ~5 KB file rather than a few hundred MB is far easier to manage (yes, the Windows Creative Cloud Desktop App is almost 800 MB by itself).

</p><p>With the majority of our Windows-based devices now being co-managed via Intune ... Sorry, Microsoft Endpoint Manager, I&apos;ll be pushing the file out as a Win32 app. This allows me plenty of control to make a copy of the existing file while copying the new file to the locations required.</p><p>For our macOS-based devices, I&apos;ll be pushing that out with Jamf. I&apos;m not sure which method I&apos;m going to use for this yet. I&apos;m torn between creating a package with the new file in it, or using a combination of a script to make the copy, and then pull the updated file from an internal repo. </p><p>On typing that, and with a few minutes reflection. I think I will create a package with the file, and a preinstall script to make a copy of the old one before &quot;installing&quot; the new one.</p><p></p><h2 id="pros-cons">Pro&apos;s &amp; Con&apos;s</h2><p>Doing this may not be an option for everyone, especially if you need to tightly control the versions that are being deployed within your environment. But for those of us who were just trying to save on data costs from within the network, this may be an option.</p><p>By doing this, you are telling the client to reach out directly to Adobe in the event that your internal server isn&apos;t contactable. You could then block access to your server via the VPN, ensuring that anything physically outside your network will contact Adobe directly. Those still inside will attempt to download from the server first.</p><ul><li>Pro - External users are no longer hammering the VPN tunnels with Adobe updates</li><li>Pro - In the event that your internal update server is down, clients can still download their updates and stay up-to-date with patches</li><li>Pro - Smoother experience for the end user<br></li><li>Con - Loss of control over deployed versions</li><li>Con - If your internal server is down, your internal devices could potentially give your connection a beating</li></ul><p>Even that second con isn&apos;t necessarily a forgone conclusion. Anything that physically remains inside your network, such as a desktop, could always be excluded from receiving the new file. </p><p><strong>Important: Keep in mind that any future packages generated via the admin portal will contain this fallback option.</strong></p>]]></content:encoded></item></channel></rss>