aws-amplify / amplify-studio

AWS Amplify Studio (Formerly Admin UI)
136 stars 31 forks source link

User: arn:aws:iam::xxxxxxxxxxxx:user/<AMPLIFY-CONFIGURE-USER> is not authorized to perform: apigateway:TagResource on resource: arn:aws:apigateway:eu-west-3::/apis/<APIID>/stages (Service: AmazonApiGatewayV2; Status Code: 403; Error Code: AccessDeniedException; Request ID: xxxx; Proxy: null) #889

Open oyatrides opened 1 year ago

oyatrides commented 1 year ago

How did you install the Amplify CLI?

npm

If applicable, what version of Node.js are you using?

No response

Amplify CLI Version

11.0.3

What operating system are you using?

Ubuntu

Did you make any manual changes to the cloud resources managed by Amplify? Please describe the changes made.

No manual changes on concerned resources

Describe the bug

Hello, I am trying to create an API Gateway websocket with amplify add custom (cdk). I am using cdk V1 because aws_apigatewayv2 of the cdk V2 is in alpha.

Here's my package.json dependencies :

  "dependencies": {
    "@aws-amplify/cli-extensibility-helper": "^2.0.0",
    "@aws-cdk/core": "~1.198.0",
    "@aws-cdk/aws-apigatewayv2": "~1.198.0",
    "@aws-cdk/aws-apigatewayv2-integrations": "~1.198.0",
    "@aws-cdk/aws-lambda": "~1.198.0"
  },
  "devDependencies": {
    "typescript": "^4.2.4"
  }

Here's the relevant CDK code used:

const webSocketApi = new WebSocketApi(this, "XXXXX", {
      apiName: "XXX",
      // this is the default expression, just putting it here for verbose purpose
      routeSelectionExpression: "$request.body.action",
      connectRouteOptions: {
        integration: new WebSocketLambdaIntegration(
          "connectIntegration",
          connectHandler
        ),
      },
      disconnectRouteOptions: {
        integration: new WebSocketLambdaIntegration(
          "disconnectIntegration",
          disconnectHandler
        ),
      },
    });

    new WebSocketStage(this, "websocketStage", {
      webSocketApi,
      stageName: cdk.Fn.ref("env"),
      autoDeploy: true,
    });

Everything is generated fine if I remove the WebsocketStage bit. The error encountered is the one written in the subject which is :

User: arn:aws:iam::XXXX:user/ is not authorized to perform: apigateway:TagResource on resource: arn:aws:apigateway:eu-west-3::/apis//stages (Service: AmazonApiGatewayV2; Status Code: 403; Error Code: AccessDeniedException; Request ID: XXX; Proxy: null)

it seems that the AdministratorAccess-Amplify policy attached to the amplify created user through amplify configure lacks the apigateway:TagResource permission.

I can't find a way to append a statement to this existing policy like the following :

amplifyPolicy.appendToPolicy(
new PolicyStatement({
        actions: [
          "apigateway:TagResource",
        ],
        resources: [*],
      })
)

I would like to do it with the CDK, and not manually, so that my colleagues could access it without manual interaction with the console, on every user/account-environment we have.

Or maybe this AWs managed policy should contain this statement by default.

Expected behavior

The custom resource Stage of my custom websocket should be created without a permission error.

