Device posture begins as a small configuration task.

Create a check for disk encryption, operating-system version or endpoint compliance, then add it to an Access policy.

At enterprise scale, posture becomes a configuration system of its own:

  • checks differ by operating system;
  • some signals come from the Cloudflare One Client;
  • others come from endpoint-management or security integrations;
  • policies depend on posture object identifiers;
  • test and production environments have different IDs;
  • a badly timed deletion can affect several applications or Gateway policies.

That is the point where I want device posture managed as code.

A device posture as code workflow from intent and compilation through deployment and observation

Posture is a reusable signal

A posture check should describe one meaningful assertion about a device.

Examples include:

  • the operating system meets a minimum version;
  • storage encryption is enabled;
  • the local firewall is enabled;
  • the device is joined to an approved domain;
  • an approved client certificate is present;
  • the endpoint is compliant in an integrated management platform;
  • a supported endpoint-security provider reports a healthy state;
  • the device is enrolled in the organisation’s Cloudflare configuration.

The check is not the access decision by itself. Access and Gateway policies combine posture with identity, application and network context.

Keeping checks reusable prevents every application team from inventing a slightly different definition of a healthy Windows device.

Describe intent with stable logical keys

Cloudflare assigns object IDs. Those IDs are necessary for API calls and policy references, but they are poor authoring identifiers.

I prefer a source model with stable logical keys:

postureChecks:
  - key: windows-disk-encryption
    name: Windows - disk encryption required
    type: disk_encryption
    platforms:
      - windows
    schedule: 5m
    input:
      requireAll: true

  - key: windows-minimum-version
    name: Windows - supported minimum version
    type: os_version
    platforms:
      - windows
    input:
      operator: ">="
      version: "10.0.22631"

This is an authoring format, not a direct copy of an API response.

The deployment layer translates it into the exact Cloudflare payload and resolves environment-specific dependencies.

Separate logical identity from platform identity

The important mapping is:

windows-disk-encryption

Test object ID
Development object ID
Production object ID

Policies should refer to the logical key in source control. The compiler or deployment layer resolves that key to the correct object ID for the target environment.

That avoids copying a production UUID into a development definition and makes environment promotion deterministic.

Use a read, compare and apply workflow

The deployment sequence should be familiar:

Load desired posture definitions

Read deployed posture checks

Match by managed logical key or metadata

Normalise current and desired values

Create a change plan

Preview or apply

Read back and verify

The plan should classify each check as:

  • no change;
  • create;
  • update in place;
  • replace;
  • disable;
  • remove.

Removal should be a separate, explicit operation because posture checks may have policy dependencies.

Compare semantically, not as raw JSON

Cloudflare can return computed fields, defaults and normalised values that were not present in the source definition.

A reliable comparison layer should:

  • ignore read-only fields;
  • sort collections where order is not meaningful;
  • normalise platform names and operators;
  • account for provider defaults;
  • distinguish absent values from intentional false values;
  • preserve fields the automation does not own.

Comparing the source file directly with an API response string creates permanent drift and repeated updates.

The automation needs an ownership model: which fields does this repository control, and which fields are provider-generated or managed elsewhere?

Validate the complete dependency graph

A posture check can be syntactically valid and still break access if a policy uses it incorrectly.

Before deployment, validate:

  • every policy posture key resolves in the target environment;
  • the check supports the intended operating system;
  • required service-provider integrations exist;
  • integration IDs map to the correct environment;
  • no check scheduled for removal is still referenced;
  • replacement preserves policy dependencies;
  • names and logical keys are unique;
  • polling and result-expiry values are operationally realistic.

The source of truth should fail closed when a dependency cannot be resolved.

Treat third-party integrations as separate resources

Endpoint-management and endpoint-security integrations have their own credentials, permissions and polling behaviour.

Do not place integration secrets in the posture definition.

Model only the logical dependency:

integration: endpoint-compliance-primary

Resolve that name to the environment’s Cloudflare integration ID during deployment. Store the integration credential in the approved secret platform and scope it to the minimum required access.

The posture rule and the integration also have different lifecycles. Replacing a credential should not require rewriting every posture definition that consumes the integration.

Build safety around update and deletion

A safe posture deployment has explicit brakes:

  • -WhatIf or check-only mode;
  • a maximum change count;
  • a separate permission for removals;
  • dependency checks before deletion;
  • lower-environment deployment first;
  • a post-deployment posture-log check;
  • a rollback record containing the previous definition;
  • an expiry for temporary exceptions.

This follows the same pattern as Building Safe Cloudflare Zero Trust Deployment Pipelines, but the dependency checks are especially important because posture objects are reused by policies.

Roll out posture in stages

A posture signal should be observed before it becomes a hard access requirement.

My preferred sequence is:

  1. create the check in a test environment;
  2. verify expected results across supported operating systems;
  3. inspect posture logs for false negatives and unknown states;
  4. deploy to development and a small pilot group;
  5. add the signal to policy in a non-blocking or limited context where possible;
  6. define support and exception handling;
  7. enforce progressively;
  8. monitor denial reasons and help-desk impact.

Posture data has freshness. Polling schedules, client connectivity and provider availability all affect whether a result is current. A control that expires faster than the signal can be refreshed will create avoidable denials.

Design for unknown and degraded states

The important question is not only what happens when a device fails a check.

Also decide what happens when:

  • the endpoint provider is unavailable;
  • the device has not reported recently;
  • the posture result is unknown;
  • the Cloudflare One Client is unhealthy;
  • an operating-system update changes the reported version;
  • a certificate rollover creates two valid device certificates;
  • the policy platform cannot retrieve a third-party signal.

Some applications should fail closed. Others may need a narrow, time-limited recovery path.

Those choices belong in the system design, not in an incident-time guess. They connect directly to Designing Zero Trust for Failure.

Minimise the data collected

Device posture can become intrusive if teams collect every available signal simply because the platform supports it.

Use signals that are:

  • necessary for an access decision;
  • explainable to users and support teams;
  • stable enough to operate;
  • mapped to a documented control;
  • retained only as long as required.

Avoid fragile checks against arbitrary files or processes unless there is a clear security requirement and an owner prepared to maintain them.

The wider lesson

Managing posture as code is not about replacing the dashboard for its own sake.

It provides:

  • versioned intent;
  • reusable device-health definitions;
  • deterministic environment mapping;
  • dependency validation;
  • previewable changes;
  • controlled deletion;
  • evidence of what was deployed.

Device trust becomes part of the security platform. It deserves the same engineering discipline as identity policy and network policy.


Further reading


The YAML, names, versions and integration keys are synthetic. The article omits account identifiers, object IDs, endpoint-provider details and production policy expressions.