Skip to content

macOS & iOS/iPadOS: Rename hosts - #48690

Merged
juan-fdz-hawa merged 3 commits into
mainfrom
38806-macos-iosipados-rename-hosts
Jul 14, 2026
Merged

macOS & iOS/iPadOS: Rename hosts#48690
juan-fdz-hawa merged 3 commits into
mainfrom
38806-macos-iosipados-rename-hosts

Conversation

@juan-fdz-hawa

@juan-fdz-hawa juan-fdz-hawa commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Related issue: Resolves #38806

Add an IT-admin naming convention for macOS/iOS/iPadOS hosts. An admin sets a name template (e.g. "iPad $FLEET_VAR_HOST_HARDWARE_SERIAL") under Controls > OS settings > Host names for a fleet or for "No team"; Fleet resolves it per host, delivers it via an Apple Settings/DeviceName MDM command, renames its own record on ACK, then verifies the name via osquery (macOS) or a DeviceInformation refetch (iOS/iPadOS). Clearing the template stops enforcement without renaming any host. Fleet Premium only, mirroring disk encryption.

sequenceDiagram
    participant Admin
    participant UI
    participant API as Core service
    participant EE as EE service
    participant DB
    participant Cron as Profile-manager cron
    participant Cmdr as MDMAppleCommander
    participant Device as macOS / iOS / iPadOS
    participant Ingest as osquery / DeviceInformation

    note over Admin,DB: 1. Set template
    Admin->>UI: Save "iPad $FLEET_VAR_HOST_HARDWARE_SERIAL"
    UI->>API: POST /api/v1/fleet/host_name_template {fleet_id, name_template}
    API->>API: license + authz (MDMAppleSettingsPayload, ActionWrite)
    API->>API: validate template (vars allow-list, charset)
    API->>EE: EnterpriseOverrides.UpdateTeamMDMHostNameTemplate
    EE->>DB: SaveTeam(config JSON) + edited_host_name_template activity
    EE->>DB: upsert host_mdm_apple_device_names rows (status=NULL) for eligible hosts
    note over Cron,Device: 2. Enforce
    Cron->>DB: rows WHERE status IS NULL (BYOD/personal enrollments never get rows)
    Cron->>Cron: resolve template per host (expand FLEET_VARs)
    Cron->>Cmdr: Settings{DeviceName: resolved} (UUID prefix DEVNAME-)
    Cmdr->>Device: APNs push + command
    DB->>DB: row status='pending', command_uuid, expected_device_name
    Device-->>API: Acknowledged / Error (CommandAndReportResults)
    alt Acknowledged
        API->>DB: hosts.computer_name/hostname = expected + host_display_names
        API->>DB: row status='verifying'
    else Error
        API->>DB: row status='failed', detail=error chain
    end
    note over Ingest,DB: 3. Verify + drift detection
    Ingest-->>API: reported computer_name (osquery) / DeviceName (refetch)
    API->>DB: reported == expected ? 'verified' : 'failed' (drift)
    note over Admin,DB: 4. Lifecycle
    Admin->>API: transfer host / new enrollment / resend
    API->>DB: dest fleet has template ? row reset to NULL (Pending) : row deleted
Loading

Checklist for submitter

If some of the following don't apply, delete the relevant line.

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

Testing

  • Added/updated automated tests
  • QA'd all new/changed functionality manually

Database migrations

  • Checked schema for all modified table for columns that will auto-update timestamps during migration.
  • Ensured the correct collation is explicitly set for character columns (COLLATE utf8mb4_unicode_ci).

New Fleet configuration settings

  • Verified that the setting is exported via fleetctl generate-gitops
  • Verified the setting is documented in a separate PR to the GitOps documentation
  • Verified that the setting is cleared on the server if it is not supplied in a YAML file (or that it is documented as being optional)
  • Verified that any relevant UI is disabled when GitOps mode is enabled

