Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/configuring-the-deploy-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,39 @@ String. Specifies the JWT signing algorithms used by the client when facilitatin

Boolean. When enabled, will allow the tool to delete resources. Default: `false`.

### `AUTH0_INCLUDED_CONNECTIONS`

Array of strings. Specifies which connections should be managed by the Deploy CLI. When configured, only the connections listed by name will be included in export and import operations. All other connections in the tenant will be completely ignored.

This is particularly useful for:
- Managing only specific connections while preserving others (e.g., self-service SSO connections, third-party integrations)
- Preventing accidental modifications to connections managed by other systems
- Isolating connection management to specific subsets of your tenant

**Important:** This setting affects all operations (export and import). Connections not in this list will not appear in exports and will not be modified during imports.

#### Example

```json
{
"AUTH0_INCLUDED_CONNECTIONS": ["github", "google-oauth2"]
}
```

In the example above, only the `github` and `google-oauth2` connections will be managed. All other connections in the tenant will be ignored.

#### Environment Variable Format

When passing as an environment variable, use JSON array format:

```shell
# JSON array format
export AUTH0_INCLUDED_CONNECTIONS='["github","google-oauth2","Username-Password-Authentication"]'

# Or as a single-line array
export AUTH0_INCLUDED_CONNECTIONS='["github"]'
```

### `AUTH0_EXCLUDED`

Array of strings. Excludes entire resource types from being managed, bi-directionally. See also: [excluding resources from management](excluding-from-management.md). Possible values: `actions`, `attackProtection`, `branding`, `clientGrants`, `clients`, `connections`, `customDomains`, `databases`, `emailProvider`, `phoneProviders`, `emailTemplates`, `guardianFactorProviders`, `guardianFactorTemplates`, `guardianFactors`, `guardianPhoneFactorMessageTypes`, `guardianPhoneFactorSelectedProvider`, `guardianPolicies`, `logStreams`, `migrations`, `organizations`, `pages`, `prompts`, `resourceServers`, `roles`, `tenant`, `triggers`, `selfServiceProfiles`.
Expand Down
4 changes: 4 additions & 0 deletions src/context/directory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export default class DirectoryContext {
resourceServers: config.AUTH0_EXCLUDED_RESOURCE_SERVERS || [],
defaults: config.AUTH0_EXCLUDED_DEFAULTS || [],
};

this.assets.include = {
connections: config.AUTH0_INCLUDED_CONNECTIONS || [],
};
}

