aws-amplify / amplify-backend

Home to all tools related to Amplify's code-first DX (Gen 2) for building fullstack apps on AWS
Apache License 2.0
175 stars 61 forks source link

add support for `cdk diff` #1764

Open josefaidt opened 3 months ago

josefaidt commented 3 months ago

Environment information

System:
  OS: macOS 14.5
  CPU: (10) arm64 Apple M1 Pro
  Memory: 159.56 MB / 32.00 GB
  Shell: /opt/homebrew/bin/fish
Binaries:
  Node: 20.15.0 - ~/.local/state/fnm_multishells/82389_1721400785562/bin/node
  Yarn: undefined - undefined
  npm: 10.8.1 - ~/.local/state/fnm_multishells/82389_1721400785562/bin/npm
  pnpm: 9.4.0 - ~/.local/state/fnm_multishells/82389_1721400785562/bin/pnpm
NPM Packages:
  @aws-amplify/backend: 1.0.4
  @aws-amplify/backend-cli: 1.1.0
  aws-amplify: 6.3.8
  aws-cdk: 2.147.2
  aws-cdk-lib: 2.147.2
  typescript: 5.5.3
AWS environment variables:
  AWS_PROFILE = josef
  AWS_REGION = us-east-1
  AWS_STS_REGIONAL_ENDPOINTS = regional
  AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1
  AWS_SDK_LOAD_CONFIG = 1
No CDK environment variables

Description

After making changes to my personal sandbox and opening a PR to merge to main/production, it would help the review process to better understand resource changes that are being made beyond the backend code diff.

For example,

  1. team member A adds a new Function with access to Data
  2. team member A deploys changes to their sandbox
  3. team member A verifies changes and opens PR
  4. PR check runs and emits diff artifacts
  5. team member B reviews diff artifacts
  6. team member B approves PR and merges to main

this can be a part of check

npx ampx check --diff [--app-id <amplify-app-id>] [--branch <git-branch>]

or on its own

npx ampx diff [--app-id <amplify-app-id>] [--branch <git-branch>]

Currently diffs can be executed by recreating CDK commands

for branch

cdk diff \
  --ci \
  --app "tsx amplify/backend.ts" \
  --output .amplify/artifacts/cdk.out \
  --context amplify-backend-name=main \ # this is your git branch
  --context amplify-backend-namespace=<amplify-app-id> \ # this is your Amplify app ID
  --context amplify-backend-type=branch

for sandbox

cdk diff \
  --ci \
  --app "tsx amplify/backend.ts" \
  --output .amplify/artifacts/cdk.out \
  --context amplify-backend-name=josef \ # this is $(whoami)
  --context amplify-backend-namespace=discord-interactions \ # this is package.json#name
  --context amplify-backend-type=sandbox
ykethan commented 3 months ago

Marking as feature request.

johnf commented 1 month ago

@josefaidt FYI, one thing I've noticed since I've been able to use diffs is that even straight after a deploy, there are always differences, even without code changes. So, there might be some idempotency issues in the underlying constructs.

For example

Bundling asset amplify-d1gtpvh7spqh9p-backendadminprod-branch-b36a66850a/AmplifyBranchLinker/CustomResourceLambda/Code/Stage...

  packages/backend-admin/.amplify/artifacts/cdk.out/bundling-temp-4e05b95e2f7fb31244ad681a912c5072f35284c98910154ea00d840059decb36/index.js  1.2mb ⚠️

⚡ Done in 28ms
Bundling asset amplify-d1gtpvh7spqh9p-backendadminprod-branch-b36a66850a/auth/SecretFetcherResourceProviderLambda/Code/Stage...

  packages/backend-admin/.amplify/artifacts/cdk.out/bundling-temp-bd4e411f9cbd7cab4accee678f878aaa537d6823982356dc2379b33a2a4baf53/index.js  305.9kb

⚡ Done in 10ms
[Warning at /amplify-xxx-backendadminprod-branch-xxx/data/amplifyData/GraphQLAPI] @predictions is deprecated. This functionality will be removed in the next major release.
[Warning at /amplify-xxx-backendadminprod-branch-xxx/data/amplifyData/GraphQLAPI] @manyToMany is deprecated. This functionality will be removed in the next major release.
[Warning at /amplify-xxxx-backendadminprod-branch-xxxx/data/amplifyData/GraphQLAPI] @searchable is deprecated. This functionality will be removed in the next major release.
Stack amplify-xxxx-backendadminprod-branch-xxxx
Resources
[~] AWS::CloudFormation::Stack auth.NestedStack/auth.NestedStackResource authxxxx 
 └─ [~] TemplateURL
     └─ [~] .Fn::Join:
         └─ @@ -13,6 +13,6 @@
            [ ]     {
            [ ]       "Fn::Sub": "cdk-xxxxassets-${AWS::AccountId}-${AWS::Region}"
            [ ]     },
            [-]     "/cb444fe90255ade753ff156ebc9b2f1bda331fc579c216def4fe5583b6cbd8de.json"
            [+]     "/d608b82054fdc22f26d2ebf0ad31f8da53a9de955eae838fc21965902c4a2b64.json"
            [ ]   ]
            [ ] ]
