Blog · Terraform · Beginner · 20 minutes · 21 October 2026
Your first Jamf Pro resource in Terraform
One smart group, start to finish
Most Jamf estates are configured by clicking. Here is the 20-minute version of managing one with Terraform instead, so every change is reviewed, versioned and repeatable.
- Create an API client in Jamf Pro with the minimum permissions
- Write one smart group as code with the Jamf Pro Terraform provider
- Plan, apply, and see the group appear in the console
If you have never used Terraform, this is the post to start with. By the end you will have one smart computer group in Jamf Pro that exists because a file says it should, and you will have seen the plan → apply loop that every later post builds on. Nothing here touches your existing configuration.
Before you start
You need: a Jamf Pro tenant you are allowed to change (a sandbox or trial tenant is ideal), a Mac with Terraform 1.6 or later installed (brew install terraform), and twenty minutes. We use the community Jamf Pro provider maintained by Deployment Theory; check the provider's documentation for the version you install, as resource arguments change between releases.
1. Create an API client in Jamf Pro
Terraform talks to Jamf Pro through the API, and the API wants an API client, not your admin login. Give it the least it needs.
- In Jamf Pro go to Settings → System → API roles and clients.
- Under API Roles, create a role called
terraform-smart-groupswith just two privileges: Create Smart Computer Groups and Read Smart Computer Groups. (Add Update and Delete later when you are ready to manage changes, not before.) - Under API Clients, create a client called
terraform, assign the role, enable it, and generate a client secret. Copy the client ID and secret now; the secret is shown once.
Why a separate role: when something goes wrong in Terraform, the blast radius is exactly the privileges you granted. Start small and widen deliberately.
2. Put the credentials somewhere Terraform can read them, not in the code
Never write the secret into a .tf file; it will end up in Git. Use environment variables for now (a vault later).
export JAMFPRO_INSTANCE_FQDN="yourcompany.jamfcloud.com"
export JAMFPRO_AUTH_METHOD="oauth2"
export JAMFPRO_CLIENT_ID="paste-the-client-id"
export JAMFPRO_CLIENT_SECRET="paste-the-secret"
Put those four lines in a file called .env in your project folder, run source .env, and add .env to .gitignore before you do anything else.
3. Write the provider block
Make a folder, and in it a file called main.tf:
terraform {
required_version = ">= 1.6"
required_providers {
jamfpro = {
source = "deploymenttheory/jamfpro"
version = "~> 0.20"
}
}
}
# Reads JAMFPRO_* from the environment. Nothing secret goes in this file.
provider "jamfpro" {}
Pin the provider version. The ~> 0.20 means "any 0.20.x", so you get fixes but not surprises. Check the current release on the Terraform Registry and use that.
4. Write the smart group
Add a second file, groups.tf. One group: Macs that have not checked in for more than thirty days. It is a useful group to have and a harmless one to create.
resource "jamfpro_smart_computer_group" "stale_30d" {
name = "TF · Stale · No check-in 30 days"
criteria {
name = "Last Check-in"
priority = 0
and_or = "and"
search_type = "more than x days ago"
value = "30"
}
}
Two habits worth starting on day one. The TF · prefix tells anyone in the console that this group is managed by code, so they should not edit it by hand. And the name says what the group is for, not how it is built.
5. Initialise, plan, apply
terraform init # downloads the provider
terraform plan # shows what would change: 1 to add, 0 to change, 0 to destroy
terraform apply # asks for confirmation, then creates the group
Read the plan before you type yes. This is the whole point: Terraform tells you what it will do, in full, before it does it. In the console, go to Computers → Smart Computer Groups and your group is there.
6. Change it, and see the difference
Edit groups.tf and change "30" to "45". Run terraform plan again. You will see one attribute changing, nothing else. That line, the diff, is what you will put in front of a reviewer in a pull request in the next post. (You will need the Update privilege on the API role for the apply to succeed; add it now, deliberately.)
7. What you now have
- A Jamf Pro resource whose source of truth is a file, not a memory
- A
terraform.tfstatefile in your folder. It maps your code to the real group's ID. Do not commit it; the next post moves it to remote storage - The plan → apply habit, which is the same for one smart group and for a whole tenant
The mistake everyone makes once
Creating the group in Terraform, then "just quickly" editing it in the console. The next terraform plan will propose to change it back, and someone will be surprised. The rule is simple: if it has the TF · prefix, the file wins. If you need it different, change the file.
Next
The next post takes an existing smart group, one you made by clicking years ago, and brings it under Terraform with terraform import, without deleting or recreating it. That is the post that makes this useful on a real estate.
Questions about this one, or want it adapted for your estate? [email protected]
The newsletter
New walkthroughs, by email
A short email when a new post goes up: what it covers, how long it takes, and the mistake everyone makes once. At most twice a month. No sales pitch.