loadFile(f: string, folder: string) {
Expand Down
5 changes: 5 additions & 0 deletions src/context/yaml/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export default class YAMLContext {
defaults: config.AUTH0_EXCLUDED_DEFAULTS || [],
};

this.assets.include = {
connections: config.AUTH0_INCLUDED_CONNECTIONS || [],
};

this.basePath = (() => {
if (!!config.AUTH0_BASE_PATH) return config.AUTH0_BASE_PATH;
//@ts-ignore because this looks to be a bug, but do not want to introduce regression; more investigation needed
Expand Down Expand Up @@ -202,6 +206,7 @@ export default class YAMLContext {

// Delete exclude as it's not part of the auth0 tenant config
delete cleaned.exclude;
delete cleaned.include;

// Optionally Strip identifiers
if (!this.config.AUTH0_EXPORT_IDENTIFIERS) {
Expand Down
15 changes: 14 additions & 1 deletion src/tools/auth0/handlers/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,19 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
conflicts: [],
};

const includedConnections = (assets.include && assets.include.connections) || [];
const filteredConnections =
includedConnections.length > 0
? connections.filter((conn) => includedConnections.includes(conn.name))
: connections;

if (includedConnections.length > 0 && filteredConnections.length !== connections.length) {
const excludedCount = connections.length - filteredConnections.length;
log.info(
`AUTH0_INCLUDED_CONNECTIONS is configured. Managing ${filteredConnections.length} connection(s), ignoring ${excludedCount} connection(s) not in the managed list.`
Copy link
Contributor

Choose a reason for hiding this comment

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

No ignoring, please add validation to ensure not to use both in the config file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented a validation check ensuring that AUTH0_INCLUDED_CONNECTIONS and AUTH0_EXCLUDED_CONNECTIONS are mutually exclusive.

);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

should not be part of src/tools/auth0/handlers/connections.ts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented the validation check within the global context.


// Convert enabled_clients by name to the id
const clients = await paginate<Client>(this.client.clients.list, {
paginate: true,
Expand All @@ -373,7 +386,7 @@ export default class ConnectionsHandler extends DefaultAPIHandler {
// Prepare an id map. We'll use this map later to get the `strategy` and SCIM enable status of the connections.
await this.scimHandler.createIdMap(existingConnections);

const formatted = connections.map((connection) => ({
const formatted = filteredConnections.map((connection) => ({
...connection,
...this.getFormattedOptions(connection, clients),
enabled_clients: getEnabledClients(assets, connection, existingConnections, clients),
Expand Down
7 changes: 6 additions & 1 deletion src/tools/auth0/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,10 @@ const auth0ApiHandlers: { [key in AssetTypes]: any } = {
};

export default auth0ApiHandlers as {
[key in AssetTypes]: { default: typeof APIHandler; excludeSchema?: any; schema: any };
[key in AssetTypes]: {
default: typeof APIHandler;
excludeSchema?: any;
schema: any;
includeSchema?: any;
};
}; // TODO: apply stronger types to schema properties
15 changes: 15 additions & 0 deletions src/tools/auth0/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ const excludeSchema = Object.entries(handlers).reduce(
{}
);

const includeSchema = Object.entries(handlers).reduce(
(map: { [key: string]: Object }, [name, obj]) => {
if (obj.includeSchema) {
map[name] = obj.includeSchema;
}
return map;
},
{}
);

export default {
type: 'object',
$schema: 'http://json-schema.org/draft-07/schema#',
Expand All @@ -28,6 +38,11 @@ export default {
properties: { ...excludeSchema },
default: {},
},
include: {
type: 'object',
properties: { ...includeSchema },
default: {},
},
},
additionalProperties: false,
};
4 changes: 4 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export type Config = {
INCLUDED_PROPS?: {
[key: string]: string[];
};
AUTH0_INCLUDED_CONNECTIONS?: string[];
AUTH0_IGNORE_UNAVAILABLE_MIGRATIONS?: boolean;
// Eventually deprecate. See: https://github.com/auth0/auth0-deploy-cli/issues/451#user-content-deprecated-exclusion-props
AUTH0_EXCLUDED_RULES?: string[];
Expand Down Expand Up @@ -136,6 +137,9 @@ export type Assets = Partial<{
exclude?: {
[key: string]: string[];
};
include?: {
[key: string]: string[];
};
clientsOrig: Asset[] | null;
themes: Theme[] | null;
forms: Form[] | null;
Expand Down
20 changes: 19 additions & 1 deletion test/context/directory/context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('#directory context validation', () => {
const context = new Context({ AUTH0_INPUT_FILE: dir }, mockMgmtClient());
await context.loadAssetsFromLocal();

expect(Object.keys(context.assets).length).to.equal(Object.keys(handlers).length + 1);
expect(Object.keys(context.assets).length).to.equal(Object.keys(handlers).length + 2);
Object.keys(context.assets).forEach((key) => {
if (key === 'exclude') {
expect(context.assets[key]).to.deep.equal({
Expand All @@ -26,6 +26,10 @@ describe('#directory context validation', () => {
resourceServers: [],
defaults: [],
});
} else if (key === 'include') {
expect(context.assets[key]).to.deep.equal({
connections: [],
});
} else {
expect(context.assets[key]).to.equal(null);
}
Expand Down Expand Up @@ -57,6 +61,20 @@ describe('#directory context validation', () => {
expect(context.assets.exclude.defaults).to.deep.equal(['emailProvider']);
});

it('should load includes', async () => {
const dir = path.resolve(testDataDir, 'directory', 'empty');
cleanThenMkdir(dir);

const config = {
AUTH0_INPUT_FILE: dir,
AUTH0_INCLUDED_CONNECTIONS: ['github', 'google-oauth2'],
};
const context = new Context(config, mockMgmtClient());
await context.loadAssetsFromLocal();

expect(context.assets.include.connections).to.deep.equal(['github', 'google-oauth2']);
});

it('should respect resource exclusion on import', async () => {
const tenantConfig = {
allowed_logout_urls: ['https://mycompany.org/logoutCallback'],
Expand Down
17 changes: 17 additions & 0 deletions test/context/yaml/context.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ describe('#YAML context validation', () => {
expect(context.assets.exclude.defaults).to.deep.equal(['emailProvider']);
});

it('should load includes', async () => {
const dir = path.resolve(testDataDir, 'yaml', 'empty');
cleanThenMkdir(dir);
const yaml = path.join(dir, 'empty.yaml');
fs.writeFileSync(yaml, '');

const config = {
AUTH0_INPUT_FILE: yaml,
AUTH0_INCLUDED_CONNECTIONS: ['github', 'google-oauth2'],
};

const context = new Context(config, mockMgmtClient());
await context.loadAssetsFromLocal();

expect(context.assets.include.connections).to.deep.equal(['github', 'google-oauth2']);
});

it('should respect resource exclusion on import', async () => {
/* Create empty directory */
const dir = path.resolve(testDataDir, 'yaml', 'resource-exclusion');
Expand Down
Loading