Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-amplify-alpha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,26 @@ const amplifyApp = new amplify.App(this, 'MyApp', {
});
```

## Compute role

This integration, enables you to assign an IAM role to the Amplify SSR Compute service to allow your server-side rendered (SSR) application to securely access specific AWS resources based on the role's permissions.

For example, you can allow your app's SSR compute functions to securely access other AWS services or resources, such as Amazon Bedrock or an Amazon S3 bucket, based on the permissions defined in the assigned IAM role.

For more information, see [Adding an SSR Compute role to allow access to AWS resources](https://docs.aws.amazon.com/amplify/latest/userguide/amplify-SSR-compute-role.html).

By default, a new role is created when `platform` is `Platform.WEB_COMPUTE` or `Platform.WEB_DYNAMIC`.
If you want to assign an IAM role to the APP, set `compute` to the role:

```ts
declare const computeRole: iam.Role;

const amplifyApp = new amplify.App(this, 'MyApp', {
platform: amplify.Platform.WEB_COMPUTE,
computeRole,
});
```

## Cache Config

Amplify uses Amazon CloudFront to manage the caching configuration for your hosted applications. A cache configuration is applied to each app to optimize for the best performance.
Expand Down
31 changes: 30 additions & 1 deletion packages/@aws-cdk/aws-amplify-alpha/lib/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as iam from 'aws-cdk-lib/aws-iam';
import { IResource, Lazy, Resource, SecretValue } from 'aws-cdk-lib/core';
import { IResource, Lazy, Resource, SecretValue, ValidationError } from 'aws-cdk-lib/core';
import { Construct } from 'constructs';
import { CfnApp } from 'aws-cdk-lib/aws-amplify';
import { BasicAuth } from './basic-auth';
Expand Down Expand Up @@ -176,6 +176,14 @@ export interface AppProps {
* @default CacheConfigType.AMPLIFY_MANAGED
*/
readonly cacheConfigType?: CacheConfigType;

/**
* The IAM role for an SSR app.
* The Compute role allows the Amplify Hosting compute service to securely access specific AWS resources based on the role's permissions.
*
* @default undefined - a new role is created when `platform` is `Platform.WEB_COMPUTE` or `Platform.WEB_DYNAMIC`, otherwise no compute role
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the compute role is used when SSR apps access AWS resources, I thought it would be more user-friendly to auto-generate it.

Ref: https://aws.amazon.com/jp/blogs/mobile/iam-compute-roles-for-server-side-rendering-with-aws-amplify-hosting/

Please let me know if you have any opinions on this.

*/
readonly computeRole?: iam.IRole;
}

/**
Expand Down Expand Up @@ -224,6 +232,11 @@ export class App extends Resource implements IApp, iam.IGrantable {
*/
public readonly grantPrincipal: iam.IPrincipal;

/**
* The IAM role for an SSR app.
*/
public readonly computeRole?: iam.IRole;

private readonly customRules: CustomRule[];
private readonly environmentVariables: { [name: string]: string };
private readonly autoBranchEnvironmentVariables: { [name: string]: string };
Expand All @@ -242,6 +255,21 @@ export class App extends Resource implements IApp, iam.IGrantable {
});
this.grantPrincipal = role;

let computedRole: iam.IRole | undefined;
const isSSR = props.platform === Platform.WEB_COMPUTE || props.platform === Platform.WEB_DYNAMIC;

if (props.computeRole) {
if (!isSSR) {
throw new ValidationError('`computeRole` can only be specified for `Platform.WEB_COMPUTE` or `Platform.WEB_DYNAMIC`.', this);
}
computedRole = props.computeRole;
} else if (isSSR) {
computedRole = new iam.Role(this, 'ComputeRole', {
assumedBy: new iam.ServicePrincipal('amplify.amazonaws.com'),
});
}
this.computeRole = computedRole;

const sourceCodeProviderOptions = props.sourceCodeProvider?.bind(this);

const app = new CfnApp(this, 'Resource', {
Expand All @@ -265,6 +293,7 @@ export class App extends Resource implements IApp, iam.IGrantable {
: { enableBasicAuth: false },
buildSpec: props.buildSpec && props.buildSpec.toBuildSpec(),
cacheConfig: props.cacheConfigType ? { type: props.cacheConfigType } : undefined,
computeRoleArn: this.computeRole?.roleArn,
customRules: Lazy.any({ produce: () => this.customRules }, { omitEmptyArray: true }),
description: props.description,
environmentVariables: Lazy.any({ produce: () => renderEnvironmentVariables(this.environmentVariables) }, { omitEmptyArray: true }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SecretValue, Stack } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as amplify from '@aws-cdk/aws-amplify-alpha';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as iam from 'aws-cdk-lib/aws-iam';

class Fixture extends Stack {
constructor(scope: Construct, id: string) {
Expand Down
50 changes: 50 additions & 0 deletions packages/@aws-cdk/aws-amplify-alpha/test/app.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Template } from 'aws-cdk-lib/assertions';
import * as codebuild from 'aws-cdk-lib/aws-codebuild';
import * as codecommit from 'aws-cdk-lib/aws-codecommit';
import * as iam from 'aws-cdk-lib/aws-iam';
import { SecretValue, Stack } from 'aws-cdk-lib';
import * as amplify from '../lib';

Expand Down Expand Up @@ -478,3 +479,52 @@ test.each([amplify.CacheConfigType.AMPLIFY_MANAGED, amplify.CacheConfigType.AMPL
},
});
});

test('create a SSR app with auto-generate compute role', () => {
// WHEN
new amplify.App(stack, 'App', {
platform: amplify.Platform.WEB_COMPUTE,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Amplify::App', {
Platform: amplify.Platform.WEB_COMPUTE,
ComputeRoleArn: {
'Fn::GetAtt': ['AppComputeRole426920E4', 'Arn'],
},
});

Template.fromStack(stack).resourceCountIs('AWS::IAM::Role', 2);
});

test('create a SSR app with compute role', () => {
// WHEN
const computeRole = new iam.Role(stack, 'ComputeRole', {
assumedBy: new iam.ServicePrincipal('amplify.amazonaws.com'),
});

new amplify.App(stack, 'App', {
platform: amplify.Platform.WEB_COMPUTE,
computeRole,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::Amplify::App', {
Platform: amplify.Platform.WEB_COMPUTE,
ComputeRoleArn: stack.resolve(computeRole.roleArn),
});
});

test('throws when compute role is set with a non SSR app', () => {
// WHEN
const computeRole = new iam.Role(stack, 'ComputeRole', {
assumedBy: new iam.ServicePrincipal('amplify.amazonaws.com'),
});

expect(() => {
new amplify.App(stack, 'App', {
platform: amplify.Platform.WEB,
computeRole,
});
}).toThrow('`computeRole` can only be specified for `Platform.WEB_COMPUTE` or `Platform.WEB_DYNAMIC`.');
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
{
"Resources": {
"ComputeRole65BDBE3E": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "amplify.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"AppRole1AF9B530": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "amplify.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"AppF1B96344": {
"Type": "AWS::Amplify::App",
"Properties": {
"BasicAuthConfig": {
"EnableBasicAuth": false
},
"ComputeRoleArn": {
"Fn::GetAtt": [
"ComputeRole65BDBE3E",
"Arn"
]
},
"IAMServiceRole": {
"Fn::GetAtt": [
"AppRole1AF9B530",
"Arn"
]
},
"Name": "App",
"Platform": "WEB_COMPUTE"
}
},
"AppmainF505BAED": {
"Type": "AWS::Amplify::Branch",
"Properties": {
"AppId": {
"Fn::GetAtt": [
"AppF1B96344",
"AppId"
]
},
"BranchName": "main",
"EnableAutoBuild": true,
"EnablePullRequestPreview": true
}
}
},
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading