Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy child stack for core deploy #302

Merged
merged 29 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9d9381f
Deploy child stack for core deploy
huntharo Jan 22, 2023
99934e9
Update roles
huntharo Jan 23, 2023
b41eff8
Fix test
huntharo Jan 23, 2023
f14d355
Delete upload key before logging
huntharo Jan 23, 2023
1175b71
Fix error check
huntharo Jan 23, 2023
4bf6325
Fix export name
huntharo Jan 23, 2023
80b12e0
Exports
huntharo Jan 23, 2023
e29c673
Deploy fixes
huntharo Jan 23, 2023
ba12eb5
Update resource policy
huntharo Jan 23, 2023
adecc49
Try again
huntharo Jan 23, 2023
433388d
Fix principal import
huntharo Jan 23, 2023
bb6a0ae
Fix proxy response parsing
huntharo Jan 23, 2023
a362380
Add tag to demo, fix error
huntharo Jan 23, 2023
df64d52
Proxy createApp request
huntharo Jan 23, 2023
fdcb0e4
Attempt to set cross account app permissions
huntharo Jan 24, 2023
d681b07
Trigger build
huntharo Jan 24, 2023
a88f247
Trigger deploy of deployer
huntharo Jan 25, 2023
86189ba
Skip hotswap
huntharo Jan 25, 2023
e8376a8
Deploy edge-to-origin again
huntharo Jan 25, 2023
f5506d9
Trigger demo build
huntharo Jan 25, 2023
cf580f4
bump deployer again
huntharo Jan 25, 2023
5a58670
Export role not function arn
huntharo Jan 25, 2023
4d0df62
Import function to export role
huntharo Jan 25, 2023
810d8f1
Update role
huntharo Jan 26, 2023
8c0849c
Move role to a child stack
huntharo Jan 26, 2023
e85bd61
Fix role export
huntharo Jan 26, 2023
c226cb1
Change name back to avoid deploy problem on PR
huntharo Jan 26, 2023
05f0611
Let this be it - Fix the export reading
huntharo Jan 26, 2023
d80adcd
Change stack name back
huntharo Jan 26, 2023
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
68 changes: 66 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,14 @@ jobs:
- name: Set Hotswap Flag on PRs
run: |
if [ -n "${PR_NUMBER}" ]; then
echo "HOTSWAP_FLAG=--hotswap" >> $GITHUB_ENV
echo "HOTSWAP_FLAG=" >> $GITHUB_ENV
else
echo "HOTSWAP_FLAG=" >> $GITHUB_ENV
fi

- name: Deploy CDK Stack
env:
AWS_ACCOUNT_ID_CHILD: ${{ secrets.AWS_ACCOUNT_ID_CHILD }}
run: |
npx cdk deploy \
${HOTSWAP_FLAG} \
Expand All @@ -260,16 +262,19 @@ jobs:
echo "::set-output name=prefix::"${PREFIX}
echo "::set-output name=edgeDomain::"$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-edge-domain-name\`].Value" --no-paginate --output text)
echo "EDGE_DOMAIN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-edge-domain-name\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "EDGE_TO_ORIGIN_ROLE_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-role-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-role-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "DEPLOYER_LAMBDA_NAME="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-deployer-func-name\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "DEPLOYER_LAMBDA_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-deployer-func-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "DEMO_APP_LAMBDA_NAME="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-demo-app-func-name\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "DEMO_APP_LAMBDA_VERSION_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-demo-app-vers-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "NEXTJS_DEMO_APP_LAMBDA_NAME="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-nextjs-demo-app-func-name\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "NEXTJS_DEMO_APP_LAMBDA_VERSION_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-nextjs-demo-app-vers-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "RELEASE_APP_LAMBDA_NAME="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-release-app-func-name\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "RELEASE_APP_LAMBDA_VERSION_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghpublic-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-release-app-vers-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV

- name: Echo Exports
run: |
env | grep EDGE_DOMAIN
env | grep EDGE_
env | grep DEPLOYER_
env | grep DEMO_APP_
env | grep NEXTJS_DEMO_APP_
Expand Down Expand Up @@ -431,6 +436,65 @@ jobs:
echo Testing App Method Invocation
curl -H"accept: application/json" --fail https://${EDGE_DOMAIN}${PREFIX}/${RELEASE_APP_NAME}/${{ needs.build.outputs.releaseAppPackageVersion }}/api/refresh/${DEMO_APP_NAME}

#
# Child Account Deploy
#

# Change to Child Account Creds
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-session-name: microapps-ghpublic-build
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID_CHILD }}:role/builder-writeRole
aws-region: ${{ env.AWS_REGION }}

- name: Deploy Child CDK Stack
if: ${{ matrix.deployName == 'microapps-core' }}
env:
PARENT_DEPLOYER_LAMBDA_ARN: ${{ env.DEPLOYER_LAMBDA_ARN }}
run: |
npx cdk deploy \
${HOTSWAP_FLAG} \
--require-approval never ${{ matrix.deployName }}-child

- name: Get Child CDK Exports
if: ${{ matrix.deployName == 'microapps-core' }}
id: getChildCDKExports
run: |
echo "CHILD_DEPLOYER_LAMBDA_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghchild-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-deployer-func-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "CHILD_DEMO_APP_LAMBDA_NAME="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghchild-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-demo-app-func-name\`].Value" --no-paginate --output text) >> $GITHUB_ENV
echo "CHILD_DEMO_APP_LAMBDA_VERSION_ARN="$(aws cloudformation list-exports --query "Exports[?Name==\`${{ matrix.deployName }}-ghchild-${NODE_ENV}${{ needs.build.outputs.prSuffix }}-demo-app-vers-arn\`].Value" --no-paginate --output text) >> $GITHUB_ENV

- name: Echo Exports
if: ${{ matrix.deployName == 'microapps-core' }}
run: |
env | grep CHILD_

- name: Publish Demo App to Child MicroApps
if: ${{ matrix.deployName == 'microapps-core' }}
run: |
npx microapps-publish publish \
-a ${DEMO_APP_NAME} \
-t lambda-url \
--startup-type direct \
-n 0.0.0-child.1 \
-d ${CHILD_DEPLOYER_LAMBDA_ARN} \
-l ${CHILD_DEMO_APP_LAMBDA_VERSION_ARN} \
-s packages/demo-app/static_files \
-i index.html --overwrite

- name: Test Demo App - Child
if: ${{ matrix.deployName == 'microapps-core' }}
run: |
echo Testing App Frame Loading
curl -H"accept: text/html" --fail https://${EDGE_DOMAIN}${PREFIX}/${DEMO_APP_NAME}/
echo Testing App HTML Loading
curl -H"accept: text/html" --fail https://${EDGE_DOMAIN}${PREFIX}/${DEMO_APP_NAME}/0.0.0-child.1/index.html
echo Testing App Method Invocation
curl -H"accept: application/json" --fail "https://${EDGE_DOMAIN}${PREFIX}/${DEMO_APP_NAME}/0.0.0-child.1/serverIncrement?currValue=1"
echo Testing App Method Invocation on _next/data Route
curl -H"accept: application/json" --fail "https://${EDGE_DOMAIN}${PREFIX}/${DEMO_APP_NAME}/_next/data/0.0.0-child.1/serverIncrement?currValue=1"

build-jsii:
if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && contains( github.event.pull_request.labels.*.name, 'BUILD-JSII'))
runs-on: ubuntu-latest
Expand Down
19 changes: 19 additions & 0 deletions packages/cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import 'source-map-support/register';
import { App, Environment } from 'aws-cdk-lib';
import { MicroAppsStack } from '../lib/MicroApps';
import { MicroAppsChildStack } from '../lib/MicroAppsChild';
import { MicroAppsBuilder } from '../lib/MicroAppsBuilder';
import { SharedProps } from '../lib/SharedProps';

Expand Down Expand Up @@ -36,6 +37,24 @@ new MicroAppsStack(app, 'microapps-core', {
// We need to know the origin region for signing requests
// Accessing Aws.REGION will end up writing a Token into the config file
originRegion: shared.region,
...(process.env.AWS_ACCOUNT_ID_CHILD
? {
childDeployenRoleArns: [
`arn:aws:iam::${process.env.AWS_ACCOUNT_ID_CHILD}:role/microapps-core-ghchild-deployer${shared.envSuffix}${shared.prSuffix}`,
],
allowedFunctionUrlAccounts: [process.env.AWS_ACCOUNT_ID_CHILD],
}
: {}),
});

new MicroAppsChildStack(app, 'microapps-core-child', {
env,
stackName: `microapps-core-ghchild${shared.envSuffix}${shared.prSuffix}`,
autoDeleteEverything: true,
assetNameRoot: 'microapps-core-ghchild',
assetNameSuffix: `${shared.envSuffix}${shared.prSuffix}`,
parentDeployerLambdaARN: process.env.PARENT_DEPLOYER_LAMBDA_ARN || '',
edgeToOriginRoleARN: process.env.EDGE_TO_ORIGIN_ROLE_ARN || '',
});

new MicroAppsStack(app, 'microapps-basic', {
Expand Down
7 changes: 4 additions & 3 deletions packages/cdk/lib/DemoApp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Construct } from 'constructs';
import { Duration, RemovalPolicy } from 'aws-cdk-lib';
import { Duration, RemovalPolicy, Tags } from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambdaNodejs from 'aws-cdk-lib/aws-lambda-nodejs';
import * as logs from 'aws-cdk-lib/aws-logs';
Expand Down Expand Up @@ -45,7 +45,7 @@ export interface DemoAppProps {
* Represents a `DemoApp`
*/
export interface IDemoApp {
lambdaFunction: lambda.IFunction;
lambdaFunction: lambda.Function;
}

/**
Expand All @@ -57,7 +57,7 @@ export interface IDemoApp {
*/
export class DemoApp extends Construct implements IDemoApp {
private _lambdaFunction: lambda.Function;
public get lambdaFunction(): lambda.IFunction {
public get lambdaFunction(): lambda.Function {
return this._lambdaFunction;
}

Expand Down Expand Up @@ -100,5 +100,6 @@ export class DemoApp extends Construct implements IDemoApp {
if (removalPolicy !== undefined) {
this._lambdaFunction.applyRemovalPolicy(removalPolicy);
}
Tags.of(this._lambdaFunction).add('microapp-managed', 'true');
}
}
88 changes: 88 additions & 0 deletions packages/cdk/lib/MicroApps.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CfnOutput, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as r53 from 'aws-cdk-lib/aws-route53';
import { MicroApps, MicroAppsProps, MicroAppsTable } from '@pwrdrvr/microapps-cdk';
Expand Down Expand Up @@ -134,6 +135,31 @@ export interface MicroAppsStackProps extends StackProps {
* DynamoDB Table name - Needed for Edge routing
*/
readonly tableName?: string;

/**
* Optional child account deployer role ARNs that
* can invoke this parent deployer Lambda
*
* @default []
*/
readonly childDeployenRoleArns?: string[];

/**
* Account IDs allowed for cross-account Function URL invocations
*
* @example ['123456789012']
* @default []
*/
readonly allowedFunctionUrlAccounts?: string[];

/**
* Optional parent account origin request lambdas
* granted permission to invoke the function URL
* via a resource policy on each alias
*
* @default []
*/
readonly parentEdgeToOriginRoleArns?: string[];
}

export class MicroAppsStack extends Stack {
Expand Down Expand Up @@ -166,6 +192,9 @@ export class MicroAppsStack extends Stack {
rootPathPrefix,
originRegion,
tableName,
childDeployenRoleArns = [],
allowedFunctionUrlAccounts = [],
parentEdgeToOriginRoleArns = [],
} = props;

let removalPolicy: RemovalPolicy | undefined = undefined;
Expand Down Expand Up @@ -238,11 +267,32 @@ export class MicroAppsStack extends Stack {
originRegion,
table: table.table,
tableNameForEdgeToOrigin: tableName ? tableName : `${assetNameRoot}${assetNameSuffix}`,
allowedFunctionUrlAccounts,
...optionalAssetNameOpts,
...optionals3PolicyOpts,
...optionalCustomDomainOpts,
});

// Give the current version an alias
const deployerAlias = new lambda.Alias(this, 'deployer-alias', {
aliasName: 'currentVersion',
version: microapps.svcs.deployerFunc.currentVersion,
});
// Allow cross-account invokes if specified
// TODO: Actually handle the list of account ID
if (childDeployenRoleArns.length > 0) {
const childRole = iam.Role.fromRoleArn(this, 'deployer-child-role', childDeployenRoleArns[0]);

microapps.svcs.deployerFunc.addPermission('deployer-child-permission', {
principal: childRole,
scope: this,
});
deployerAlias.addPermission('deployer-child-permission-alias', {
principal: new iam.ArnPrincipal(childDeployenRoleArns[0]),
scope: this,
});
}

if (deployDemoApp) {
const demoApp = new DemoApp(this, 'demo-app', {
appName: 'demo-app',
Expand All @@ -254,10 +304,28 @@ export class MicroAppsStack extends Stack {
const appVersion = (demoApp.lambdaFunction as lambda.Function).currentVersion;
appVersion.applyRemovalPolicy(RemovalPolicy.RETAIN);

// TODO: Grant any parent Edge to Origin Lambdas permission to invoke this function
// via URL

// if (parentEdgeToOriginRoleArns && parentEdgeToOriginRoleArns.length > 0) {
// parentEdgeToOriginRoleArns.forEach((parentRoleArn) => {
// appVersion.addPermission('demo-app-permission', {
// principal: new iam.ArnPrincipal(parentRoleArn),
// scope: this,
// action: 'lambda:InvokeFunctionUrl',
// });
// });
// }

new CfnOutput(this, 'demo-app-func-name', {
value: `${demoApp.lambdaFunction.functionName}`,
exportName: `${this.stackName}-demo-app-func-name`,
});

new CfnOutput(this, 'demo-app-vers-arn', {
value: `${appVersion.functionArn}`,
exportName: `${this.stackName}-demo-app-vers-arn`,
});
}

if (deployReleaseApp) {
Expand Down Expand Up @@ -305,11 +373,27 @@ export class MicroAppsStack extends Stack {
});
}

// Import the Edge to Origin Lambda function so we can get the role ARN
const edgeToOriginFunc = lambda.Function.fromFunctionArn(
this,
'edge-to-origin-func-import',
// The functionArn will have the version number at the end, so we need to
// remove that to get the ARN of the function itself
(microapps.edgeToOrigin?.edgeToOriginFunction?.functionArn || '')
.split(':')
.slice(0, 7)
.join(':'),
);

// Exports
new CfnOutput(this, 'edge-domain-name', {
value: domainNameEdge ? domainNameEdge : microapps.cf.cloudFrontDistro.domainName,
exportName: `${this.stackName}-edge-domain-name`,
});
new CfnOutput(this, 'edge-to-origin-role-arn', {
value: `${microapps.edgeToOrigin?.edgeToOriginFunction?.role?.roleArn}`,
exportName: `${this.stackName}-edge-to-origin-arn`,
});
new CfnOutput(this, 'dynamodb-table-name', {
value: `${tableName ? tableName : microapps.svcs.table.tableName}`,
exportName: `${this.stackName}-dynamodb-table-name`,
Expand All @@ -318,5 +402,9 @@ export class MicroAppsStack extends Stack {
value: `${microapps.svcs.deployerFunc.functionName}`,
exportName: `${this.stackName}-deployer-func-name`,
});
new CfnOutput(this, 'deployer-func-arn', {
value: `${microapps.svcs.deployerFunc.functionArn}`,
exportName: `${this.stackName}-deployer-func-arn`,
});
}
}
Loading