{"id":33233,"date":"2026-01-12T14:30:30","date_gmt":"2026-01-12T09:00:30","guid":{"rendered":"https:\/\/www.hexnode.com\/blogs\/?p=33233"},"modified":"2026-01-12T14:45:49","modified_gmt":"2026-01-12T09:15:49","slug":"infrastructure-as-code-provisioning-kiosk-fleets-via-api","status":"publish","type":"post","link":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/","title":{"rendered":"Infrastructure as Code: Provisioning Kiosk Fleets via API"},"content":{"rendered":"<p>In the modern enterprise, &#8220;Manual Provisioning&#8221; is a liability.<\/p>\n<p>We have normalized Infrastructure as Code (IaC) for the cloud\u2014using Terraform or Ansible to spin up thousands of servers with a single commit. Yet, when it comes to Physical Endpoint Management, many organizations remain stuck in &#8220;ClickOps.&#8221; Admins log into a GUI, manually click through wizards, and hope they didn&#8217;t miss a checkbox.<\/p>\n<p>For a fleet of 10,000 retail kiosks or logistics tablets, this manual approach guarantees configuration drift, security gaps, and slow rollout times.<\/p>\n<p>It is time to treat your Kiosk Fleet like a Kubernetes Cluster.<\/p>\n<p>This guide details how to leverage the Hexnode API to architect a true IaC pipeline for physical devices. We will move beyond simple automation to build a resilient, audit-ready provisioning system that creates locations, enforces policies, and manages state without a single UI interaction.<\/p>\n<p><center>    \t\t<!-- button style scb20be917a3efc78059cf9961ee4e54284 -->\r\n    \t\t<style>\r\n    \t\t\t.scb20be917a3efc78059cf9961ee4e54284, a.scb20be917a3efc78059cf9961ee4e54284{\r\n    \t\t\t\tcolor: #fff;\r\n    \t\t\t\tbackground-color: #00868B;\r\n    \t\t\t}\r\n    \t\t\t.scb20be917a3efc78059cf9961ee4e54284:hover, a.scb20be917a3efc78059cf9961ee4e54284:hover{\r\n    \t\t\t\t    \t\t\t\tbackground-color: #32b8bd;\r\n    \t\t\t}\r\n    \t\t<\/style>\r\n    \t\t<a href=\"https:\/\/www.hexnode.com\/mobile-device-management\/developers\/?utm_source=hexnode_blog_kiosk_infrastructure_as_code&amp;utm_medium=referral&amp;utm_campaign=button\" class=\"ht-shortcodes-button scb20be917a3efc78059cf9961ee4e54284  hn-cta__blogs--inline-button \" id=\"\" style=\"\" >\r\n    \t\tBrowse the Hexnode Developer Hub<\/a>\r\n    \t\t<\/center>    \t\t<div class=\"hts-messages hts-messages--alert  hts-messages--withtitle  \"   >\r\n    \t\t\t<span class=\"hts-messages__title\">\ud83d\udca1 What is Infrastructure as Code (IaC)?<\/span>    \t\t\t    \t\t\t\t<p>\r\n    \t\t\t\t\tThink of IaC as a &#8220;Digital Blueprint&#8221; for your devices. Instead of manually clicking buttons in a portal to set up each kiosk, you write a simple text file that describes exactly how you want your devices to look and behave.<br \/>\nWhy it matters:<\/p>\n<ul>\n<li><strong>Speed:<\/strong> Configure 1,000 kiosks as fast as you can configure one.<\/li>\n<li><strong>Consistency:<\/strong> Every device gets the exact same settings\u2014zero human error.<\/li>\n<li><strong>History:<\/strong> Since it\u2019s a file, you can see exactly who changed a setting and when.<\/li>\n<\/ul>\n<p>    \t\t\t\t<\/p>\r\n    \t\t\t    \t\t\t\r\n    \t\t<\/div><!-- \/.ht-shortcodes-messages -->\r\n    \t\t<\/p>\n<h2>Why MDM Architects are Abandoning &#8220;ClickOps&#8221;<\/h2>\n<p>Before we dive into the code, let\u2019s look at why the industry is shifting from manual console management (ClickOps) to Infrastructure as Code (IaC).<\/p>\n<table style=\"border-collapse: collapse; width: 100%; border: 1px solid #000000;\">\n<tbody>\n<tr style=\"background-color: #e2f0ff; border-style: solid; border-color: #000000;\">\n<td style=\"width: 20%; padding: 10px; border: 1px solid #000000; text-align: left;\"><strong>Feature<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\"><strong>Manual (ClickOps)<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\"><strong>IaC (Hexnode API)<\/strong><\/td>\n<\/tr>\n<tr style=\"border-style: solid; border-color: #000000;\">\n<td style=\"width: 20%; padding: 10px; border: 1px solid #000000; background-color: #e2f0ff; text-align: left;\"><strong>Setup Speed<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">15\u201330 mins per device<\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">&lt; 1 second for the entire fleet<\/td>\n<\/tr>\n<tr style=\"border-style: solid; border-color: #000000;\">\n<td style=\"width: 20%; padding: 10px; border: 1px solid #000000; background-color: #e2f0ff; text-align: left;\"><strong>Audit Trail<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">Fragmented system logs<\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">Git History (Commit Hash &amp; Author)<\/td>\n<\/tr>\n<tr style=\"border-style: solid; border-color: #000000;\">\n<td style=\"width: 20%; padding: 10px; border: 1px solid #000000; background-color: #e2f0ff; text-align: left;\"><strong>Configuration Drift<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">High risk; requires manual audits<\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">Auto-detected and self-remediated<\/td>\n<\/tr>\n<tr style=\"border-style: solid; border-color: #000000;\">\n<td style=\"width: 20%; padding: 10px; border: 1px solid #000000; background-color: #e2f0ff; text-align: left;\"><strong>Consistency<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">Human error-prone<\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">100% bit-identical across 10,000 sites<\/td>\n<\/tr>\n<tr style=\"border-style: solid; border-color: #000000;\">\n<td style=\"width: 20%; padding: 10px; border: 1px solid #000000; background-color: #e2f0ff; text-align: left;\"><strong>Scalability<\/strong><\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">Linear effort (More devices = More work)<\/td>\n<td style=\"width: 40%; padding: 10px; border: 1px solid #000000; text-align: left;\">Exponential (10 devices = 10,000 devices)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>The Strategic Shift: Kiosk as Code<\/h2>\n<p>The core philosophy is simple: The Source of Truth is Git, not the Console.<\/p>\n<p>Instead of configuring a store location inside the Hexnode portal, you define the store&#8217;s &#8220;Desired State&#8221; in a version-controlled JSON or YAML file.<\/p>\n<div class=\"copy-code-download \">\n<pre class=\"lang:default decode:true\">The Enterprise Blueprint (store_manifest.json): \r\nJSON \r\n{ \r\n  \"location_code\": \"NYC-042\", \r\n  \"metadata\": { \r\n    \"contact\": \"ops-nyc@enterprise.com\", \r\n    \"cost_center\": \"CC-90210\" \r\n  }, \r\n  \"target_group\": \"Kiosk-NYC-042-Active\", \r\n  \"compliance_profile\": \"PCI-DSS-Lockdown-v3\", \r\n  \"mandatory_apps\": [ \r\n    {\"id\": \"com.hexnode.pos\", \"version\": \"2.1.0\"}, \r\n    {\"id\": \"com.hexnode.scanner\", \"version\": \"1.4.5\"} \r\n  ] \r\n} \r\n<\/pre>\n<\/div>\n<p>Our provisioning engine reads this manifest and orchestrates the Hexnode API to enforce this state. If the manifest changes, the fleet updates automatically.<\/p>\n<p><center><a href=\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/The-Enterprise-Kiosk-as-Code-Pipeline.png?format=webp\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" class=\"\" src=\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/The-Enterprise-Kiosk-as-Code-Pipeline.png?format=webp\" alt=\"Enterprise Kiosk-as-Code Pipeline\" width=\"682\" height=\"425\" \/><\/a><\/center><center><em>Enterprise Kiosk-as-Code Pipeline<\/em><\/center><\/p>\n<h2>Phase 1: The Architecture &amp; Prerequisites<\/h2>\n<p>To build this pipeline, we need a secure bridge between your Code Repository and the Hexnode Infrastructure.<br \/>\nPrerequisites:<\/p>\n<ol>\n<li>Hexnode Enterprise Account with API access.<\/li>\n<li>ervice Account API Key: Do not use a personal admin key. Generate a dedicated API key with scoped permissions (Write: Policies, Write: Groups, Read: Devices).<\/li>\n<li>CI\/CD Runner: A secure environment (Jenkins, GitHub Actions, GitLab CI) to execute the Python logic.<\/li>\n<\/ol>\n<div class=\"next_blog\"><div class=\"post-next\"><div class=\"hex_blog_box_parent\"><div class=\"blog_warp_next\"><div class=\"next_blog_thumb\" style=\"background-image:url(https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2025\/11\/Kiosk-Management-2-scaled.png?format=webp)\"><\/div><div class=\"next_post_content\"><div class=\"center_box\"><h4>The Definitive Guide to Kiosk Management and Strategy<\/h4><p>Master the fundamentals first. Explore the Definitive Guide to Kiosk Management to align your technical strategy.<\/p><\/div><\/div><\/div><a class=\"hex_blog_box_link hn-cta__blogs--blog-box\" href=\"https:\/\/www.hexnode.com\/blogs\/the-definitive-guide-to-kiosk-management-and-strategy-2025-edition\/?utm_source=hexnode_blog_kiosk_infrastructure_as_code&utm_medium=referral&utm_campaign=blog_box\" aria-label=\"The Definitive Guide to Kiosk Management and Strategy\"><\/a><\/div><\/div><\/div>\n<h2>Phase 2: The Idempotent Provisioning Engine<\/h2>\n<p>In an enterprise environment, scripts must be idempotent. Running the script 100 times should result in the same state as running it once, without creating duplicate groups or errors.<\/p>\n<p>Before running the engine, we must define our Desired State. This is the &#8220;Code&#8221; in Infrastructure as Code.<\/p>\n<h3>1. The Manifest &amp; Environment Setup<\/h3>\n<p>Create a file named store_manifest.json. This acts as your configuration file for a specific retail site or fleet branch.<\/p>\n<div class=\"copy-code-download \">\n<pre class=\"lang:default decode:true\">The Manifest (store_manifest.json): \r\n{ \r\n    \"location_code\": \"NYC-042\", \r\n    \"target_group\": \"Kiosk-NYC-042-Active\", \r\n    \"policy_id\": \"1024\" \r\n} \r\n<\/pre>\n<\/div>\n<h3>Environment Variables<\/h3>\n<p>For security, do not hardcode your credentials. Set the following environment variables on your local machine or within your CI\/CD runner (e.g., GitHub Secrets):<\/p>\n<p><code>HEXNODE_API_KEY:<\/code> Your secret API Key from the Hexnode Portal.<\/p>\n<p><code>HEXNODE_PORTAL_URL:<\/code> Your portal address (e.g., company.hexnodemdm.com).<\/p>\n<h3>3. The Provisioning Script<\/h3>\n<p>Here is the logic for a robust provision_store.py script:<\/p>\n<div class=\"copy-code-download \">\n<pre class=\"lang:default decode:true\">import requests import json import sys import os  \r\n\r\n# Configuration: Load from Environment Variables for Security \r\n\r\nAPI_KEY = os.getenv(\"HEXNODE_API_KEY\") PORTAL_URL = os.getenv(\"HEXNODE_PORTAL_URL\") # .hexnodemdm.com \r\n\r\nHEADERS = { \"Authorization\": API_KEY, \"Content-Type\": \"application\/json\" } \r\n\r\ndef get_existing_group_id(group_name): \"\"\" Check if group exists by iterating through all paginated API results. \"\"\" # Start with the initial URL url = f\"https:\/\/{PORTAL_URL}\/api\/v1\/device_groups\/\" \r\n\r\nwhile url: \r\n    response = requests.get(url, headers=HEADERS) \r\n     \r\n    if response.status_code != 200: \r\n        print(f\"Error fetching groups: {response.status_code}\") \r\n        break \r\n         \r\n    data = response.json() \r\n    groups = data.get('results', []) \r\n     \r\n    # Check current page for the group \r\n    for group in groups: \r\n        if group['groupname'] == group_name: \r\n            return group['id'] \r\n    # Update url to the 'next' page link; if it's None, the loop ends \r\n    url = data.get('next') \r\n     \r\nreturn None \r\n  \r\n\r\ndef create_device_group(group_name): \"\"\"Creates a group only if it doesn't exist.\"\"\" existing_id = get_existing_group_id(group_name) if existing_id: print(f\"\ud83d\udd39 Idempotency Check: Group '{group_name}' already exists (ID: {existing_id}).\") return existing_id  \r\n\r\nurl = f\"https:\/\/{PORTAL_URL}\/api\/v1\/device_groups\/\"  \r\npayload = {  \r\n    \"groupname\": group_name,  \r\n    \"description\": \"Provisioned via IaC Pipeline [DO NOT EDIT MANUALLY]\"  \r\n}  \r\nresponse = requests.post(url, headers=HEADERS, json=payload)  \r\nif response.status_code == 201:  \r\n    group_id = response.json().get('id')  \r\n    print(f\"\u2705 Created New Group: {group_name} (ID: {group_id})\")  \r\n    return group_id  \r\nelse:  \r\n    print(f\"\u274c Critical Error: Failed to create group. {response.text}\")  \r\n    sys.exit(1)  \r\n  \r\n\r\ndef enforce_policy(policy_id, group_id): \"\"\" Associates a pre-existing policy to a specific device group. Ensures the 'Desired State' is mapped correctly. \"\"\" \r\n\r\n url = f\"https:\/\/{PORTAL_URL}\/api\/v1\/actions\/associate_policy\/\" # Payload requires IDs in list format payload = [{ \"policies\": [policy_id], \"devicegroups\": [group_id] }] response = requests.post(url, headers=HEADERS, json=payload) if response.status_code == 200: print(f\"\u2705 State Enforced: Policy {policy_id} mapped to Group {group_id}\") else: print(f\"\u274c Mapping Error: {response.status_code} - {response.text}\") \r\n\r\ndef main(): # Load Manifest with open(\"store_manifest.json\") as f: config = json.load(f)  \r\n\r\nprint(f\"\ud83d\ude80 Initializing Provisioning for: {config['location_code']}\")  \r\n  \r\n# 1. State Enforcement: Device Group  \r\ngroup_id = create_device_group(config['target_group'])  \r\n  \r\n# 2. State Enforcement: Security Policy  \r\npolicy_id = config['policy_id'] \r\nenforce_policy(policy_id, group_id)  \r\n  \r\nprint(\"\ud83c\udf89 Infrastructure State Enforced Successfully.\")  \r\n  \r\n\r\nif name == \"main\": main() \r\n<\/pre>\n<\/div>\n    \t\t<div class=\"hts-messages hts-messages--alert  hts-messages--withtitle hts-messages--withicon \"   >\r\n    \t\t\t<span class=\"hts-messages__title\">\u26a0\ufe0f Note on policy management: <\/span>    \t\t\t    \t\t\t\t<p>\r\n    \t\t\t\t\tFor a true IaC workflow, the Policy ID must exist in the Hexnode portal before the script runs. We recommend creating your &#8220;Golden Master&#8221; Kiosk Policy in the UI once (e.g., PCI-Compliance-Kiosk-v1) and storing its ID in your store_manifest.json. This ensures your automation is connecting verified security profiles rather than creating redundant, unmanaged policies for every site.     \t\t\t\t<\/p>\r\n    \t\t\t    \t\t\t\r\n    \t\t<\/div><!-- \/.ht-shortcodes-messages -->\r\n    \t\t\n<h2>The &#8220;State File&#8221; &#8211; Your infrastructure\u2019s memory<\/h2>\n<p>In the world of Terraform, State is the source of truth that tracks what you actually deployed. Without a state file, your script is blind to what it did yesterday.<br \/>\nTo turn our Python engine into a professional IaC tool, we implement a local_state.json. This file records the mapping between your human-readable manifest and the unique IDs returned by the Hexnode API.<\/p>\n<p>Why State Matters:<\/p>\n<ul>\n<li><strong>Speed:<\/strong> The script doesn&#8217;t have to query the API to &#8220;find&#8221; IDs every time; it looks at the local state first.<\/li>\n<li><strong>De-provisioning:<\/strong> If you remove a store from your manifest, the state file tells the script exactly which Hexnode Group ID needs to be deleted.<\/li>\n<li><strong>Drift Detection:<\/strong> It allows you to compare the Desired State (Manifest) vs. Last Known State (Local) vs. Live Reality (Hexnode API).<\/li>\n<\/ul>\n<p>How the code evolves:<\/p>\n<div class=\"copy-code-download \">\n<pre class=\"lang:default decode:true\">Python \r\ndef update_local_state(location_code, group_id, policy_id): \r\n    \"\"\" \r\n    Saves the successful deployment metadata to a local state file. \r\n    \"\"\" \r\n    state_file = \"fleet_state.json\" \r\n    # Load existing state or create new \r\n    if os.path.exists(state_file): \r\n        with open(state_file, 'r') as f: \r\n            state = json.load(f) \r\n    else: \r\n        state = {} \r\n    # Update state for this specific location \r\n    state[location_code] = { \r\n        \"hexnode_group_id\": group_id, \r\n        \"enforced_policy_id\": policy_id, \r\n        \"last_synced\": \"2026-01-07T15:30:00Z\" \r\n    } \r\n    with open(state_file, 'w') as f: \r\n        json.dump(state, f, indent=4) \r\n    print(f\"\ud83d\udcbe State updated for {location_code}\") \r\n<\/pre>\n<\/div>\n<h2>Phase 3: The Enterprise Strategy (Day 2 Operations)<\/h2>\n<p>Writing the script is Step 1. Running it at scale requires a strategy for Governance and Observability.<\/p>\n<h3>1. Drift Detection (The &#8220;Cron&#8221; Job)<\/h3>\n<p>The biggest risk in IT is &#8220;Shadow Configuration&#8221;\u2014a local admin manually changing a policy to troubleshoot an issue and forgetting to revert it.<\/p>\n<ul>\n<li>The Fix: Run a &#8220;Read-Only&#8221; version of your script nightly.<\/li>\n<li><strong>The Logic:<\/strong> Fetch the live configuration from Hexnode and compare it against store_manifest.json.<\/li>\n<li><strong>The Action:<\/strong> If they differ, trigger a PagerDuty alert or auto-remediate the drift by overwriting the manual change.<\/li>\n<\/ul>\n<h3>2. Security &amp; Secret Management<\/h3>\n<p>Never hardcode API keys.<\/p>\n<ul>\n<li><strong>Vault Integration:<\/strong> Inject HEXNODE_API_KEY at runtime using HashiCorp Vault, AWS Secrets Manager, or GitHub Secrets.<\/li>\n<li><strong>IP Allowlisting:<\/strong> Configure Hexnode to accept API calls only from the static IP of your CI\/CD NAT Gateway.<\/li>\n<\/ul>\n<h3>3. Audit &amp; Compliance (SOC 2 \/ PCI-DSS)<\/h3>\n<p>By moving to IaC, you gain a perfect audit trail for free.<\/p>\n<ul>\n<li>The &#8220;Who&#8221;: Git Commit Author.<\/li>\n<li>The &#8220;When&#8221;: Git Commit Timestamp.<\/li>\n<li>The &#8220;What&#8221;: The Git Diff shows exactly which policy was changed.<\/li>\n<li>The &#8220;Why&#8221;: The Pull Request description.<\/li>\n<\/ul>\n<p>When an auditor asks, &#8220;Who changed the Kiosk Lockdown Policy on Tuesday?&#8221;, you don&#8217;t look at logs; you look at the Git History.<\/p>\n<h2>Phase 4: Zero-Touch Enrollment via API<\/h2>\n<p>Configuring a group and a policy is only half the battle. The true power of IaC is realized when a device is taken out of the box, powered on, and automatically finds its &#8220;Desired State&#8221; without an admin ever touching the screen.<\/p>\n<h3>Pre-Enrollment Configuration<\/h3>\n<p>Instead of waiting for devices to check in, we use the Hexnode API to &#8220;Pre-Provision&#8221; them. By fetching Apple DEP or Android Zero-Touch profiles via the API, you can map serial numbers to your IaC-created groups before the hardware even leaves the warehouse.<\/p>\n<p>Enterprise Workflow:<\/p>\n<ul>\n<li>Code: Define the store in store_manifest.json.<\/li>\n<li>API: Script creates the Hexnode Group and Policy mapping.<\/li>\n<li>Enrollment: Script triggers a POST \/api\/v1\/enrollment\/ request to generate a dynamic enrollment URL or binds the device&#8217;s Serial Number to the new Group ID.<\/li>\n<\/ul>\n<h3>The Enrollment API Logic<\/h3>\n<p>You can automate the generation of enrollment requests so that store managers receive an automated &#8220;Setup&#8221; email or SMS the moment your script finishes running.<\/p>\n<div class=\"copy-code-download \">\n<pre class=\"lang:default decode:true\">def trigger_enrollment(email, group_id): \r\n    \"\"\" \r\n    Sends an automated enrollment request to the store manager. \r\n    \"\"\" \r\n    url = f\"https:\/\/{PORTAL_URL}\/api\/v1\/enrollment\/email\/\" \r\n    payload = { \r\n        \"email\": email, \r\n        \"device_group\": group_id \r\n    } \r\n    response = requests.post(url, headers=HEADERS, json=payload) \r\n    if response.status_code == 200: \r\n        print(f\"\ud83d\udce9 Enrollment request dispatched to {email}\") \r\n<\/pre>\n<\/div>\n<section id='resource-single'>\n                    <div class='resource-box'>\n                        <div class='resource-box__image-section'>\n                            <div class='resource-box__image-wrap'>\n                                <img decoding=\"async\" src=\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/The-Ultimate-Guide-to-Kiosk-Management-Everything-your-business-needs-to-know_Thumbnails-for-white-papers-e1767860909196.png?format=webp\" class=\"resource-box__image\" alt=\"The Ultimate Guide to Kiosk Management: Everything your business needs to know\" loading=\"lazy\" srcset=\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/The-Ultimate-Guide-to-Kiosk-Management-Everything-your-business-needs-to-know_Thumbnails-for-white-papers-e1767860909196-504x350.png?format=webp 504w, https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/The-Ultimate-Guide-to-Kiosk-Management-Everything-your-business-needs-to-know_Thumbnails-for-white-papers-e1767860909196-226x300.png?format=webp 226w, https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/The-Ultimate-Guide-to-Kiosk-Management-Everything-your-business-needs-to-know_Thumbnails-for-white-papers-e1767860909196-75x100.png?format=webp 75w\" sizes=\"auto, (max-width: 504px) 100vw, 504px\" title=\"The-Ultimate-Guide-to-Kiosk-Management-Everything-your-business-needs-to-know_Thumbnails-for-white-papers\" \/>\n                            <\/div>\n                        <\/div>\n                        <div class='resource-box__content-section'>\n                            <h5 class='resource-box__content-subheading'>\n                            Featured Resource\n                            <\/h5>\n                            <h4 class='resource-box__content-heading'>\n                            The Ultimate Guide to Kiosk Management: Everything your business needs to know\n                            <\/h4>\n                            <p class='resource-box__contents'>\n                            Ready to scale globally? Access our comprehensive guide on security, compliance, and lifecycle management for enterprise architects.\n                            <\/p>\n                            <a class='resource-box__content-link hn-cta__blogs--resource-box' href='https:\/\/www.hexnode.com\/resources\/white-papers\/the-ultimate-guide-to-kiosk-management-everything-your-business-needs-to-know\/?resource=MTI3NA==&datacount=1&utm_source=hexnode_blog_kiosk_infrastructure_as_code&utm_medium=referral&utm_campaign=resource_box'>\n                            Download the White Paper!\n                            <svg xmlns='http:\/\/www.w3.org\/2000\/svg' width='20' height='20' viewBox='0 0 20 20'>\n                            <g id='arrow' transform='translate(-309 -191)' opacity='0'>\n                                <rect id='base' width='20' height='20' transform='translate(309 191)' fill='none'\/>\n                                <path id='arrow-2' data-name='arrow' d='M13.093.5,6.8,6.8.5.5' transform='translate(315 207.594) rotate(-90)' fill='none' stroke='#0549d1' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.2'\/>\n                            <\/g>\n                            <\/svg>\n\n                            <\/a>\n                        <\/div>\n                    <\/div>\n                <\/section>\n<h2>Conclusion: From Admin to Architect<\/h2>\n<p>Transitioning to Infrastructure as Code elevates the role of the Endpoint Administrator. You are no longer a &#8220;ticket clicker&#8221; but a Platform Architect.<\/p>\n<p>By adopting this Hexnode API-driven workflow, you ensure that 1,000 stores look exactly like 1 store. You eliminate human error, secure your configuration chain, and gain the agility to deploy complex fleets in minutes, not months.<\/p>\n<p>Stop clicking. Start coding.<\/p>\n<div class=\"signup_box\"><div class=\"signup_wrap_img\"><div class=\"signup-bg\" style=\"background-image:url(https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2025\/12\/Designing-the-Perfect-Android-Kiosk-UI-Branding-1.jpg?format=webp)\"><\/div><\/div><div class=\"signup_wrap\"><h5>Kill Configuration Drift for Good.<\/h5><p>Transition from ClickOps to IaC with our expert deep dives. Start your 14-day Hexnode free trial now.<\/p><a href=\"https:\/\/www.hexnode.com\/mobile-device-management\/cloud\/signup\/?utm_source=hexnode_blog_kiosk_infrastructure_as_code&utm_medium=referral&utm_campaign=trial_sign_up_box \" class=\"hn-cta__blogs--signup-stripe\" target=\"_blank\"> SIGNUP NOW!<\/a><\/div><\/div>\n<h2>\u2753 Frequently Asked Questions (FAQs)<\/h2>\n<p><strong>\ud83d\udccd Can I manage MDM using Infrastructure as Code (IaC)?<\/strong><\/p>\n<p style=\"padding-left: 40px;\">Yes. By leveraging the Hexnode REST API, IT teams can manage physical device fleets using IaC principles. Instead of manual console configuration, administrators define device groups, policies, and app assignments in code (JSON\/YAML) and use scripts to automatically provision and enforce these states across thousands of devices.<\/p>\n<p><strong>\ud83d\udccdHow do I automate Kiosk provisioning with Python?<\/strong><\/p>\n<p style=\"padding-left: 40px;\">You can automate Kiosk provisioning by writing a Python script that interacts with the Hexnode API.<br \/>\nThe script should:<\/p>\n<ul>\n<li style=\"list-style-type: none;\">\n<ul>\n<li>Create a Device Group for the new location.<\/li>\n<li>Assign Policies (Lockdown profiles) to that group.<\/li>\n<li>Trigger Enrollment, ensuring that when devices activate, they automatically inherit the code-defined configuration without human intervention.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In the modern enterprise, &#8220;Manual Provisioning&#8221; is a liability. We have normalized Infrastructure as Code&#8230;<\/p>\n","protected":false},"author":63,"featured_media":33355,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2021,263],"tags":[4835,4862],"class_list":["post-33233","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices","category-technical-deep-dives","tag-scripting","tag-kiosk-management","tab_group-how-tos"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.6 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Infrastructure as Code: Provisioning Kiosk Fleets via API<\/title>\n<meta name=\"description\" content=\"Manage kiosks like servers. Use Infrastructure as Code (IaC) and the Hexnode API to provision and manage retail fleets automatically.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Infrastructure as Code: Provisioning Kiosk Fleets via API\" \/>\n<meta property=\"og:description\" content=\"Manage kiosks like servers. Use Infrastructure as Code (IaC) and the Hexnode API to provision and manage retail fleets automatically.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/\" \/>\n<meta property=\"og:site_name\" content=\"Hexnode Blogs\" \/>\n<meta property=\"article:published_time\" content=\"2026-01-12T09:00:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-01-12T09:15:49+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp\" \/>\n\t<meta property=\"og:image:width\" content=\"1340\" \/>\n\t<meta property=\"og:image:height\" content=\"700\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Aurelia Clark\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Aurelia Clark\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/\",\"url\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/\",\"name\":\"Infrastructure as Code: Provisioning Kiosk Fleets via API\",\"isPartOf\":{\"@id\":\"https:\/\/www.hexnode.com\/blogs\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp\",\"datePublished\":\"2026-01-12T09:00:30+00:00\",\"dateModified\":\"2026-01-12T09:15:49+00:00\",\"author\":{\"@id\":\"https:\/\/www.hexnode.com\/blogs\/#\/schema\/person\/5a68119aee27bd1b35c6cccbc88bbd4f\"},\"description\":\"Manage kiosks like servers. Use Infrastructure as Code (IaC) and the Hexnode API to provision and manage retail fleets automatically.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#primaryimage\",\"url\":\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp\",\"contentUrl\":\"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp\",\"width\":1340,\"height\":700,\"caption\":\"Infrastructure as Code Provisioning Kiosk Fleets via API\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.hexnode.com\/blogs\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Infrastructure as Code: Provisioning Kiosk Fleets via API\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.hexnode.com\/blogs\/#website\",\"url\":\"https:\/\/www.hexnode.com\/blogs\/\",\"name\":\"Hexnode Blogs\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.hexnode.com\/blogs\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.hexnode.com\/blogs\/#\/schema\/person\/5a68119aee27bd1b35c6cccbc88bbd4f\",\"name\":\"Aurelia Clark\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.hexnode.com\/blogs\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/fa5292590b4faa16f1da4203f8671b3523b567220d194a8b8644bfe7707aa8a3?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/fa5292590b4faa16f1da4203f8671b3523b567220d194a8b8644bfe7707aa8a3?s=96&d=mm&r=g\",\"caption\":\"Aurelia Clark\"},\"description\":\"Associate Product Marketer at Hexnode focused on SaaS content marketing. I craft blogs that translate complex device management concepts into content rooted in real IT workflows and product realities.\",\"url\":\"https:\/\/www.hexnode.com\/blogs\/author\/aurelia-clark\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Infrastructure as Code: Provisioning Kiosk Fleets via API","description":"Manage kiosks like servers. Use Infrastructure as Code (IaC) and the Hexnode API to provision and manage retail fleets automatically.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/","og_locale":"en_US","og_type":"article","og_title":"Infrastructure as Code: Provisioning Kiosk Fleets via API","og_description":"Manage kiosks like servers. Use Infrastructure as Code (IaC) and the Hexnode API to provision and manage retail fleets automatically.","og_url":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/","og_site_name":"Hexnode Blogs","article_published_time":"2026-01-12T09:00:30+00:00","article_modified_time":"2026-01-12T09:15:49+00:00","og_image":[{"width":1340,"height":700,"url":"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp","type":"image\/png"}],"author":"Aurelia Clark","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Aurelia Clark","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/","url":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/","name":"Infrastructure as Code: Provisioning Kiosk Fleets via API","isPartOf":{"@id":"https:\/\/www.hexnode.com\/blogs\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#primaryimage"},"image":{"@id":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#primaryimage"},"thumbnailUrl":"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp","datePublished":"2026-01-12T09:00:30+00:00","dateModified":"2026-01-12T09:15:49+00:00","author":{"@id":"https:\/\/www.hexnode.com\/blogs\/#\/schema\/person\/5a68119aee27bd1b35c6cccbc88bbd4f"},"description":"Manage kiosks like servers. Use Infrastructure as Code (IaC) and the Hexnode API to provision and manage retail fleets automatically.","breadcrumb":{"@id":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#primaryimage","url":"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp","contentUrl":"https:\/\/cdn.hexnode.com\/blogs\/wp-content\/uploads\/2026\/01\/Infrastructure-as-Code-Provisioning-Kiosk-Fleets-via-API.png?format=webp","width":1340,"height":700,"caption":"Infrastructure as Code Provisioning Kiosk Fleets via API"},{"@type":"BreadcrumbList","@id":"https:\/\/www.hexnode.com\/blogs\/infrastructure-as-code-provisioning-kiosk-fleets-via-api\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.hexnode.com\/blogs\/"},{"@type":"ListItem","position":2,"name":"Infrastructure as Code: Provisioning Kiosk Fleets via API"}]},{"@type":"WebSite","@id":"https:\/\/www.hexnode.com\/blogs\/#website","url":"https:\/\/www.hexnode.com\/blogs\/","name":"Hexnode Blogs","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.hexnode.com\/blogs\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/www.hexnode.com\/blogs\/#\/schema\/person\/5a68119aee27bd1b35c6cccbc88bbd4f","name":"Aurelia Clark","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.hexnode.com\/blogs\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/fa5292590b4faa16f1da4203f8671b3523b567220d194a8b8644bfe7707aa8a3?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/fa5292590b4faa16f1da4203f8671b3523b567220d194a8b8644bfe7707aa8a3?s=96&d=mm&r=g","caption":"Aurelia Clark"},"description":"Associate Product Marketer at Hexnode focused on SaaS content marketing. I craft blogs that translate complex device management concepts into content rooted in real IT workflows and product realities.","url":"https:\/\/www.hexnode.com\/blogs\/author\/aurelia-clark\/"}]}},"_links":{"self":[{"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/posts\/33233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/comments?post=33233"}],"version-history":[{"count":20,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/posts\/33233\/revisions"}],"predecessor-version":[{"id":33418,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/posts\/33233\/revisions\/33418"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/media\/33355"}],"wp:attachment":[{"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/media?parent=33233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/categories?post=33233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hexnode.com\/blogs\/wp-json\/wp\/v2\/tags?post=33233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}