[~] AWS::CloudFormation::Stack data.NestedStack/data.NestedStackResource dataxxx
 └─ [~] TemplateURL
     └─ [~] .Fn::Join:
         └─ @@ -13,6 +13,6 @@
            [ ]     {
            [ ]       "Fn::Sub": "cdk-xxx-assets-${AWS::AccountId}-${AWS::Region}"
            [ ]     },
            [-]     "/a73f28963b6adae78ab7be28904a93e4df5cc6303c41882c05e7defb4ba2f1ba.json"
            [+]     "/e72957a2e5949da1ff343471a691e2d82d73bea40b925b27158c9eadc21d43dc.json"
            [ ]   ]
            [ ] ]

Stack amplify-xxx-backendadminprod-branch-xxx-auth179371D7-YW65J8PXS1H9
Resources
[~] Custom::SecretFetcherResource auth/AZURE_AD_CLIENT_IDSecretFetcherResource AZUREADCLIENTIDSecretFetcherResource 
 └─ [~] secretLastUpdated
     ├─ [-] 1726614685011
     └─ [+] 1726615686452
[~] Custom::SecretFetcherResource auth/AZURE_AD_CLIENT_SECRETSecretFetcherResource AZUREADCLIENTSECRETSecretFetcherResource 
 └─ [~] secretLastUpdated
     ├─ [-] 1726614685011
     └─ [+] 1726615686452

Stack amplify-xxxx-backendadminprod-branch-b36a66850a-data7552DF31-EBPERYWBKO06
Resources
[~] AWS::AppSync::ApiKey data/amplifyData/GraphQLAPI/DefaultApiKey amplifyDataGraphQLAPIDefaultApiKey1C8ED374 
 └─ [~] Expires
     ├─ [-] 1758150685
     └─ [+] 1758151686

Stack amplify-dxxx-backendadminprod-bra-amplifyDataAmplifyTableManagerNestedStackA-N2S40YJVA23C
There were no differences
Stack amplify-xxx-backendadminprod-bra-amplifyDataFavourInfoNestedStackFavourInfo-104HTZGUDERS7
There were no differences
Stack amplify-xxx-backendadminprod-bra-amplifyDataFavourInfoVersionNestedStackFav-89P23LWED8LQ
There were no differences
Stack amplify-xxx-backendadminprod-bra-amplifyDataConnectionStackNestedStackConne-1MRIAROINF4GS
There were no differences
Stack amplify-xxx-backendadminprod-branch-b36a66850a-storage0EC3F24A-6Q611OREY5KF
There were no differences
i5d6 commented 1 month ago

Implementation Steps:

  1. Review the AWS Amplify backend code and ensure that the cdk diff command is properly integrated.
  2. Configure access controls using AWS IAM roles and permissions to restrict access to the cdkdiff command.
  3. Set up auditing mechanisms using AWS CloudTrail or AWS CloudWatch to track changes made using the cdkdiff command.
  4. Develop a validation and approval process for changes proposed by the cdk diff command.
  5. Implement IaC best practices by using version control systems and tracking changes to IaC templates.

Example Code (AWS CDK):

import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';

export class AmplifyBackendStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // Define IAM role for cdk diff command
    const cdkDiffRole = new iam.Role(this, 'CdkDiffRole', {
      assumedBy: new iam.ServicePrincipal('cdk.amazonaws.com'),
    });

    // Grant necessary permissions to cdk diff role
    cdkDiffRole.addToPolicy(
      new iam.PolicyStatement({
        effect: iam.Effect.ALLOW,
        actions: ['cloudformation:DescribeStacks', 'cloudformation:DescribeStackResources'],
        resources: ['*'],
      }),
    );

    // Define validation and approval process for changes
    const validationLambda = new cdk.aws_lambda.Function(
      this,
      'ValidationLambda',
      {
        runtime: cdk.aws_lambda.Runtime.NODEJS_14_X,
        handler: 'index.handler',
        code: cdk.aws_lambda.Code.fromAsset('lambda'),
      },
    );

    // Grant validation lambda execution role to cdk diff role
    cdkDiffRole.grantExecute(validationLambda);

    // Define IaC template for infrastructure changes
    const infraTemplate = new cdk.aws_cloudformation.CloudFormationStack(
      this,
      'InfraTemplate',
      {
        templateBody: JSON.stringify({
          Resources: {
            MyResource: {
              Type: 'AWS::EC2::Instance',
              Properties: {
                ImageId: 'ami-abc123',
              },
            },
          },
        }),
      },
    );

    // Use version control system to track changes to IaC template
    const gitRepo = new cdk.aws_codecommit.Repository(
      this,
      'GitRepo',
      {
        repositoryName: 'my-infra-repo',
      },
    );

    // Grant git repo access to cdk diff role
    cdkDiffRole.grantRead(gitRepo);
  }
}