Skip to content

Commit 499ba85

Browse files
authoredDec 14, 2021
feat(amplify): Add Amplify asset deployment resource (#16922)
This change adds a custom resource that allows users to publish S3 assets to AWS Amplify. fixes #16208 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent c633529 commit 499ba85

File tree

11 files changed

+1429
-2
lines changed

11 files changed

+1429
-2
lines changed
 

‎packages/@aws-cdk/aws-amplify/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,13 @@ const amplifyApp = new amplify.App(stack, 'App', {
208208
],
209209
});
210210
```
211+
212+
## Deploying Assets
213+
214+
`sourceCodeProvider` is optional; when this is not specified the Amplify app can be deployed to using `.zip` packages. The `asset` property can be used to deploy S3 assets to Amplify as part of the CDK:
215+
216+
```ts
217+
const asset = new assets.Asset(this, "SampleAsset", {});
218+
const amplifyApp = new amplify.App(this, 'MyApp', {});
219+
const branch = amplifyApp.addBranch("dev", { asset: asset });
220+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
export interface AmplifyJobId {
2+
/**
3+
* If this field is included in an event passed to "IsComplete", it means we
4+
* initiated an Amplify deployment that should be monitored using
5+
* amplify:GetJob
6+
*/
7+
AmplifyJobId?: string;
8+
}
9+
10+
export type ResourceEvent = AWSLambda.CloudFormationCustomResourceEvent & AmplifyJobId;
11+
12+
export interface IsCompleteResponse {
13+
/**
14+
* Indicates if the resource operation is complete or should we retry.
15+
*/
16+
readonly IsComplete: boolean;
17+
18+
/**
19+
* Additional/changes to resource attributes.
20+
*/
21+
readonly Data?: { [name: string]: any };
22+
};
23+
24+
export abstract class ResourceHandler {
25+
protected readonly requestId: string;
26+
protected readonly logicalResourceId: string;
27+
protected readonly requestType: 'Create' | 'Update' | 'Delete';
28+
protected readonly physicalResourceId?: string;
29+
protected readonly event: ResourceEvent;
30+
31+
constructor(event: ResourceEvent) {
32+
this.requestType = event.RequestType;
33+
this.requestId = event.RequestId;
34+
this.logicalResourceId = event.LogicalResourceId;
35+
this.physicalResourceId = (event as any).PhysicalResourceId;
36+
this.event = event;
37+
}
38+
39+
public onEvent() {
40+
switch (this.requestType) {
41+
case 'Create':
42+
return this.onCreate();
43+
case 'Update':
44+
return this.onUpdate();
45+
case 'Delete':
46+
return this.onDelete();
47+
}
48+
49+
throw new Error(`Invalid request type ${this.requestType}`);
50+
}
51+
52+
public isComplete() {
53+
switch (this.requestType) {
54+
case 'Create':
55+
return this.isCreateComplete();
56+
case 'Update':
57+
return this.isUpdateComplete();
58+
case 'Delete':
59+
return this.isDeleteComplete();
60+
}
61+
62+
throw new Error(`Invalid request type ${this.requestType}`);
63+
}
64+
65+
protected log(x: any) {
66+
// eslint-disable-next-line no-console
67+
console.log(JSON.stringify(x, undefined, 2));
68+
}
69+
70+
protected abstract async onCreate(): Promise<AmplifyJobId>;
71+
protected abstract async onDelete(): Promise<void>;
72+
protected abstract async onUpdate(): Promise<AmplifyJobId>;
73+
protected abstract async isCreateComplete(): Promise<IsCompleteResponse>;
74+
protected abstract async isDeleteComplete(): Promise<IsCompleteResponse>;
75+
protected abstract async isUpdateComplete(): Promise<IsCompleteResponse>;
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// aws-sdk available at runtime for lambdas
2+
// eslint-disable-next-line import/no-extraneous-dependencies
3+
import { Amplify, S3 } from 'aws-sdk';
4+
import { AmplifyJobId, IsCompleteResponse, ResourceEvent, ResourceHandler } from './common';
5+
6+
export interface AmplifyAssetDeploymentProps {
7+
AppId: string;
8+
BranchName: string;
9+
S3BucketName: string;
10+
S3ObjectKey: string;
11+
TimeoutSeconds: number;
12+
}
13+
14+
export class AmplifyAssetDeploymentHandler extends ResourceHandler {
15+
private readonly props: AmplifyAssetDeploymentProps;
16+
protected readonly amplify: Amplify;
17+
protected readonly s3: S3;
18+
19+
constructor(amplify: Amplify, s3: S3, event: ResourceEvent) {
20+
super(event);
21+
22+
this.props = parseProps(this.event.ResourceProperties);
23+
this.amplify = amplify;
24+
this.s3 = s3;
25+
}
26+
27+
// ------
28+
// CREATE
29+
// ------
30+
31+
protected async onCreate(): Promise<AmplifyJobId> {
32+
// eslint-disable-next-line no-console
33+
console.log('deploying to Amplify with options:', JSON.stringify(this.props, undefined, 2));
34+
35+
// Verify no jobs are currently running.
36+
const jobs = await this.amplify
37+
.listJobs({
38+
appId: this.props.AppId,
39+
branchName: this.props.BranchName,
40+
maxResults: 1,
41+
})
42+
.promise();
43+
44+
if (
45+
jobs.jobSummaries &&
46+
jobs.jobSummaries.find(summary => summary.status === 'PENDING')
47+
) {
48+
return Promise.reject('Amplify job already running. Aborting deployment.');
49+
}
50+
51+
// Create a pre-signed get URL of the asset so Amplify can retrieve it.
52+
const assetUrl = this.s3.getSignedUrl('getObject', {
53+
Bucket: this.props.S3BucketName,
54+
Key: this.props.S3ObjectKey,
55+
});
56+
57+
// Deploy the asset to Amplify.
58+
const deployment = await this.amplify
59+
.startDeployment({
60+
appId: this.props.AppId,
61+
branchName: this.props.BranchName,
62+
sourceUrl: assetUrl,
63+
})
64+
.promise();
65+
66+
return {
67+
AmplifyJobId: deployment.jobSummary.jobId,
68+
};
69+
}
70+
71+
protected async isCreateComplete() {
72+
return this.isActive(this.event.AmplifyJobId);
73+
}
74+
75+
// ------
76+
// DELETE
77+
// ------
78+
79+
protected async onDelete(): Promise<void> {
80+
// We can't delete this resource as it's a deployment.
81+
return;
82+
}
83+
84+
protected async isDeleteComplete(): Promise<IsCompleteResponse> {
85+
// We can't delete this resource as it's a deployment.
86+
return {
87+
IsComplete: true,
88+
};
89+
}
90+
91+
// ------
92+
// UPDATE
93+
// ------
94+
95+
protected async onUpdate() {
96+
return this.onCreate();
97+
}
98+
99+
protected async isUpdateComplete() {
100+
return this.isActive(this.event.AmplifyJobId);
101+
}
102+
103+
private async isActive(jobId?: string): Promise<IsCompleteResponse> {
104+
if (!jobId) {
105+
throw new Error('Unable to determine Amplify job status without job id');
106+
}
107+
108+
const job = await this.amplify
109+
.getJob({
110+
appId: this.props.AppId,
111+
branchName: this.props.BranchName,
112+
jobId: jobId,
113+
})
114+
.promise();
115+
116+
if (job.job.summary.status === 'SUCCEED') {
117+
return {
118+
IsComplete: true,
119+
Data: {
120+
JobId: jobId,
121+
Status: job.job.summary.status,
122+
},
123+
};
124+
} if (job.job.summary.status === 'FAILED' || job.job.summary.status === 'CANCELLED') {
125+
throw new Error(`Amplify job failed with status: ${job.job.summary.status}`);
126+
} else {
127+
return {
128+
IsComplete: false,
129+
};
130+
}
131+
}
132+
}
133+
134+
function parseProps(props: any): AmplifyAssetDeploymentProps {
135+
return props;
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// eslint-disable-next-line import/no-extraneous-dependencies
2+
import { IsCompleteResponse } from '@aws-cdk/custom-resources/lib/provider-framework/types';
3+
// aws-sdk available at runtime for lambdas
4+
// eslint-disable-next-line import/no-extraneous-dependencies
5+
import { Amplify, S3, config } from 'aws-sdk';
6+
import { ResourceEvent } from './common';
7+
import { AmplifyAssetDeploymentHandler } from './handler';
8+
9+
const AMPLIFY_ASSET_DEPLOYMENT_RESOURCE_TYPE = 'Custom::AmplifyAssetDeployment';
10+
11+
config.logger = console;
12+
13+
const amplify = new Amplify();
14+
const s3 = new S3({ signatureVersion: 'v4' });
15+
16+
export async function onEvent(event: ResourceEvent) {
17+
const provider = createResourceHandler(event);
18+
return provider.onEvent();
19+
}
20+
21+
export async function isComplete(
22+
event: ResourceEvent,
23+
): Promise<IsCompleteResponse> {
24+
const provider = createResourceHandler(event);
25+
return provider.isComplete();
26+
}
27+
28+
function createResourceHandler(event: ResourceEvent) {
29+
switch (event.ResourceType) {
30+
case AMPLIFY_ASSET_DEPLOYMENT_RESOURCE_TYPE:
31+
return new AmplifyAssetDeploymentHandler(amplify, s3, event);
32+
default:
33+
throw new Error(`Unsupported resource type "${event.ResourceType}"`);
34+
}
35+
}

‎packages/@aws-cdk/aws-amplify/lib/branch.ts

+111-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1+
import * as path from 'path';
12
import * as codebuild from '@aws-cdk/aws-codebuild';
2-
import { IResource, Lazy, Resource } from '@aws-cdk/core';
3+
import * as iam from '@aws-cdk/aws-iam';
4+
import * as lambda from '@aws-cdk/aws-lambda';
5+
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
6+
import { Asset } from '@aws-cdk/aws-s3-assets';
7+
import {
8+
CustomResource,
9+
IResource,
10+
Lazy,
11+
Resource,
12+
Duration,
13+
NestedStack,
14+
Stack,
15+
} from '@aws-cdk/core';
16+
import { Provider } from '@aws-cdk/custom-resources';
317
import { Construct } from 'constructs';
418
import { CfnBranch } from './amplify.generated';
519
import { IApp } from './app';
@@ -90,6 +104,16 @@ export interface BranchOptions {
90104
* @default - no stage
91105
*/
92106
readonly stage?: string;
107+
108+
/**
109+
* Asset for deployment.
110+
*
111+
* The Amplify app must not have a sourceCodeProvider configured as this resource uses Amplify's
112+
* startDeployment API to initiate and deploy a S3 asset onto the App.
113+
*
114+
* @default - no asset
115+
*/
116+
readonly asset?: Asset
93117
}
94118

95119
/**
@@ -148,6 +172,19 @@ export class Branch extends Resource implements IBranch {
148172

149173
this.arn = branch.attrArn;
150174
this.branchName = branch.attrBranchName;
175+
176+
if (props.asset) {
177+
new CustomResource(this, 'DeploymentResource', {
178+
serviceToken: AmplifyAssetDeploymentProvider.getOrCreate(this),
179+
resourceType: 'Custom::AmplifyAssetDeployment',
180+
properties: {
181+
AppId: props.app.appId,
182+
BranchName: branchName,
183+
S3ObjectKey: props.asset.s3ObjectKey,
184+
S3BucketName: props.asset.s3BucketName,
185+
},
186+
});
187+
}
151188
}
152189

153190
/**
@@ -161,3 +198,76 @@ export class Branch extends Resource implements IBranch {
161198
return this;
162199
}
163200
}
201+
202+
class AmplifyAssetDeploymentProvider extends NestedStack {
203+
/**
204+
* Returns the singleton provider.
205+
*/
206+
public static getOrCreate(scope: Construct) {
207+
const providerId =
208+
'com.amazonaws.cdk.custom-resources.amplify-asset-deployment-provider';
209+
const stack = Stack.of(scope);
210+
const group =
211+
(stack.node.tryFindChild(providerId) as AmplifyAssetDeploymentProvider) ?? new AmplifyAssetDeploymentProvider(stack, providerId);
212+
return group.provider.serviceToken;
213+
}
214+
215+
private readonly provider: Provider;
216+
217+
constructor(scope: Construct, id: string) {
218+
super(scope, id);
219+
220+
const onEvent = new NodejsFunction(
221+
this,
222+
'amplify-asset-deployment-on-event',
223+
{
224+
entry: path.join(
225+
__dirname,
226+
'asset-deployment-handler/index.ts',
227+
),
228+
runtime: lambda.Runtime.NODEJS_14_X,
229+
handler: 'onEvent',
230+
initialPolicy: [
231+
new iam.PolicyStatement({
232+
resources: ['*'],
233+
actions: [
234+
's3:GetObject',
235+
's3:GetSignedUrl',
236+
'amplify:ListJobs',
237+
'amplify:StartDeployment',
238+
],
239+
}),
240+
],
241+
},
242+
);
243+
244+
const isComplete = new NodejsFunction(
245+
this,
246+
'amplify-asset-deployment-is-complete',
247+
{
248+
entry: path.join(
249+
__dirname,
250+
'asset-deployment-handler/index.ts',
251+
),
252+
runtime: lambda.Runtime.NODEJS_14_X,
253+
handler: 'isComplete',
254+
initialPolicy: [
255+
new iam.PolicyStatement({
256+
resources: ['*'],
257+
actions: ['amplify:GetJob*'],
258+
}),
259+
],
260+
},
261+
);
262+
263+
this.provider = new Provider(
264+
this,
265+
'amplify-asset-deployment-handler-provider',
266+
{
267+
onEventHandler: onEvent,
268+
isCompleteHandler: isComplete,
269+
totalTimeout: Duration.minutes(5),
270+
},
271+
);
272+
}
273+
}

‎packages/@aws-cdk/aws-amplify/package.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,20 @@
8080
"@aws-cdk/cfn2ts": "0.0.0",
8181
"@aws-cdk/pkglint": "0.0.0",
8282
"@types/jest": "^27.0.3",
83-
"@types/yaml": "1.9.6"
83+
"@types/yaml": "1.9.6",
84+
"aws-sdk": "^2.848.0"
8485
},
8586
"dependencies": {
8687
"@aws-cdk/aws-codebuild": "0.0.0",
8788
"@aws-cdk/aws-codecommit": "0.0.0",
8889
"@aws-cdk/aws-iam": "0.0.0",
8990
"@aws-cdk/aws-kms": "0.0.0",
91+
"@aws-cdk/aws-lambda": "0.0.0",
92+
"@aws-cdk/aws-lambda-nodejs": "0.0.0",
93+
"@aws-cdk/aws-s3-assets": "0.0.0",
9094
"@aws-cdk/aws-secretsmanager": "0.0.0",
9195
"@aws-cdk/core": "0.0.0",
96+
"@aws-cdk/custom-resources": "0.0.0",
9297
"constructs": "^3.3.69",
9398
"yaml": "1.10.2"
9499
},
@@ -100,8 +105,12 @@
100105
"@aws-cdk/aws-codecommit": "0.0.0",
101106
"@aws-cdk/aws-iam": "0.0.0",
102107
"@aws-cdk/aws-kms": "0.0.0",
108+
"@aws-cdk/aws-lambda": "0.0.0",
109+
"@aws-cdk/aws-lambda-nodejs": "0.0.0",
110+
"@aws-cdk/aws-s3-assets": "0.0.0",
103111
"@aws-cdk/aws-secretsmanager": "0.0.0",
104112
"@aws-cdk/core": "0.0.0",
113+
"@aws-cdk/custom-resources": "0.0.0",
105114
"constructs": "^3.3.69"
106115
},
107116
"engines": {

‎packages/@aws-cdk/aws-amplify/test/asset-deployment-handler/index.test.ts

+741
Large diffs are not rendered by default.

‎packages/@aws-cdk/aws-amplify/test/branch.test.ts

+63
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import * as path from 'path';
12
import { Template } from '@aws-cdk/assertions';
3+
import { Asset } from '@aws-cdk/aws-s3-assets';
24
import { SecretValue, Stack } from '@aws-cdk/core';
35
import * as amplify from '../lib';
46

@@ -99,3 +101,64 @@ test('with env vars', () => {
99101
],
100102
});
101103
});
104+
105+
test('with asset deployment', () => {
106+
// WHEN
107+
const asset = new Asset(app, 'SampleAsset', {
108+
path: path.join(__dirname, './test-asset'),
109+
});
110+
app.addBranch('dev', { asset });
111+
112+
// THEN
113+
Template.fromStack(stack).hasResourceProperties('Custom::AmplifyAssetDeployment', {
114+
ServiceToken: {
115+
'Fn::GetAtt': [
116+
'comamazonawscdkcustomresourcesamplifyassetdeploymentproviderNestedStackcomamazonawscdkcustomresourcesamplifyassetdeploymentproviderNestedStackResource89BDFEB2',
117+
'Outputs.comamazonawscdkcustomresourcesamplifyassetdeploymentprovideramplifyassetdeploymenthandlerproviderframeworkonEventA449D9A9Arn',
118+
],
119+
},
120+
AppId: {
121+
'Fn::GetAtt': [
122+
'AppF1B96344',
123+
'AppId',
124+
],
125+
},
126+
BranchName: 'dev',
127+
S3ObjectKey: {
128+
'Fn::Join': [
129+
'',
130+
[
131+
{
132+
'Fn::Select': [
133+
0,
134+
{
135+
'Fn::Split': [
136+
'||',
137+
{
138+
Ref: 'AssetParameters8c89eadc6be22019c81ed6b9c7d9929ae10de55679fd8e0e9fd4c00f8edc1cdaS3VersionKey70C0B407',
139+
},
140+
],
141+
},
142+
],
143+
},
144+
{
145+
'Fn::Select': [
146+
1,
147+
{
148+
'Fn::Split': [
149+
'||',
150+
{
151+
Ref: 'AssetParameters8c89eadc6be22019c81ed6b9c7d9929ae10de55679fd8e0e9fd4c00f8edc1cdaS3VersionKey70C0B407',
152+
},
153+
],
154+
},
155+
],
156+
},
157+
],
158+
],
159+
},
160+
S3BucketName: {
161+
Ref: 'AssetParameters8c89eadc6be22019c81ed6b9c7d9929ae10de55679fd8e0e9fd4c00f8edc1cdaS3Bucket83484C89',
162+
},
163+
});
164+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
{
2+
"Parameters": {
3+
"AssetParameters76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7S3Bucket3C55BA0F": {
4+
"Type": "String",
5+
"Description": "S3 bucket for asset \"76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7\""
6+
},
7+
"AssetParameters76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7S3VersionKeyE1E2D7D6": {
8+
"Type": "String",
9+
"Description": "S3 key for asset version \"76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7\""
10+
},
11+
"AssetParameters76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7ArtifactHashB1665559": {
12+
"Type": "String",
13+
"Description": "Artifact hash for asset \"76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7\""
14+
},
15+
"AssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56S3Bucket7A871D89": {
16+
"Type": "String",
17+
"Description": "S3 bucket for asset \"ff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56\""
18+
},
19+
"AssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56S3VersionKeyAACF81DD": {
20+
"Type": "String",
21+
"Description": "S3 key for asset version \"ff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56\""
22+
},
23+
"AssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56ArtifactHash2A4E644A": {
24+
"Type": "String",
25+
"Description": "Artifact hash for asset \"ff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56\""
26+
},
27+
"AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketDC4B98B1": {
28+
"Type": "String",
29+
"Description": "S3 bucket for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\""
30+
},
31+
"AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3VersionKeyA495226F": {
32+
"Type": "String",
33+
"Description": "S3 key for asset version \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\""
34+
},
35+
"AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1ArtifactHashA521A16F": {
36+
"Type": "String",
37+
"Description": "Artifact hash for asset \"daeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1\""
38+
},
39+
"AssetParametersa1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2ddS3Bucket456FC783": {
40+
"Type": "String",
41+
"Description": "S3 bucket for asset \"a1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2dd\""
42+
},
43+
"AssetParametersa1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2ddS3VersionKey4A933266": {
44+
"Type": "String",
45+
"Description": "S3 key for asset version \"a1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2dd\""
46+
},
47+
"AssetParametersa1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2ddArtifactHash7857C55E": {
48+
"Type": "String",
49+
"Description": "Artifact hash for asset \"a1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2dd\""
50+
}
51+
},
52+
"Resources": {
53+
"AppRole1AF9B530": {
54+
"Type": "AWS::IAM::Role",
55+
"Properties": {
56+
"AssumeRolePolicyDocument": {
57+
"Statement": [
58+
{
59+
"Action": "sts:AssumeRole",
60+
"Effect": "Allow",
61+
"Principal": {
62+
"Service": "amplify.amazonaws.com"
63+
}
64+
}
65+
],
66+
"Version": "2012-10-17"
67+
}
68+
}
69+
},
70+
"AppF1B96344": {
71+
"Type": "AWS::Amplify::App",
72+
"Properties": {
73+
"Name": "App",
74+
"BasicAuthConfig": {
75+
"EnableBasicAuth": false
76+
},
77+
"IAMServiceRole": {
78+
"Fn::GetAtt": [
79+
"AppRole1AF9B530",
80+
"Arn"
81+
]
82+
}
83+
}
84+
},
85+
"AppmainF505BAED": {
86+
"Type": "AWS::Amplify::Branch",
87+
"Properties": {
88+
"AppId": {
89+
"Fn::GetAtt": [
90+
"AppF1B96344",
91+
"AppId"
92+
]
93+
},
94+
"BranchName": "main",
95+
"EnableAutoBuild": true,
96+
"EnablePullRequestPreview": true
97+
}
98+
},
99+
"AppmainDeploymentResource442DE93D": {
100+
"Type": "Custom::AmplifyAssetDeployment",
101+
"Properties": {
102+
"ServiceToken": {
103+
"Fn::GetAtt": [
104+
"comamazonawscdkcustomresourcesamplifyassetdeploymentproviderNestedStackcomamazonawscdkcustomresourcesamplifyassetdeploymentproviderNestedStackResource89BDFEB2",
105+
"Outputs.cdkamplifyappassetdeploymentcomamazonawscdkcustomresourcesamplifyassetdeploymentprovideramplifyassetdeploymenthandlerproviderframeworkonEventC3C43E44Arn"
106+
]
107+
},
108+
"AppId": {
109+
"Fn::GetAtt": [
110+
"AppF1B96344",
111+
"AppId"
112+
]
113+
},
114+
"BranchName": "main",
115+
"S3ObjectKey": {
116+
"Fn::Join": [
117+
"",
118+
[
119+
{
120+
"Fn::Select": [
121+
0,
122+
{
123+
"Fn::Split": [
124+
"||",
125+
{
126+
"Ref": "AssetParameters76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7S3VersionKeyE1E2D7D6"
127+
}
128+
]
129+
}
130+
]
131+
},
132+
{
133+
"Fn::Select": [
134+
1,
135+
{
136+
"Fn::Split": [
137+
"||",
138+
{
139+
"Ref": "AssetParameters76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7S3VersionKeyE1E2D7D6"
140+
}
141+
]
142+
}
143+
]
144+
}
145+
]
146+
]
147+
},
148+
"S3BucketName": {
149+
"Ref": "AssetParameters76c74dffba7c3eb9a040dc95633eac403472969bf8a18831ac1cf243971c5bf7S3Bucket3C55BA0F"
150+
}
151+
},
152+
"UpdateReplacePolicy": "Delete",
153+
"DeletionPolicy": "Delete"
154+
},
155+
"comamazonawscdkcustomresourcesamplifyassetdeploymentproviderNestedStackcomamazonawscdkcustomresourcesamplifyassetdeploymentproviderNestedStackResource89BDFEB2": {
156+
"Type": "AWS::CloudFormation::Stack",
157+
"Properties": {
158+
"TemplateURL": {
159+
"Fn::Join": [
160+
"",
161+
[
162+
"https://s3.",
163+
{
164+
"Ref": "AWS::Region"
165+
},
166+
".",
167+
{
168+
"Ref": "AWS::URLSuffix"
169+
},
170+
"/",
171+
{
172+
"Ref": "AssetParametersa1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2ddS3Bucket456FC783"
173+
},
174+
"/",
175+
{
176+
"Fn::Select": [
177+
0,
178+
{
179+
"Fn::Split": [
180+
"||",
181+
{
182+
"Ref": "AssetParametersa1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2ddS3VersionKey4A933266"
183+
}
184+
]
185+
}
186+
]
187+
},
188+
{
189+
"Fn::Select": [
190+
1,
191+
{
192+
"Fn::Split": [
193+
"||",
194+
{
195+
"Ref": "AssetParametersa1ec2b3c34d7ba5b1816474781916bb1c8a8086a266e6d7cf88a0720b114d2ddS3VersionKey4A933266"
196+
}
197+
]
198+
}
199+
]
200+
}
201+
]
202+
]
203+
},
204+
"Parameters": {
205+
"referencetocdkamplifyappassetdeploymentAssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56S3BucketA0EDA7B5Ref": {
206+
"Ref": "AssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56S3Bucket7A871D89"
207+
},
208+
"referencetocdkamplifyappassetdeploymentAssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56S3VersionKeyD32C918ARef": {
209+
"Ref": "AssetParametersff9527128e3cc60cee11deb3d533504348f62709c853288178d757494fd92c56S3VersionKeyAACF81DD"
210+
},
211+
"referencetocdkamplifyappassetdeploymentAssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketA5B3B03BRef": {
212+
"Ref": "AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3BucketDC4B98B1"
213+
},
214+
"referencetocdkamplifyappassetdeploymentAssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3VersionKey61CE3542Ref": {
215+
"Ref": "AssetParametersdaeb79e3cee39c9b902dc0d5c780223e227ed573ea60976252947adab5fb2be1S3VersionKeyA495226F"
216+
}
217+
}
218+
},
219+
"UpdateReplacePolicy": "Delete",
220+
"DeletionPolicy": "Delete"
221+
}
222+
}
223+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// !cdk-integ pragma:ignore-assets
2+
import * as path from 'path';
3+
import { Asset } from '@aws-cdk/aws-s3-assets';
4+
import { App, Stack, StackProps } from '@aws-cdk/core';
5+
import { Construct } from 'constructs';
6+
import * as amplify from '../lib';
7+
8+
class TestStack extends Stack {
9+
constructor(scope: Construct, id: string, props?: StackProps) {
10+
super(scope, id, props);
11+
12+
const asset = new Asset(this, 'SampleAsset', {
13+
path: path.join(__dirname, './test-asset'),
14+
});
15+
16+
const amplifyApp = new amplify.App(this, 'App', {});
17+
amplifyApp.addBranch('main', { asset });
18+
}
19+
}
20+
21+
const app = new App();
22+
new TestStack(app, 'cdk-amplify-app-asset-deployment');
23+
app.synth();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello world! I am deployed on AWS Amplify using the addAssetDeployment method!

0 commit comments

Comments
 (0)
Please sign in to comment.