Reproduction steps

  1. Create an amplify user through amplify configure
  2. Attach to it the policy arn:aws:iam::aws:policy/AdministratorAccess-Amplify
  3. amplify add custom
  4. use cdk v1
  5. import { WebSocketLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations"; import { WebSocketApi, WebSocketStage } from "@aws-cdk/aws-apigatewayv2";
  6. code a websocket API (see bug description for reference)
  7. Initialize a WebSocketStage (see bug description for reference)
  8. amplify push

Project Identifier

No response

Log output

``` User: arn:aws:iam::XXXX:user/ is not authorized to perform: apigateway:TagResource on resource: arn:aws:apigateway:eu-west-3::/apis//stages (Service: AmazonApiGatewayV2; Status Code: 403; Error Code: AccessDeniedException; Request ID: XXX; Proxy: null) ```

Additional information

Thank you for help !

Before submitting, please confirm:

oyatrides commented 1 year ago

So I tried to add in the console the required ApiGateway permission on my amplify user, but I discovered that the "apigateway:TagResource" action seem not to exist. The console didn't tell me about this one.However, I tried authorizing every APIGateway and ApiGatewayV2 actions through apigateway: and apigatewayv2: actions, and the cdk amplify push actually managed to build the stage of the websocket.

So I would guess that the problem is either an API Gateway one, where tagRessource is an anomaly and shouldn't exist, or it's an Amplify one, where it lacks the proper permission in the AdministratorAccess-Amplify Policy, or maybe even both ?

I hope this helps

josefaidt commented 1 year ago

Hey @oyatrides :wave: thanks for raising this! Although I was not able to reproduce by applying tags via amplify/backend/tags.json and creating a REST API via the CLI with amplify add api, I was able to reproduce by creating a similar custom resource.

  1. amplify init -y (be sure to initialize with an IAM user that has AdministratorAccess-Amplify
  2. amplify add custom
  3. Make the necessary adjustments to the amplify/backend/package.json file to use CDK v1
  4. Copy the following into the newly-created cdk-stack.ts

    import * as cdk from '@aws-cdk/core'
    import * as AmplifyHelpers from '@aws-amplify/cli-extensibility-helper'
    import { WebSocketApi, WebSocketStage } from '@aws-cdk/aws-apigatewayv2'
    import { WebSocketLambdaIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'
    import * as lambda from '@aws-cdk/aws-lambda'
    
    const fakeHandler = `
      exports.handler = async (event) => {
        console.log('event', event)
        return {
          statusCode: 200,
          body: JSON.stringify('Hello from Lambda!'),
        }
      }
    `
    
    export class cdkStack extends cdk.Stack {
      constructor(
        scope: cdk.Construct,
        id: string,
        props?: cdk.StackProps,
        amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps
      ) {
        super(scope, id, props)
        /* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
        new cdk.CfnParameter(this, 'env', {
          type: 'String',
          description: 'Current Amplify CLI env name',
        })
    
        const connectHandler = new lambda.Function(this, 'connectHandler', {
          runtime: lambda.Runtime.NODEJS_16_X,
          handler: 'index.handler',
          code: lambda.Code.fromInline(fakeHandler),
        })
    
        const disconnectHandler = new lambda.Function(this, 'disconnectHandler', {
          runtime: lambda.Runtime.NODEJS_16_X,
          handler: 'index.handler',
          code: lambda.Code.fromInline(fakeHandler),
        })
    
        const webSocketApi = new WebSocketApi(this, 'XXXXX', {
          apiName: 'XXX',
          // this is the default expression, just putting it here for verbose purpose
          routeSelectionExpression: '$request.body.action',
          connectRouteOptions: {
            integration: new WebSocketLambdaIntegration(
              'connectIntegration',
              connectHandler
            ),
          },
          disconnectRouteOptions: {
            integration: new WebSocketLambdaIntegration(
              'disconnectIntegration',
              disconnectHandler
            ),
          },
        })
    
        new WebSocketStage(this, 'websocketStage', {
          webSocketApi,
          stageName: cdk.Fn.ref('env'),
          autoDeploy: true,
        })
      }
    }
  5. amplify push -y
  6. observe error
  7. Add the following IAM policy as an inline policy to the IAM user
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "apigateway:TagResource"
                ],
                "Resource": "*",
                "Condition": {
                    "ForAnyValue:StringEquals": {
                        "aws:CalledVia": [
                            "cloudformation.amazonaws.com"
                        ]
                    }
                }
            }
        ]
    }
  8. re-attempt push with amplify push -y
  9. observe successful

Transferring to amplify-studio to add this policy to the managed AdministratorAccess-Amplify policy