diff --git a/packages/@aws-cdk/aws-amplify/README.md b/packages/@aws-cdk/aws-amplify/README.md index 7c15079d914d6..b8e3bd91c2306 100644 --- a/packages/@aws-cdk/aws-amplify/README.md +++ b/packages/@aws-cdk/aws-amplify/README.md @@ -122,7 +122,10 @@ mySinglePageApp.addCustomRule(amplify.CustomRule.SINGLE_PAGE_APPLICATION_REDIREC Add a domain and map sub domains to branches: ```ts -const domain = amplifyApp.addDomain('example.com'); +const domain = amplifyApp.addDomain('example.com', { + enableAutoSubdomain: true, // in case subdomains should be auto registered for branches + autoSubdomainCreationPatterns: ['*', 'pr*'], // regex for branches that should auto register subdomains +}); domain.mapRoot(master); // map master branch to domain root domain.mapSubDomain(master, 'www'); domain.mapSubDomain(dev); // sub domain prefix defaults to branch name diff --git a/packages/@aws-cdk/aws-amplify/lib/app.ts b/packages/@aws-cdk/aws-amplify/lib/app.ts index bf1d5bc3d5e89..43f8e308cb8f9 100644 --- a/packages/@aws-cdk/aws-amplify/lib/app.ts +++ b/packages/@aws-cdk/aws-amplify/lib/app.ts @@ -289,6 +289,7 @@ export class App extends Resource implements IApp, iam.IGrantable { return new Domain(this, id, { ...options, app: this, + autoSubDomainIamRole: this.grantPrincipal as iam.IRole, }); } } diff --git a/packages/@aws-cdk/aws-amplify/lib/domain.ts b/packages/@aws-cdk/aws-amplify/lib/domain.ts index bf6ba7afe8017..f8683f44b123d 100644 --- a/packages/@aws-cdk/aws-amplify/lib/domain.ts +++ b/packages/@aws-cdk/aws-amplify/lib/domain.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import { Lazy, Resource, IResolvable } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnDomain } from './amplify.generated'; @@ -21,6 +22,20 @@ export interface DomainOptions { * @default - use `addSubDomain()` to add subdomains */ readonly subDomains?: SubDomain[]; + + /** + * Automatically create subdomains for connected branches + * + * @default false + */ + readonly enableAutoSubdomain?: boolean; + + /** + * Branches which should automatically create subdomains + * + * @default - all repository branches ['*', 'pr*'] + */ + readonly autoSubdomainCreationPatterns?: string[]; } /** @@ -31,6 +46,12 @@ export interface DomainProps extends DomainOptions { * The application to which the domain must be connected */ readonly app: IApp; + + /** + * The IAM role with access to Route53 when using enableAutoSubdomain + * @default the IAM role from App.grantPrincipal + */ + readonly autoSubDomainIamRole?: iam.IRole; } /** @@ -106,6 +127,9 @@ export class Domain extends Resource { appId: props.app.appId, domainName, subDomainSettings: Lazy.any({ produce: () => this.renderSubDomainSettings() }, { omitEmptyArray: true }), + enableAutoSubDomain: !!props.enableAutoSubdomain, + autoSubDomainCreationPatterns: props.autoSubdomainCreationPatterns || ['*', 'pr*'], + autoSubDomainIamRole: props.autoSubDomainIamRole?.roleArn, }); this.arn = domain.attrArn; diff --git a/packages/@aws-cdk/aws-amplify/test/domain.test.ts b/packages/@aws-cdk/aws-amplify/test/domain.test.ts index ca7c211d14094..7b0f28f75837d 100644 --- a/packages/@aws-cdk/aws-amplify/test/domain.test.ts +++ b/packages/@aws-cdk/aws-amplify/test/domain.test.ts @@ -1,3 +1,4 @@ +import * as iam from '@aws-cdk/aws-iam'; import '@aws-cdk/assert/jest'; import { App, SecretValue, Stack } from '@aws-cdk/core'; import * as amplify from '../lib'; @@ -120,3 +121,113 @@ test('throws at synthesis without subdomains', () => { // THEN expect(() => app.synth()).toThrow(/The domain doesn't contain any subdomains/); }); + +test('auto subdomain all branches', () => { + // GIVEN + const stack = new Stack(); + const app = new amplify.App(stack, 'App', { + sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ + owner: 'aws', + repository: 'aws-cdk', + oauthToken: SecretValue.plainText('secret'), + }), + }); + const prodBranch = app.addBranch('master'); + + // WHEN + const domain = app.addDomain('amazon.com', { + enableAutoSubdomain: true, + }); + domain.mapRoot(prodBranch); + + // THEN + expect(stack).toHaveResource('AWS::Amplify::Domain', { + EnableAutoSubDomain: true, + AutoSubDomainCreationPatterns: [ + '*', + 'pr*', + ], + AutoSubDomainIAMRole: { + 'Fn::GetAtt': [ + 'AppRole1AF9B530', + 'Arn', + ], + }, + }); +}); + +test('auto subdomain some branches', () => { + // GIVEN + const stack = new Stack(); + const app = new amplify.App(stack, 'App', { + sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ + owner: 'aws', + repository: 'aws-cdk', + oauthToken: SecretValue.plainText('secret'), + }), + }); + const prodBranch = app.addBranch('master'); + + // WHEN + const domain = app.addDomain('amazon.com', { + enableAutoSubdomain: true, + autoSubdomainCreationPatterns: ['features/**'], + }); + domain.mapRoot(prodBranch); + + // THEN + expect(stack).toHaveResource('AWS::Amplify::Domain', { + EnableAutoSubDomain: true, + AutoSubDomainCreationPatterns: ['features/**'], + AutoSubDomainIAMRole: { + 'Fn::GetAtt': [ + 'AppRole1AF9B530', + 'Arn', + ], + }, + }); +}); + +test('auto subdomain with IAM role', () => { + // GIVEN + const stack = new Stack(); + const app = new amplify.App(stack, 'App', { + sourceCodeProvider: new amplify.GitHubSourceCodeProvider({ + owner: 'aws', + repository: 'aws-cdk', + oauthToken: SecretValue.plainText('secret'), + }), + role: iam.Role.fromRoleArn( + stack, + 'AmplifyRole', + `arn:aws:iam::${Stack.of(stack).account}:role/AmplifyRole`, + { mutable: false }, + ), + }); + const prodBranch = app.addBranch('master'); + + // WHEN + const domain = app.addDomain('amazon.com', { + enableAutoSubdomain: true, + autoSubdomainCreationPatterns: ['features/**'], + }); + domain.mapRoot(prodBranch); + + // THEN + expect(stack).toHaveResource('AWS::Amplify::Domain', { + EnableAutoSubDomain: true, + AutoSubDomainCreationPatterns: ['features/**'], + AutoSubDomainIAMRole: { + 'Fn::Join': [ + '', + [ + 'arn:aws:iam::', + { + Ref: 'AWS::AccountId', + }, + ':role/AmplifyRole', + ], + ], + }, + }); +}); \ No newline at end of file