Summary by CodeRabbit

  • New Features
    • Added an admin/maintainer-only Host names control for Apple (macOS, iOS, iPadOS) to enforce host naming via a configurable template.
    • Extended host details OS settings with Host name status (Enforcing/Verifying/Verified/Failed) and a Resend action when eligible.
    • Added GitOps support to set/clear and export the template, including validation and activity logging.
  • Bug Fixes
    • Improved host name template enforcement to stay in sync across enrollment, resets, transfers, and transitions between team-scoped and “No team” settings.

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.75277% with 189 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.99%. Comparing base (1318b3c) to head (e3e0bb3).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
server/datastore/mysql/apple_device_names.go 76.50% 24 Missing and 23 partials ⚠️
server/service/apple_device_names.go 58.57% 18 Missing and 11 partials ⚠️
ee/server/service/teams.go 76.11% 8 Missing and 8 partials ⚠️
server/service/apple_mdm.go 70.00% 8 Missing and 7 partials ⚠️
server/service/mdm.go 79.24% 6 Missing and 5 partials ⚠️
server/mdm/apple/commander.go 76.19% 5 Missing and 5 partials ⚠️
...ttings/cards/HostNameTemplate/HostNameTemplate.tsx 88.23% 8 Missing ⚠️
server/mdm/lifecycle/lifecycle.go 42.85% 4 Missing and 4 partials ⚠️
...es/hosts/details/cards/HostSummary/HostSummary.tsx 53.33% 7 Missing ⚠️
server/datastore/mysql/teams.go 62.50% 3 Missing and 3 partials ⚠️
... and 13 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #48690      +/-   ##
==========================================
- Coverage   68.08%   67.99%   -0.10%     
==========================================
  Files        3784     3805      +21     
  Lines      239276   240633    +1357     
  Branches    12575    12656      +81     
