How auth0-deploy-cli Detects and Resolves Conflicts When Updating Resources
The auth0-deploy-cli detects naming conflicts by comparing desired assets against existing remote resources in calculateChanges.ts, then resolves them by temporarily renaming conflicting resources with random suffixes via the default.ts handler before applying updates.
When managing Auth0 tenants through infrastructure-as-code, naming collisions frequently occur as resources are renamed locally while older versions persist remotely. The auth0-deploy-cli implements a deterministic conflict resolution mechanism that ensures idempotent deployments without manual intervention. This article examines the conflict detection logic in src/tools/calculateChanges.ts and the resolution strategy implemented in src/tools/auth0/handlers/default.ts.
Conflict Detection Logic in calculateChanges.ts
The conflict detection process begins in src/tools/calculateChanges.ts, where the calculateChanges function receives two parameters: the desired assets from local configuration (assets) and the current remote state (existing).
The function first categorizes assets into three lists: create, update, and del. When the identifier list includes name, the logic performs an additional collision detection pass:
- Compiles "future assets" (resources scheduled for creation or update)
- Compares these against existing resources not marked for deletion
- Identifies collisions where
namematches but primary identifiers differ
// src/tools/calculateChanges.ts
if (identifiers.includes('name')) {
const uniqueID = identifiers[0];
const futureAssets: Asset[] = [...create, ...update];
futureAssets.forEach((a) => {
// skip if the colliding item will be deleted
const inDeleted = del.filter((e) => e.name === a.name && e[uniqueID] !== a[uniqueID])[0];
if (!inDeleted) {
const conflict = (existing || []).filter(
(e) => e.name === a.name && e[uniqueID] !== a[uniqueID]
)[0];
if (conflict) {
// rename the existing conflicting resource with a temporary random suffix
const temp = Math.random().toString(36).substr(2, 5);
conflicts.push({
...conflict,
name: `${conflict.name}-${temp}`,
});
}
}
});
}
When a conflict is detected, the system generates a temporary name by appending a random 5-character alphanumeric suffix (e.g., my-client-a9x3p) and adds the modified resource to the conflicts array.
Conflict Resolution Strategy in default.ts
The src/tools/auth0/handlers/default.ts file contains the generic handler that processes the conflicts array returned by calculateChanges. This handler treats conflict resolution as a specialized update operation that must execute before other asset modifications.
The resolution process follows this sequence:
- Rename conflicting resources – Update existing resources with their temporary suffixed names
- Process deletions – Remove assets marked for deletion (if
AUTH0_ALLOW_DELETEis enabled) - Create new assets – Add resources that don't exist remotely
- Update remaining assets – Apply changes to non-conflicting existing resources
// src/tools/auth0/handlers/default.ts
// Process Renaming Entries Temp due to conflicts in names
await this.client.pool
.addEachTask({
data: conflicts || [],
generator: (updateItem) =>
retryWithExponentialBackoff(() => {
const updateFN = this.getClientFN(this.functions.update);
const updatePayload = (() => {
const data = stripFields({ ...updateItem }, this.stripUpdateFields);
return stripObfuscatedFieldsFromPayload(data, this.sensitiveFieldsToObfuscate);
})();
return updateFN(updateItem[this.id], updatePayload);
}, retryConfig)
.then((data) => this.didUpdate(data as Asset))
.catch((err) => {
throw new Error(
`Problem updating ${this.type} ${this.objString(updateItem)}\n${err}`
);
}),
})
.promise();
By renaming conflicting resources before creating new ones, the CLI ensures that name uniqueness constraints in the Auth0 Management API are never violated during the deployment process.
End-to-End Conflict Resolution Flow
Understanding the complete flow helps clarify how auth0-deploy-cli maintains tenant consistency:
- Configuration parsing – The CLI loads local YAML or directory-based configurations and constructs the desired asset state
- Remote state retrieval – Each resource handler (e.g.,
clients.ts,rules.ts) fetches existing assets from the Auth0 Management API - Change calculation –
calculateChangescompares states and identifies conflicts where future assets share names with existing resources not scheduled for deletion - Temporary renaming – Conflicting existing resources receive random suffixes via the
default.tshandler - Asset synchronization – The CLI proceeds with deletions, creations, and updates in that order
- Cleanup – On subsequent runs, temporarily renamed resources that are no longer referenced in local configuration become candidates for deletion
Practical Examples
Basic Import with Automatic Conflict Resolution
When importing a client named my-client that already exists remotely under different metadata:
npm run build && node lib/index.js import -c config.json -i ./local/
The CLI automatically:
- Detects the naming collision in
calculateChanges.ts - Renames the remote client to
my-client-x1a9bviadefault.ts - Creates the new
my-clientwith updated configuration - Leaves the suffixed version for manual review or subsequent deletion
Conflict Resolution with Deletions Disabled
Even when AUTH0_ALLOW_DELETE is set to false, conflict resolution still functions:
export AUTH0_ALLOW_DELETE=false
npm run build && node lib/index.js import -c config.json -i ./local/
In this scenario:
- The conflicting resource is renamed but not deleted
- Both the old (renamed) and new versions coexist in the tenant
- Subsequent deployments can remove the suffixed version if deletion is later enabled
Programmatic Conflict Detection
For custom tooling or debugging, you can invoke the conflict detection logic directly:
import { calculateChanges } from './src/tools/calculateChanges';
import { ClientHandler } from './src/tools/auth0/handlers/clients';
const handler = new ClientHandler(/* config & client */);
const desired = [ /* array of client definitions from YAML */ ];
const existing = await handler.getAll(); // remote state
const changes = calculateChanges({
handler,
assets: desired,
existing,
identifiers: ['client_id', 'name'],
allowDelete: true,
});
console.log('Conflicts to rename:', changes.conflicts);
This returns the conflicts array containing the temporary-renamed representations that default.ts would process during a standard deployment.
Summary
- Conflict detection occurs in
src/tools/calculateChanges.tsby comparing future asset names against existing resources not scheduled for deletion - Temporary renaming uses random 5-character suffixes (e.g.,
resource-a9x3p) to resolve naming collisions without data loss - Resolution execution happens in
src/tools/auth0/handlers/default.tsbefore deletions, creations, or standard updates - Idempotent deployments are ensured by processing conflicts first, allowing the Auth0 Management API to maintain unique name constraints throughout the operation
Frequently Asked Questions
How does auth0-deploy-cli detect naming conflicts?
The CLI detects naming conflicts in src/tools/calculateChanges.ts by comparing the names of assets scheduled for creation or update against existing remote resources. When the identifier list includes name, the function checks if any future asset shares a name with an existing asset that has a different primary identifier and is not marked for deletion.
What happens when a conflict is detected during import?
When a conflict is detected, the CLI generates a temporary name for the existing resource by appending a random 5-character suffix (e.g., my-client-x1a9b). This renamed resource is added to the conflicts array. During execution in src/tools/auth0/handlers/default.ts, the CLI updates the existing resource with this temporary name first, freeing the original name for the new or updated resource.
Can I disable automatic conflict resolution?
No, the conflict resolution mechanism is built into the core deployment logic and cannot be disabled through configuration flags. However, you can control whether the renamed (conflicting) resources are subsequently deleted by setting AUTH0_ALLOW_DELETE=false. This preserves both the old (renamed) and new versions in your tenant.
Which resources support conflict detection?
Conflict detection applies to any resource handler that uses name as an identifier and inherits from the default handler in src/tools/auth0/handlers/default.ts. This includes clients, resource servers, rules, hooks, and connections. Each resource-specific handler (such as clients.ts or rules.ts) invokes calculateChanges with appropriate identifiers to trigger the conflict detection logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →