[{"content":"this field has a lot of people who got here sideways. i\u0026rsquo;m one of them.\ni know enough to get around. i can read code, understand what it\u0026rsquo;s doing at face value, catch something that looks wrong. years of working alongside engineers will do that for you, and honestly, it had to: you can\u0026rsquo;t manage a technical team from a position of total bewilderment. but building something from the ground up was always where i\u0026rsquo;d hit a ceiling. i was a GUI guy in a world that occasionally required a terminal.\nthe project in late 2024, after we migrated from Jamf to Fleet, we had a small visibility problem. our zero-touch enrollment process was rebuilt, tested, and launched, but when things went sideways, we found out the slow way: an employee messaged us hours after something broke on their first day. there was no real-time window into what was actually happening on a device as it enrolled.\nit was a low-risk gap. onesies and twosies, not a crisis. but i was starting to see enough of what AI could do that i thought: this is exactly the kind of problem worth pointing it at. small enough to experiment safely. real enough to matter if it worked.\nso i gave AI a job. no procurement process required.\nthe result was echo: a system that watched Fleet MDM enrollments in real time, coordinated a team of specialized agents on AWS Bedrock, and posted live progress updates to Slack. when all the policies verified, it celebrated. when something went wrong, it escalated. it watched so we didn\u0026rsquo;t have to, at any hour, without needing to be asked.\nthe full architecture story is on YouTube, but that\u0026rsquo;s not really what this post is about.\nwhat shifted here\u0026rsquo;s the thing i didn\u0026rsquo;t fully appreciate until i was in the middle of building echo: i wasn\u0026rsquo;t primarily writing code. i was primarily writing instructions.\nnow, there was real code involved. Lambdas, Terraform, API wiring. i had touched this stuff before, but this was more advanced than anything i\u0026rsquo;d tackled on my own. AI coding tools did a lot of the heavy lifting.\nand i mean that literally. the way you built with AI back then was: describe what you need, get code back, apply it, see what breaks, describe the problem, get a fix. you were the glue. not writing from scratch, but not copy-pasting blind either. you had to know enough to ask the right question, understand the answer well enough to apply it, and iterate until the thing ran. that loop, it turned out, was where i did my best work.\nfor someone who\u0026rsquo;d spent years knowing what they wanted but struggling with the translation, that was the revelation.\nthe same instinct carried into writing instructions for echo itself. not code. a job description. and if you\u0026rsquo;ve ever written one for an actual human, you\u0026rsquo;ll find the instinct is immediately familiar. the difference is, this one was written for exactly our environment, our tools, our workflow. nobody else\u0026rsquo;s.\nthe answer to \u0026ldquo;how specific do you need to be\u0026rdquo; was: very specific. more specific than felt necessary. the kind of specific that requires you to close every door you didn\u0026rsquo;t know you were leaving open.\nand that, it turned out, was something i had been practicing for years.\nuse your words when our kids were young, my wife and i said this to them constantly. when they\u0026rsquo;d get frustrated and shut down, when they\u0026rsquo;d give up on communicating because articulating what they needed felt harder than just hoping someone figured it out, we\u0026rsquo;d tell them: use your words. the more precisely you can describe what you want, the more likely you are to actually get it.\nwe said it like it was obvious. it isn\u0026rsquo;t. it\u0026rsquo;s a skill. and it compounds.\nwhat i discovered building echo was that the skill i\u0026rsquo;d spent my whole career developing, the ability to communicate clearly, specifically, and creatively about what i wanted a system to do, was the exact skill that made all of it work. the prompting to build. the instructions for the agents. all of it. not just helpful. essential.\nit didn\u0026rsquo;t matter that i couldn\u0026rsquo;t recite syntax from memory. what mattered was whether i could describe what i needed precisely enough to get something useful back, and then precisely enough again when that something broke. and then precisely enough again when i was writing the job description for an agent that had no common sense and would absolutely make things up if i left a door open.\ni wasn\u0026rsquo;t limited anymore by what i could write in a code editor. i was limited only by how clearly i could describe what i wanted. and for the first time in my career, that was a race i knew how to run.\nthe change this might sound like a story about a cool monitoring tool we built. it isn\u0026rsquo;t, really.\nit\u0026rsquo;s a story about a category of work that hadn\u0026rsquo;t existed for me before: building a product from a vague idea, through to something running in production, without needing someone to write the technical parts i couldn\u0026rsquo;t write myself. not copy-pasting from a forum. not asking a colleague to handle the hard bit. designing the thing, describing the thing, and iterating until the thing worked.\nback then, building with AI meant you were the glue: chat window open in one tab, code editor in another, you in the middle, doing by hand the kind of coordination you were trying to automate. it was slow. it was often humbling. and it worked. and the more i sat with it, the more i started to wonder what else was possible.\nbut it starts here: with a non-technical person realizing that the thing they were actually good at, using their words, was the unlock. everything else followed.","href":"https://the.hmn.engineer/posts/use-your-words/","kind":"page","lang":"en","lastmod":"2026-03-31T00:00:00Z","objectID":"e929292bb67c9d1585da9f962424e2a8","publishDate":"2026-03-31T00:00:00Z","section":"posts","tags":[],"title":"use your words","type":"posts"},{"content":"let me set the scene.\nit was late 2024. our team had just made the decision to move our entire mac fleet from Jamf Pro to Fleet. renewal was coming up. we didn\u0026rsquo;t want to renew. this was fine.\nthe timeline we gave ourselves: 9 days.\nthe migration backstory is here. what i want to talk about is the engine underneath it: the Okta Workflows system that made the whole thing run itself.\nwhy okta workflows, though? fair question. Okta Workflows is, on paper, an identity automation platform. it\u0026rsquo;s not marketed as \u0026ldquo;the thing you use to migrate your MDM.\u0026rdquo; there\u0026rsquo;s no Fleet connector in the Okta Integration Network. and yet, here we are.\nthe answer was practical: we were already using it. our team had been building no-code automations in Workflows for a while, so when we needed to spin something up fast, it was the obvious choice. the no-code environment meant we could prototype quickly, and the Workflows Tables feature gave us a real-time data store we could read and write to without rate limiting headaches.\nwhen you\u0026rsquo;re staring down a 9-day deadline, you don\u0026rsquo;t go looking for the best tool. you reach for the one you already trust.\nwhat employees experienced before i pull back the curtain, i want to revisit what this looked like from the other side, because the whole point of building something this complicated was so that employees didn\u0026rsquo;t have to think about it.\nit started with a company-wide Slack message from me laying out what was coming and when. on migration day, employees just had to look for the \u0026ldquo;migrate to Fleet\u0026rdquo; option in their Fleet Desktop menu bar icon and click \u0026ldquo;start.\u0026rdquo;\nabout 45 to 90 seconds later, Apple\u0026rsquo;s native remote management screen appeared. they clicked \u0026ldquo;enroll,\u0026rdquo; entered their computer password, got a green checkmark, and clicked \u0026ldquo;quit.\u0026rdquo;\nstart to finish: under 5 minutes.\nsince we ran this migration, Apple has introduced native MDM migration support that makes this employee-facing step even smoother, including the ability to enforce migration with a deadline if users don\u0026rsquo;t act. that\u0026rsquo;s a genuinely nice improvement. but it doesn\u0026rsquo;t change what\u0026rsquo;s happening underneath. the state tracking, the cleanup, the self-healing, the Slack updates: none of that comes from Apple. that\u0026rsquo;s still on you to build. which is exactly what we did.\nthe architecture: one table to rule them all the heart of the whole system was a single Okta Workflows Table. for some of our previous automations we\u0026rsquo;d experimented with other data stores and had run into enough friction to know we wanted something native to the tool we were already building in. Workflows Tables were the right call: fast, reliable, and no auth headaches. since this migration was largely an internal operation, we didn\u0026rsquo;t have a pressing need to surface the data anywhere else, so keeping it inside Okta Workflows was a non-issue.\neach row represented a device. and the table ended up being more than just a migration scoreboard; it doubled as a live device inventory. the data wasn\u0026rsquo;t all collected at once. it was populated and updated on a cadence as each flow ran and conditions were met. by the time a device was fully migrated, a row would contain:\ndevice_model: what kind of mac it was macos_version: what OS it was running assigned_user + email: who owned it fleet_url: its home in Fleet filevault_status: encrypted? good. enrollment_type: automatic or manual and a series of migration state columns, each one acting as a gate for the next flow in the chain that last point is key. every flow evaluated specific column values before deciding whether to act. a device didn\u0026rsquo;t move forward until the table said it was ready. the whole system was essentially a state machine, with the table as the source of truth.\nthe individual flows will make more sense with one more concept in your back pocket: helper flows. in Okta Workflows, a helper flow is a child flow that a parent flow can call to process items one at a time. since we were dealing with a table full of devices, not a single record, helper flows were what made it possible to loop through rows and apply logic to each device individually, in sequence. if there\u0026rsquo;s one thing my team and i keep coming back to across all of our Workflows builds, it\u0026rsquo;s this: helper flows show up everywhere. they\u0026rsquo;re less of a nice-to-have and more of a key ingredient worth understanding early.\nthe flows we built seven flows in total. with the exception of the host inventory collector, which ran on its own cadence throughout the day, these flows were designed to run in sequence during a targeted window each night. each one set up the conditions for the next.\nmigration trigger\nthis was the entry point. when an employee clicked \u0026ldquo;migrate to Fleet\u0026rdquo; in the Fleet Desktop UI, it fired a webhook that hit this flow. the flow queued the MDM unenrollment in Jamf and returned a 201 confirming the unenroll was in the queue (note: queued, not necessarily executed yet). it then wrote to the table and posted a Slack notification so we could watch progress in real time.\nenrollment watcher\nFleet\u0026rsquo;s activity log routes all activity through a single webhook. this flow sat on that webhook and used branching logic to filter specifically for events where type = mdm_enrolled; anything else was ignored. when it found a match, it looked up the device by serial number in Fleet to get its Fleet ID, found the right row in the table, and marked it enrolled. another Slack update fired.\nthis was the confirmation step: the Jamf unenroll was in the queue, and now we knew Fleet had actually taken over.\nhost inventory collector\na scheduled flow that ran four times a day. for any table row that had a Fleet ID, it queried Fleet and pulled back the device\u0026rsquo;s team, OS version, FileVault status, and MDM server URL, writing all of it back into the table. this kept the inventory live throughout the migration without anyone having to manually check anything.\njamf removal\nthis one was a two-part operation spanning two separate flow runs, because script execution results aren\u0026rsquo;t available in real time.\nthe first run evaluated whether conditions were met, then triggered a specific script execution via the Fleet API, targeting the individual device. rather than waiting for a result that wasn\u0026rsquo;t coming, it recorded the execution ID returned by the Fleet API back into the table and moved on.\nthe second run came later in the nightly window. it picked up that execution ID, queried the Fleet API for the result of that specific script run, and evaluated the exit code. if it returned 0, it updated jamf_bits_removed in the table and the device was cleared to move forward.\nhost transfer\nevaluated four conditions: unenrolled from Jamf, sitting in the staging team, not yet transferred, and enrolled in Fleet. if all four were true, it moved the device into the production workstations team and updated the table.\nthe staging step was intentional. newly migrated devices were parked there deliberately, keeping them away from production policies and configuration profiles until we were confident they were fully clear of Jamf. moving to the workstations team was the signal that a device was clean, compliant, and ready to have the full policy set applied.\nenrollment remediation\nthis one was novel, because it handled reality. sometimes the Jamf unenrollment would get queued but not fully complete, leaving a device stuck in an in-between state where Jamf had told it to leave but Fleet couldn\u0026rsquo;t fully take over. this flow found those devices and triggered a script that ran a single command: jamf removeMdmProfile. just enough to nudge the process along. having a flow that could detect and self-heal stuck states automatically meant nobody was babysitting devices late at night.\nmigration completion check\nevaluated whether a device had fully crossed the finish line: unenrolled from Jamf, in the production workstations team, jamf bits removed, team transfer confirmed. once all four conditions were true, it marked the device complete in the table and posted a Slack notification.\nthe missing connector as of today, there is no official Okta Workflows connector for Fleet. the ecosystem is still growing, and it meant every API call we made during the migration had to be manually specified through Okta\u0026rsquo;s generic HTTP connector: endpoints, request structure, all of it, every time.\nso after the migration wrapped, we started building a custom Fleet connector using Okta\u0026rsquo;s Connector Builder. the idea is simple: package the most common Fleet API calls as proper Workflow cards, with auth configured once at the connector level. Okta has a solid 7-part series on how to build one if you want to go down that path.\nit\u0026rsquo;s still a work in progress, but the goal is the same as everything else in this post: less manual wiring, more reusable building blocks. every future Fleet-related flow should start from a real connector, not a pile of one-off HTTP cards.\nbeautifully anticlimactic looking back, the whole thing was a little boring to watch unfold. nothing broke spectacularly. nobody stayed up all night. Okta Workflows let us build a system that did the migration for us: tracking state, handling failures, promoting devices automatically, and keeping the team informed the whole way. no-code, no drama. the system just\u0026hellip; worked.\nlooking back, what i\u0026rsquo;m most proud of isn\u0026rsquo;t necessarily just the technical execution. it\u0026rsquo;s that we took a chance. we had a tight window, a tool we trusted, and a team willing to move fast and figure it out. the migration proved that when we needed to, we could. that kind of confidence doesn\u0026rsquo;t come from planning. it comes from doing.\ni presented this work at Oktane 2025 as part of the Flowcase session, alongside other customers sharing how they\u0026rsquo;re putting Workflows to use. we walked away with the Workflows Future Builder Award. the full presentation is available on demand here if you want to see it in action.","href":"https://the.hmn.engineer/posts/going-with-the-flow/","kind":"page","lang":"en","lastmod":"2026-03-17T00:00:00Z","objectID":"ebfb7a735e0720202ebc265f2dfd3d1d","publishDate":"2026-03-17T00:00:00Z","section":"posts","tags":[],"title":"going with the flow","type":"posts"},{"content":"in november 2024, my team migrated macOS device management providers from Jamf Pro to Fleet Device Management. it took 9 days. it went well. and i\u0026rsquo;ve been meaning to write about it ever since.\nhere\u0026rsquo;s the thing about writing a migration story over a year later in my world: the landscape has already shifted. Apple shipped native MDM migration support in macOS 26, complete with admin-set deadlines and enforced re-enrollment. Jamf announced a unified Platform API ecosystem at JNUC 2025 that addresses a lot of the API fragmentation we ran into. the world has moved.\nso this isn\u0026rsquo;t a \u0026ldquo;here\u0026rsquo;s why you should leave Jamf\u0026rdquo; post. Jamf served us well for years. it kept our devices in line, profiles applied, policies shipped. it did the job.\nbut in late 2024, our team was evolving. we were thinking less like IT administrators and more like engineers. we wanted to manage devices the way we managed everything else: through code, through automation, through workflows that didn\u0026rsquo;t require someone clicking through a UI on a Tuesday night. we were spending more time maintaining the MDM than actually helping people. and the API limitations at the time made building reliable automation harder than it needed to be.\nso we went looking for something that matched where we were headed. we found Fleet.\nwhy fleet this wasn\u0026rsquo;t a \u0026ldquo;shiny new tool\u0026rdquo; decision. it was a \u0026ldquo;where do we want to be in two years\u0026rdquo; decision.\nFleet checked a few boxes that mattered to us:\none platform, multiple operating systems. we were running separate MDMs for macOS and Windows. that\u0026rsquo;s two admin consoles, two sets of policies, two places to check when something breaks. Fleet gave us a single pane of glass for everything. fewer tools, fewer headaches.\nconfigure with code. we\u0026rsquo;d already gone down this road with Okta, managing our identity provider through Terraform and treating configuration like infrastructure. Fleet gave us the same option for device management. policies, profiles, and settings defined in YAML, stored in a Git repo, applied through pull requests. version-controlled configs, repeatable deployments, no more \u0026ldquo;who changed that profile last month?\u0026rdquo; and if GitOps isn\u0026rsquo;t your thing, the admin UI works just fine too.\nactual transparency. Fleet is open-source. not \u0026ldquo;open-source-ish\u0026rdquo; or \u0026ldquo;we publish some things.\u0026rdquo; their entire codebase, bug tracker, and roadmap live on GitHub. when we found bugs, we reported them. when we wanted features, we requested them. and Fleet actually listened and shipped. that\u0026rsquo;s rare.\nsupport that feels like a partnership. from the start, Fleet\u0026rsquo;s support felt less like a vendor relationship and more like having an extra teammate. our team had direct access to real people who knew our environment, and throughout the migration, that responsiveness made a real difference. Fleet\u0026rsquo;s \u0026ldquo;My Device\u0026rdquo; page also lets every employee see exactly what\u0026rsquo;s being managed on their machine. no mystery, no \u0026ldquo;what is IT doing to my laptop?\u0026rdquo; it builds trust with the people using the devices, and trust makes everything else easier.\nhow we pulled it off the prep work before we could move anything, we needed to get organized. we started by cleaning house in Jamf, trimming the cruft that accumulates when you\u0026rsquo;ve been running a platform for years. then we extracted everything that mattered: policies, configuration profiles, the settings we actually relied on. all of it got cataloged and turned into a project plan so nothing got lost in translation.\nFleet\u0026rsquo;s policy engine runs on osquery, which was new to us. learning a new query language in the middle of a migration sounds stressful, but we leaned on AI assistants to help bridge the gap between what we had in Jamf and what Fleet needed. it ended up being one of those \u0026ldquo;learn by doing\u0026rdquo; situations that actually worked.\nthe tracking system we needed a single source of truth for every device in the migration. where is it, what\u0026rsquo;s its status, has it enrolled, does it need attention.\nwe went with an Okta Workflows table, which was a deliberate choice. we already knew Okta Workflows was going to be our automation engine for a bunch of post-migration tasks, so having the tracking data native to the same platform meant our automations could read and write without the latency of bouncing between external APIs. no round-trips to a Google Sheet, no waiting on third-party connections. just fast, direct access to the data right where the logic lived.\ndeploying the agent before we could migrate anyone, we needed the Fleet agent on every Mac. so we used the tool we were migrating away from to get it done. poetic, right?\nwe built a custom Fleet agent package using fleetctl and pushed it out via a Jamf policy. within a few days, the majority of our fleet had the agent installed and checking in.\nthe announcement we sent a company-wide Slack message. it was friendly, clear, and included links to docs and support. we told people what to expect, reassured them it would be painless, and kept the tone light. this part matters more than people think. a migration only goes smoothly if the humans involved aren\u0026rsquo;t anxious about it.\nthe actual migration we updated Apple Business Manager to reassign devices from Jamf to Fleet. then, from the employee\u0026rsquo;s perspective, it was three steps:\nclick \u0026ldquo;Migrate to Fleet\u0026rdquo; in the Fleet menu bar icon click \u0026ldquo;Start\u0026rdquo; in the migration window click \u0026ldquo;Enroll\u0026rdquo; when Apple\u0026rsquo;s Remote Management prompt appeared that\u0026rsquo;s it. no tickets, no appointments, no \u0026ldquo;bring your laptop to IT.\u0026rdquo;\nthe automation layer this is the part i\u0026rsquo;m most proud of.\nonce a device enrolled in Fleet, a chain of Okta Workflows kicked in automatically. the device got assigned to its proper team in Fleet. nightly scripts cleaned up leftover Jamf artifacts so nothing lingered from the old world. a health check workflow scanned for broken enrollments and quietly fixed issues before anyone had a chance to notice them. and every migration event posted to Slack in real-time, giving us a running scoreboard of where things stood.\nwe didn\u0026rsquo;t have to babysit the migration. we built the workflows, set them loose, and watched devices roll through. the Okta Workflows table tied it all together: at any point, we could see how many devices had migrated, who was still pending, and whether anything needed attention.\nthe results the migration went smoothly. really smoothly. but the speed isn\u0026rsquo;t the real story.\nwhat mattered was what happened after. we eventually consolidated into a single MDM (Windows came later in 2025), and our team found a new gear. it\u0026rsquo;s hard to describe without sounding like i\u0026rsquo;m knocking what came before, and i want to be clear: Jamf was the only MDM i\u0026rsquo;d ever known, and it earned its place. it kept our environment healthy for years.\nbut somewhere along the way, we\u0026rsquo;d stopped being curious. the tool wasn\u0026rsquo;t evolving at the pace our ambitions were, and that gap had quietly made us stagnant. Fleet gave us a breath of fresh air. not just a new tool, but a new energy. the pace of development, the open roadmap, the possibilities baked into the platform. it made us want to build again, explore again, ask \u0026ldquo;what if\u0026rdquo; again.\nwe went from maintaining to imagining.\nthat\u0026rsquo;s the part nobody tells you about a good migration. it\u0026rsquo;s not just about moving from one thing to another. it\u0026rsquo;s about what you get to do once you\u0026rsquo;re on the other side.\nlooking ahead (and back) it\u0026rsquo;s funny writing about a migration that happened over a year ago. the tools have changed, Apple has made the mechanics easier, and even Jamf has evolved. if we were doing this today, some of the hoops we jumped through simply wouldn\u0026rsquo;t exist.\nbut the stuff that made it work? the automation strategy, the communication, the decision to bet on a platform that made our team excited to build again? that\u0026rsquo;s timeless. no OS update is going to automate that for you.\nwe\u0026rsquo;ve been building on Fleet ever since. this is the first post. it won\u0026rsquo;t be the last.","href":"https://the.hmn.engineer/posts/fleet-migration/","kind":"page","lang":"en","lastmod":"2025-02-26T00:00:00Z","objectID":"4e91ca008cc43c4c2ac1af72adb5d65c","publishDate":"2025-02-26T00:00:00Z","section":"posts","tags":[],"title":"the migration that made us curious again","type":"posts"}]