==========================================
+ Hits       162918   163618     +700     
- Misses      61573    62152     +579     
- Partials    14785    14863      +78     
Flag Coverage Δ
backend 69.58% <75.75%> (+0.01%) ⬆️
frontend 59.38% <81.04%> (-0.57%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@juan-fdz-hawa
juan-fdz-hawa force-pushed the 38806-macos-iosipados-rename-hosts branch from 815c4a3 to ce5ebc1 Compare July 6, 2026 12:16
@juan-fdz-hawa juan-fdz-hawa changed the title Added changes file macOS & iOS/iPadOS: Rename hosts Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds Premium host name templates for macOS, iOS, and iPadOS across fleet and No team scopes. Templates support API, GitOps, validation, activity logging, MDM enforcement, verification, failure handling, resend, transfer reconciliation, and persistence. Host details, OS settings summaries, filters, Controls pages, and command palette entries expose the resulting status.

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the requested Apple host-name templates, enforcement, GitOps, UI, status tracking, and tests for #38806.
Out of Scope Changes check ✅ Passed The diff appears focused on host-name templating and related plumbing; no clearly unrelated feature work stands out.
Title check ✅ Passed The title is concise and clearly describes the main change: Apple host renaming for macOS/iOS/iPadOS.
Description check ✅ Passed The description includes the related issue, summary, testing, migrations, and GitOps checks, with only a few noncritical template items omitted.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 38806-macos-iosipados-rename-hosts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@juan-fdz-hawa
juan-fdz-hawa force-pushed the 38806-macos-iosipados-rename-hosts branch from a0fe844 to 0b48eed Compare July 13, 2026 11:50
@juan-fdz-hawa
juan-fdz-hawa force-pushed the 38806-macos-iosipados-rename-hosts branch from ae9760c to 070c04f Compare July 13, 2026 12:11
@juan-fdz-hawa
juan-fdz-hawa marked this pull request as ready for review July 13, 2026 12:20
@juan-fdz-hawa
juan-fdz-hawa requested review from a team as code owners July 13, 2026 12:20

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tests.tsx (1)

137-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicate test case.

This "No-team" test block (lines 137-157) is byte-for-byte identical logic to the earlier test at lines 28-48 (same platforms, same hostNameSetting, same assertions). The row generator is confirmed team-agnostic by the code comment itself, so this second test doesn't exercise any different code path — it re-runs the exact same assertions under a different name. Consider removing it or, if you want to document the team-agnostic behavior, replacing it with a single comment on the original test rather than a full duplicate it.each.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tests.tsx`
around lines 137 - 157, Remove the duplicate No-team it.each test block around
generateTableData; the earlier host-name row test already covers the same
platforms, fixture, and assertions. If needed, document the team-agnostic
behavior with a comment on the original test instead of retaining a second test.
server/service/osquery.go (1)

1408-1412: 🚀 Performance & Scalability | 🔵 Trivial

Logic is correct; consider whether this should run when the feature is unused.

This block issues an UpdateHostDeviceNameStatusFromReport call for every macOS MDM host on each detail update, even in deployments with no host-name template configured on any team or "No team". The datastore call is a no-op for hosts without an enforcement row, but it's still an extra write round-trip on the osquery detail path. If a cheap "any template configured" signal is available (e.g. from ac), gating on it would avoid the call for the common no-feature case.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/service/osquery.go` around lines 1408 - 1412, Add a cheap
feature-usage guard to the block around UpdateHostDeviceNameStatusFromReport,
using the available ac configuration to determine whether any host-name template
is configured for a team or “No team.” Keep the existing detailUpdated, MDM,
macOS, and ComputerName checks, and skip the datastore call entirely when no
template is configured.
server/service/integration_enterprise_test.go (1)

33552-33565: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Verify no partial persistence after the invalid-template spec applies.

The two invalid name_template spec-apply calls (real and dry_run=true) at lines 33556-33558 don't assert getTemplate() is unchanged afterward, unlike the equivalent valid-dry-run block immediately below (line 33562 checks require.Equal(t, tmpl, getTemplate())). Adding the same assertion here is cheap and guards against a validation failure leaving a partial write.

♻️ Suggested addition
 	s.Do("POST", "/api/latest/fleet/spec/fleets",
 		specWith(map[string]any{"name_template": "X-$FLEET_VAR_NOPE"}), http.StatusUnprocessableEntity)
 	s.Do("POST", "/api/latest/fleet/spec/fleets",
 		specWith(map[string]any{"name_template": "X-$FLEET_VAR_NOPE"}), http.StatusUnprocessableEntity, "dry_run", "true")
+	require.Equal(t, tmpl, getTemplate())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/service/integration_enterprise_test.go` around lines 33552 - 33565,
Add assertions after the invalid-template spec-apply calls in the team-spec test
to verify getTemplate() still equals tmpl, covering both the real and dry-run
requests. Use the existing tmpl and getTemplate symbols and preserve the valid
dry-run assertion below.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/datastore/mysql/apple_device_names.go`:
- Around line 343-349: The team name-template SQL path must not treat JSON null
as a valid template. Confirm the declared type and marshaling behavior of
TeamMDM.HostNameTemplate; if it can marshal to null, add the same JSON_TYPE
string guard used by deviceNameNoTeamTemplateExpr to both the team filter here
and reconcileHostDeviceNamesForTeamDB, preserving valid non-empty templates.

In `@server/service/appconfig.go`:
- Around line 1887-1896: Clear mdm.HostNameTemplate during the license-downgrade
handling in server/service/appconfig.go at lines 1887-1896, alongside the
existing Fleet Desktop reset, so Premium values are not retained on Free. The
reconcile logic in server/service/apple_device_names.go at lines 26-46 requires
no direct change because removing the stored template resolves the
stale-template path; preserve its existing MDM.EnabledAndConfigured behavior.

In `@server/service/apple_device_names.go`:
- Around line 85-99: Update the too-long and matching-name branches in the host
processing switch to handle SetHostDeviceNameStatus errors like the default
enqueue branch: log the failure with relevant host context and continue
processing pending hosts without returning from the batch function. Preserve
their existing status updates and notify behavior for successful calls.

In `@server/service/handler.go`:
- Line 847: Add the `/api/_version_/fleet/hosts/{host_id}/name_template/resend`
route to `service.mdmConfigurationRequiredEndpoints` so the
`resendHostNameTemplateEndpoint` registered through `mdmAnyMW` is included in
MDM-not-configured checks.

---

Nitpick comments:
In
`@frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tests.tsx`:
- Around line 137-157: Remove the duplicate No-team it.each test block around
generateTableData; the earlier host-name row test already covers the same
platforms, fixture, and assertions. If needed, document the team-agnostic
behavior with a comment on the original test instead of retaining a second test.

In `@server/service/integration_enterprise_test.go`:
- Around line 33552-33565: Add assertions after the invalid-template spec-apply
calls in the team-spec test to verify getTemplate() still equals tmpl, covering
both the real and dry-run requests. Use the existing tmpl and getTemplate
symbols and preserve the valid dry-run assertion below.

In `@server/service/osquery.go`:
- Around line 1408-1412: Add a cheap feature-usage guard to the block around
UpdateHostDeviceNameStatusFromReport, using the available ac configuration to
determine whether any host-name template is configured for a team or “No team.”
Keep the existing detailUpdated, MDM, macOS, and ComputerName checks, and skip
the datastore call entirely when no template is configured.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 969dfd9b-19f9-4f20-8921-3ecc84cee5a2

📥 Commits

Reviewing files that changed from the base of the PR and between 2299f02 and 070c04f.

📒 Files selected for processing (105)
  • changes/38806-macos-iosipados-rename-hosts
  • cmd/fleet/cron.go
  • cmd/fleetctl/fleetctl/generate_gitops.go
  • cmd/fleetctl/fleetctl/gitops_test.go
  • cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigJson.json
  • cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigTeamMaintainerJson.json
  • cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigTeamMaintainerYaml.yml
  • cmd/fleetctl/fleetctl/testdata/expectedGetConfigAppConfigYaml.yml
  • cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigJson.json
  • cmd/fleetctl/fleetctl/testdata/expectedGetConfigIncludeServerConfigYaml.yml
  • cmd/fleetctl/fleetctl/testdata/expectedGetTeamsJson.json
  • cmd/fleetctl/fleetctl/testdata/expectedGetTeamsYaml.yml
  • cmd/fleetctl/fleetctl/testdata/generateGitops/appConfig.json
  • cmd/fleetctl/fleetctl/testdata/generateGitops/expectedGlobalControls.yaml
  • cmd/fleetctl/fleetctl/testdata/generateGitops/teamConfig.json
  • cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/fleets/team-a-thumbsup.yml
  • cmd/fleetctl/fleetctl/testdata/generateGitops/test_dir_premium/fleets/unassigned.yml
  • cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigEmpty.yml
  • cmd/fleetctl/fleetctl/testdata/macosSetupExpectedAppConfigSet.yml
  • cmd/fleetctl/fleetctl/testdata/macosSetupExpectedTeam1And2Empty.yml
  • cmd/fleetctl/fleetctl/testdata/macosSetupExpectedTeam1And2Set.yml
  • cmd/fleetctl/fleetctl/testdata/macosSetupExpectedTeam1Empty.yml
  • cmd/fleetctl/fleetctl/testdata/macosSetupExpectedTeam1Set.yml
  • cmd/fleetctl/fleetctl/testing_utils/testing_utils.go
  • ee/server/service/service.go
  • ee/server/service/teams.go
  • frontend/components/CommandPalette/CommandPalette.tsx
  • frontend/components/CommandPalette/groups/controls.ts
  • frontend/components/CommandPalette/helpers.tests.ts
  • frontend/components/CommandPalette/helpers.ts
  • frontend/interfaces/config.ts
  • frontend/interfaces/host.ts
  • frontend/interfaces/mdm.ts
  • frontend/interfaces/team.ts
  • frontend/pages/ManageControlsPage/OSSettings/OSSettingsNavItems.tests.tsx
  • frontend/pages/ManageControlsPage/OSSettings/OSSettingsNavItems.tsx
  • frontend/pages/ManageControlsPage/OSSettings/cards/HostNameTemplate/HostNameTemplate.tests.tsx
  • frontend/pages/ManageControlsPage/OSSettings/cards/HostNameTemplate/HostNameTemplate.tsx
  • frontend/pages/ManageControlsPage/OSSettings/cards/HostNameTemplate/_styles.scss
  • frontend/pages/ManageControlsPage/OSSettings/cards/HostNameTemplate/index.ts
  • frontend/pages/hosts/details/HostDetailsPage/HostDetailsPage.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsModal.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingStatusCell/OSSettingStatusCell.tests.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingStatusCell/OSSettingStatusCell.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingStatusCell/helpers.ts
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsResendCell/OSSettingsResendCell.tests.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsResendCell/OSSettingsResendCell.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTable.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tests.tsx
  • frontend/pages/hosts/details/OSSettingsModal/OSSettingsTable/OSSettingsTableConfig.tsx
  • frontend/pages/hosts/details/cards/HostSummary/HostSummary.tests.tsx
  • frontend/pages/hosts/details/cards/HostSummary/HostSummary.tsx
  • frontend/pages/hosts/details/helpers.ts
  • frontend/router/paths.ts
  • frontend/services/entities/host_name_template.ts
  • frontend/services/entities/hosts.ts
  • frontend/utilities/endpoints.ts
  • pkg/spec/gitops.go
  • pkg/spec/gitops_test.go
  • server/datastore/mysql/apple_device_names.go
  • server/datastore/mysql/apple_device_names_test.go
  • server/datastore/mysql/apple_mdm.go
  • server/datastore/mysql/hosts.go
  • server/datastore/mysql/hosts_test.go
  • server/datastore/mysql/labels.go
  • server/datastore/mysql/migrations/tables/20260713115453_CreateHostMDMAppleDeviceNames.go
  • server/datastore/mysql/schema.sql
  • server/datastore/mysql/teams.go
  • server/fleet/activities.go
  • server/fleet/app.go
  • server/fleet/datastore.go
  • server/fleet/hosts.go
  • server/fleet/hosts_test.go
  • server/fleet/mdm.go
  • server/fleet/name_template.go
  • server/fleet/name_template_test.go
  • server/fleet/service.go
  • server/fleet/teams.go
  • server/fleet/teams_test.go
  • server/mdm/apple/commander.go
  • server/mdm/apple/commander_test.go
  • server/mdm/lifecycle/lifecycle.go
  • server/mdm/lifecycle/lifecycle_test.go
  • server/mock/datastore_mock.go
  • server/mock/service/service_mock.go
  • server/service/appconfig.go
  • server/service/appconfig_test.go
  • server/service/apple_device_names.go
  • server/service/apple_mdm.go
  • server/service/apple_mdm_test.go
  • server/service/client.go
  • server/service/handler.go
  • server/service/hosts.go
  • server/service/hosts_test.go
  • server/service/integration_core_test.go
  • server/service/integration_enterprise_test.go
  • server/service/integration_mdm_test.go
  • server/service/mdm.go
  • server/service/mdm_test.go
  • server/service/osquery.go
  • server/service/osquery_test.go
  • tools/cloner-check/generated_files/appconfig.txt
  • tools/cloner-check/generated_files/teamconfig.txt
  • tools/cloner-check/generated_files/teammdm.txt
  • tools/dibble/pkg/seed/activities.go

Comment thread server/datastore/mysql/apple_device_names.go
Comment thread server/service/appconfig.go
Comment thread server/service/apple_device_names.go
Comment thread server/service/handler.go
// POST /hosts/{host_id:[0-9]+}/configuration_profiles/{profile_uuid}/resend endpoint.
mdmAnyMW.POST("/api/_version_/fleet/hosts/{host_id:[0-9]+}/configuration_profiles/resend/{profile_uuid}", resendHostMDMProfileEndpoint, resendHostMDMProfileRequest{})
mdmAnyMW.POST("/api/_version_/fleet/hosts/{host_id:[0-9]+}/configuration_profiles/{profile_uuid}/resend", resendHostMDMProfileEndpoint, resendHostMDMProfileRequest{})
mdmAnyMW.POST("/api/_version_/fleet/hosts/{host_id:[0-9]+}/name_template/resend", resendHostNameTemplateEndpoint, resendHostNameTemplateRequest{})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the name_template/resend path is registered in mdmConfigurationRequiredEndpoints.
rg -nP --type=go -C2 'mdmConfigurationRequiredEndpoints' server/service
rg -nP --type=go 'name_template/resend' server/service

Repository: fleetdm/fleet

Length of output: 2690


🏁 Script executed:

#!/bin/bash
sed -n '806,940p' server/service/testing_utils_test.go | cat -n

Repository: fleetdm/fleet

Length of output: 7147


Add name_template/resend to mdmConfigurationRequiredEndpoints. This MDM endpoint is registered under mdmAnyMW, but it’s missing from service.mdmConfigurationRequiredEndpoints, so it won’t be covered by the MDM-not-configured checks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/service/handler.go` at line 847, Add the
`/api/_version_/fleet/hosts/{host_id}/name_template/resend` route to
`service.mdmConfigurationRequiredEndpoints` so the
`resendHostNameTemplateEndpoint` registered through `mdmAnyMW` is included in
MDM-not-configured checks.

// distinguishes it from a company-owned device.
const deviceNameEligibleHostsJoins = `
FROM hosts h
JOIN nano_enrollments ne ON ne.device_id = h.uuid

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is minor but you can likely just do ne.id=h.uuid. For all devices there's a row with id=uuid and device_id=uuid, then there are for some devices user-enrollment rows with ne.device_id=uuid and id=uuid+":"+[user uuid]. So I think using the id directly might be a small DB optimization, and lessen the chance the wrong enrollment is grabbed if deviceNameEligibleHostsWhere changes(right now it's 'Device' filter on type should do that)

