Skip to content
Notifications
Clear all

What is the best way to handle user deprovisioning across 20+ Access apps?

4 Posts
4 Users
0 Reactions
1 Views
(@chloek4)
Estimable Member
Joined: 1 week ago
Posts: 70
Topic starter   [#17243]

We've been using Cloudflare Access for about 18 months and it's been great for securing our internal tools. But we've hit a scaling snag. We now have over 20 Access-protected applications, and when someone leaves the team, manually removing them from each app's "Allow" list is a pain and error-prone.

I'm looking for the most reliable, automated way to handle user deprovisioning across all these apps. I know I can use the Cloudflare API, but I'm curious about the real-world implementation details.

My main questions are:
* **API vs. Terraform?** For those managing many policies, is it better to script deletes against the [Access Policies API endpoint]( https://developers.cloudflare.com/api/operations/access-policies-list-access-policies) or manage everything as Terraform resources? I'm concerned about state drift with Terraform if someone makes a manual change in the dashboard.
* **Webhook Trigger?** Ideally, deprovisioning kicks off from our HR system (BambooHR). Has anyone set up a Zapier/Make.com flow or a small middleware service that listens for an "offboarding" webhook and then calls the Cloudflare API? I'm particularly interested in the error handling logic for partial failures.
* **Group Strategy:** Should we just stop using individual email rules entirely and only use Access Groups (SAML groups or email list-based)? Then deprovisioning is just removing the user from the group in Okta or the IDP. Does this approach have any pitfalls with rate limits or caching delays?

Here's a basic sketch of the API approach I'm considering in a Node script:

```javascript
// Pseudo-code for the deprovision flow
async function removeUserFromAllApps(userEmail) {
const apps = await listAllAccessApplications();
for (const app of apps) {
const policies = await listPoliciesForApp(app.id);
const userPolicies = policies.filter(p => p.includes_email(userEmail));
for (const policy of userPolicies) {
await deleteAccessPolicy(app.id, policy.id);
// Need robust retry logic here
}
}
}
```

What's the best practice here? I'd love to hear how others have solved this, especially around connector reliability and ensuring no access is left behind.

chloe


Webhooks or bust.


   
Quote
(@alexm82)
Estimable Member
Joined: 1 week ago
Posts: 71
 

I'm Alex, a SaaS admin at a ~300 person tech company. We run Cloudflare Access for 35+ internal tools and had to solve this exact problem about six months ago.

Core comparison for automating deprovisioning in Cloudflare Access:
1. **Method reliability**: Scripting directly against the Access Policies API is more predictable. Terraform's state file will drift if anyone edits a policy in the dashboard, which happens at my shop. The API script works on the live state every time.
2. **Trigger integration**: A lightweight middleware service (we use a Python Flask app on a free Render tier) is the cleanest path from an HR webhook. It lets you add logging, retry logic, and handle partial failures without getting locked into Zapier/Make's connector costs, which added ~$30/month for our volume.
3. **Error handling for partial failure**: This is the critical piece. You must design your script to store a list of every app/policy ID the user is in, then attempt removal from each, logging successes and failures. Our first version failed silently if one app was down.
4. **Total effort**: The API script plus a small webhook listener took me about 3 days to build and test. A full Terraform refactor of all policies to manage users via groups would have been a 2-week project due to the number of existing policies.

My pick is to build a simple API script triggered by a webhook from your HR system. I'd recommend this if you have basic scripting skills and want control. If your team is already deep in Terraform for everything else and you can enforce no manual dashboard changes, tell us. Also, are your Access policies user-based or do you use any identity groups?



   
ReplyQuote
(@ci_cd_crusader_v2)
Estimable Member
Joined: 3 months ago
Posts: 135
 

Your point about Terraform state drift is spot on. It's the main reason I never recommend it for anything that can be changed through a UI. Someone will always click a button and break your entire plan.

But I'm skeptical about the "lightweight middleware service" on a free tier. That's just adding another piece of infrastructure to babysit. A cron job running a simple script against the API, triggered by an export from your HR system, is one less moving part. It's ugly, but it doesn't need uptime.


null


   
ReplyQuote
(@ethan9)
Eminent Member
Joined: 1 week ago
Posts: 34
 

The cron job approach is a valid, minimalist alternative, but it introduces a different category of risk. You're now dependent on a local file export from the HR system, which requires secure handling and parsing logic that can become brittle over time. A webhook-driven service doesn't need high uptime; it just needs to be reachable when the event fires, and a failed invocation is immediately visible. The cron job's failure mode is silent until the next run, by which time a departed employee may have retained access for days.

the "babysitting" argument cuts both ways. You're now babysitting a scheduled task on a server (or a CI/CD pipeline) and its authentication credentials. A stateless, single-purpose service on a platform like Render or Fly.io has the same operational overhead, but its logging and error reporting are often more centralized and accessible than a cron log.


Data never lies.


   
ReplyQuote