Business Challenge
Everything up to #52 answered "who is this principal". This post is the next question: under what conditions should the answer still be yes. It is the right question to ask, the service that answers it is well built, and the three things worth knowing about it are all in the documentation rather than hidden.
It is not. Access Context Manager is designed to define specific rules or context, and policy is configured and enforced across various points, such as VPC Service Controls. An access level is a definition sitting in a container; until something binds it, it restricts nothing.
Correct approachTreat access levels as vocabulary and the binding as the control. Verify the enforcement point — a binding, a perimeter, an IAP setting, an IAM condition — not the level.
It does the opposite. Multiple access levels are logically ORed, and the logical OR means that to access the resource, the user must meet the conditions of at least one of the selected levels. Each level you add is another way in.
Correct approachTighten inside a single level, where attributes combine as AND. Add a second level only when you intend to permit an alternative route.
It is ignored. The documentation states that where one operand uniquely determines the result, the other operand might or might not be evaluated, and then adds the part that matters: in addition, if that evaluation produces a runtime error, it is ignored. A condition that breaks does not fail closed; it stops contributing.
Correct approachKeep custom expressions simple enough that they cannot error, and test each condition in isolation rather than only testing the combined level.
There is a published list of gaps, and one of them is absolute: Data Studio is always allowed unrestricted access to the Google Cloud APIs, regardless of Access Context Manager policies. Alongside it, device attributes are not available for non-Google OAuth client applications, and you cannot use an IP address as an attribute for Docker connections.
Correct approachRead the limitations page as part of the design, not as troubleshooting. Where a path is exempt, the control has to come from somewhere else.
Architecture
The premise is the one worth agreeing with before picking at the detail. A perimeter model trusts whatever is inside it, which stops being meaningful once people work from anywhere on devices you did not buy. The stated aim is to reduce the size of the privileged network and instead let you grant access based on the context of the request, such as device type, user identity, and more. That is the correct direction of travel and this post is not an argument against it.
Three objects, and only one of them does anything
| Object | What it is | Does it restrict anything? |
|---|---|---|
| Access policy | A container for all of your Access Context Manager resources, organisation-wide. | No |
| Access level | Conditions on IP, region, principal and device policy. Access levels describe the requirements for requests to be honored. | No — a definition only |
| Binding or perimeter | A group bound to one or more levels, or a VPC Service Controls perimeter, or an IAP setting, or an IAM condition. | Yes |
And the relationship to everything in the previous fifty-two posts is additive, not substitutive: these checks are applied in addition to standard IAM policy. Context-aware access cannot grant anything. It can only subtract from what IAM already allows, which makes it a genuinely safe control to add — the failure mode of getting it wrong is a lockout, not an exposure. Except where it errors, which is the next section.
It is targeted at a group, which means #52 applies in full
The enforcement for console and API access works by binding a Google group: users in the specified group are granted access only if they satisfy the conditions that are defined in the bound access levels. So this is an enforcement group in exactly the sense #52 described, with all the same properties — the membership that decides who is covered lives outside the policy, members can leave unless whoCanLeaveGroup is set, and changes take time. The binding itself adds its own delay: the binding might take a few minutes to propagate.
Scope is bounded in a way worth stating plainly: context-Aware Access policies apply only to users within your organization. External collaborators, and the consumer accounts #49 was about, are not covered by this mechanism at all.
Four posts in a row have turned up a documented carve-out, and here is this one: we recommend that you exclude at least one Organization Admin or Organization Owner from this group to reduce the risk of an accidental lockout. It is a recommendation rather than a built-in bypass, which makes it better than #51’s super-admin exemption — it is yours to make, scope and record. But the shape is identical. Every control in this stack that can lock you out ships with an escape hatch, and the escape hatch is always an account with more power than the ones being controlled. Write down which account it is, and put the #51 protections on it.
Why This Architecture Holds Up
Two combining rules operate at different levels, and they pull in opposite directions. This is the part that produces policies which do not mean what their author intended.
| Where | How it combines | Effect of adding one more |
|---|---|---|
| Attributes within a basic access level | Combined as either an AND operation (all must be true) or a NOR operation (none must be true) | Tightens |
| Access levels bound to a group | Multiple access levels are logically ORed | Loosens |
So "we have three access levels on that binding" is not evidence of a strict policy. It means there are three independent ways to satisfy it, and the effective requirement is the weakest of the three. The NOR option inside a level is also worth noticing, because it gives you a clean way to express "none of these" — useful for excluding regions or networks without inverting every other attribute by hand.
The short-circuit, which is the thing I did not expect
Custom access levels are written in a subset of Common Expression Language, and the condition must resolve to a single boolean value. The documentation attaches a warning to combining them that is easy to read past and worth reading twice.
Where one operand uniquely determines the result, the other operand might or might not be evaluated — ordinary short-circuit evaluation, familiar from any language. Then: in addition, if that evaluation produces a runtime error, it is ignored. The documentation supplies its own worked example, and the outcome is the one you would least want. A region check and an IP check are combined with OR; the evaluation of origin.region_code fails, but the levels.ip_check evaluation succeeds, and the request is granted. The region restriction — which was presumably the point — contributed nothing, and nothing in the result says so.
Put that together with the OR semantics and you get a specific failure worth guarding against. A geographic restriction and a corporate-network restriction in two separate access levels on one binding is already an OR, so either alone is sufficient. If the geographic condition then errors — an attribute unavailable for that request type, say — it is not merely bypassed for that request; it is silently absent from the evaluation. The policy looks like two controls and behaves like one, and the audit log shows a granted request that satisfied the policy, because it did.
The practical defence is unglamorous: express the intent inside one access level where attributes are ANDed, keep custom expressions short enough that they have no error modes, and test each condition on its own by binding it alone to a test group before combining it with anything.
What it cannot see
The limitations are published in one short list, and they are design inputs rather than trivia:
- Data Studio is exempt, unconditionally. Data Studio is always allowed unrestricted access to the Google Cloud APIs, regardless of Access Context Manager policies. If a reporting tool can read the data, the device and network conditions on your humans do not apply to that path.
- Non-Google OAuth clients get no device signal. Device attributes are not available for non-Google OAuth client applications, so a third-party client is, from the device policy point of view, an unmanaged one.
- Some transports cannot carry an IP condition. You cannot use an IP address as an attribute for Docker connections, nor can you use private IP addresses when connecting to private clusters using kubectl or a managed Looker instance.
- You cannot use a scoped access level here, so delegated folder- or project-scoped policies do not apply to this binding.
On the IP point, know which ranges count as private, because the list includes one people forget: alongside the three RFC 1918 blocks and IPv6 unique local addresses, the following IP ranges are treated as private ranges by Access Context Manager includes 100.64.0.0/10, the RFC 6598 shared address space used by carrier-grade NAT. A home or mobile connection can sit in that range.
Two smaller notes
Device conditions are a paid feature, stated twice: this feature is available only as part of a paid enterprise security subscription, and creating access levels by using device attributes is available in the paid subscription of Chrome Enterprise Premium. So "require an encrypted device with a screen lock" is a licensing decision before it is a policy decision — the same shape as #52’s licence-gated membership audit.
And an echo of #48: an access policy is versioned using an etag, and if multiple sources change your access policy, using the etag field for the gcloud command-line tool and API calls helps prevent unintended overwrites and conflicts. Google has shipped the concurrency control here that #48 found missing from the allow policy, which is a genuine improvement — provided your automation actually passes the etag.
Key Architecture Decisions
| Decision | Choose this | Because |
|---|---|---|
| Expressing a strict requirement | Multiple attributes in one access level | Attributes within a level are ANDed; levels are ORed. |
| Binding several access levels | Only to permit genuine alternatives | Each added level is another sufficient route in. |
| Custom CEL expressions | Keep them short and error-free | A runtime error in a condition is ignored, not denied. |
| Testing a new condition | Bind it alone to a test group first | A combined level hides a condition that never evaluates. |
| Excluding regions or networks | NOR within one level | It expresses "none of these" without inverting everything. |
| Reporting and BI paths | Control them elsewhere | Data Studio is exempt from these policies entirely. |
| Third-party OAuth clients | Do not rely on device policy | Device attributes are unavailable for them. |
| Lockout protection | Exclude one named Org Admin, and protect it | It is the documented recommendation, and it is a privileged account. |
| Automation that edits the policy | Pass the etag | Concurrent writers otherwise overwrite each other. |
| Who is covered | Check it is your own users | These policies apply only to users within your organization. |
The one to look at today
Open your access bindings and count the access levels on each one. Anywhere the count is greater than one, write down what the effective requirement actually is — which is the weakest level, not the strongest — and check whether that matches what the binding was created to do. This takes ten minutes and finds the gap between "we enforce device posture and corporate network" and "we enforce device posture or corporate network", which is a meaningful difference that the console presents as a tidy list either way.
Closing Thought
This is the part of the stack I would defend most readily. Context-aware access can only subtract from what IAM already permits, which makes it close to free to adopt: the worst outcome of a mistake is that somebody cannot work, which you find out immediately and loudly. Set against the alternative of trusting a network perimeter, it is plainly the better model, and the etag concurrency control is the sort of detail that suggests the service was built by people who had been bitten.
The thing to carry forward is narrower and it is about reading. Three times in this post the documentation says precisely what happens — levels are ORed, errors are ignored, Data Studio is exempt — and all three are the kind of sentence that reads as housekeeping until you need it to be true. Fifty-three posts in, that is the pattern this series keeps finding: not vendors hiding things, but load-bearing facts placed where nobody is looking, in a limitations list or a note about audit logs or a page about propagation. The architecture is usually sound. The reading is where the work is.
#54 takes the other half of conditional access: Privileged Access Manager and just-in-time elevation — granting a role for an hour instead of forever, and what that does to the review problem.
Comments