// SendNotifications afterwards. This lets a bulk sender enqueue one command per
// host and then wake every device with a single batched push instead of one
// APNs request per host.
func (svc *MDMAppleCommander) DeviceNameSettingWithoutNotifications(ctx context.Context, hostUUID, cmdUUID, deviceName string) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this pattern a lot and think we might need to consider doing it elsewhere. Or having some process that batches up pushes for most bulk commands. Nicely done

teamFilter, args := deviceNameTeamScope(teamID)

stmt := `
INSERT INTO host_mdm_apple_device_names (host_uuid, status)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One optimization that might be worth making here is setting any existing enqueued commands for pending rows to active=0 in nano_enrollment_queue in the process of doing this. Reason for this is that we attempt to execute commands in order but there's no guarantee that commands get executed in order.

If you can imagine the case of a NotNow command, when we get that response we go and find the next non-NotNow command to handle the device. The device may still be in the state generating the NotNows when it gets that, or it may not(e.g. when a user's logging in at the login window it'll return NotNow for certain commands until they get their password entered all the way but once they hit the desktop it starts responding normally). Without clearing any pending NotNow commands there's a chance the new one with the updated name gets executed before the old pending one in this sort of scenario and device gets the wrong name.

IIRC we try to do this for certain profile reconciliation cases for this reason

Settings is not on Apple's list of commands that don't return NotNow so I suspect htis is a possibility though a small one

Resolves #38806

Add an IT-admin naming convention for macOS/iOS/iPadOS hosts. An admin
sets a name template (e.g. "iPad $FLEET_VAR_HOST_HARDWARE_SERIAL") under
Controls > OS settings > Host names for a fleet or for "No team"; Fleet
resolves it per host, delivers it via an Apple `Settings`/`DeviceName`
MDM command, renames its own record on ACK, then verifies the name via
osquery (macOS) or a DeviceInformation refetch (iOS/iPadOS). Clearing
the template stops enforcement without renaming any host. Fleet Premium
only, mirroring disk encryption.

Backend:
- New `host_mdm_apple_device_names` enforcement table (status/command_uuid/
expected_device_name/detail) with a NULL-status = queued state machine,
plus datastore methods for upsert/delete/list/set/verify/resend/reconcile.
- `name_template` config key on TeamMDM, TeamSpecMDM, TeamPayloadMDM, and
the global AppConfig.MDM (for "No team"); template validation (built-in
vars only, no secrets, charset, 255-char and 63-byte limits) and a
per-host resolver; `edited_host_name_template` activity.
- `POST /api/v1/fleet/host_name_template` (fleet_id omitted/0 targets
"No team") and `POST /api/v1/fleet/hosts/{id}/name_template/resend`.
- `Settings`/`DeviceName` commander builder with a `DEVNAME-` command
prefix and batched APNs push from the profile-manager cron.
- Command-result handler (ACK -> rename + verifying; error -> failed),
drift detection at both ingestion sites with a 60s grace window, and
enforcement reconciliation on team transfer and MDM enrollment.
- GitOps `controls.name_template` for fleets and "No team", including
generate-gitops round-tripping.
- Eligibility excludes non-MDM, BYOD/personal, and account-driven user
enrollments; ineligible hosts get no row (recovery-lock precedent).

Frontend:
- Controls > OS settings "Host names" card (fleet + No team), premium
gate, MDM-not-configured empty state, and GitOps-mode disabled treatment.
- Synthetic "Host name" row in the host details OS settings modal for
darwin/ios/ipados with statuses and a resend button.
- Command-palette entry; host name statuses fold into the OS-settings
aggregate cards and os_settings host-list filter.
@juan-fdz-hawa
juan-fdz-hawa force-pushed the 38806-macos-iosipados-rename-hosts branch from 070c04f to 24b35ca Compare July 14, 2026 11:28
@juan-fdz-hawa
juan-fdz-hawa merged commit 82db4d5 into main Jul 14, 2026
53 checks passed
@juan-fdz-hawa
juan-fdz-hawa deleted the 38806-macos-iosipados-rename-hosts branch July 14, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

macOS & iOS/iPadOS: Rename hosts

3 participants