I am using Pulumi to build an SFTP server in AWS with authentication via API Gateway and a Lambda function. For some reason, the transfer server is unable to verify access to the API gateway. I receieve the following error on pulumi up: error: 1 error occurred: creating Transfer Server: InvalidRequestException: Unable to verify access to API {URL} Here is the relevant Pulumi code. The problem is likely with sftpServerPolicy and sftpServer (at the bottom). // Create an S3 bucket to store files accessible via SFTP const sftpBucket = new aws.s3.Bucket(`${config.prefix}-testSftp-bucket`, { acl: "private", tags: config.tags, }); // Create a secret for the SFTP login const sftpSecret = new aws.secretsmanager.Secret(`${config.prefix}-testSftp-secret`, { tags: config.tags, }); // Define the IAM role and policy that allows the Lambda function to access S3 resources const authLambdaRole = new aws.iam.Role(`${config.prefix}-testSftpAuth-lambda-role`, { assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ Service: "lambda.amazonaws.com" }), }); const authLambdaSecretsPolicy = new aws.iam.RolePolicy(`${config.prefix}-testSftpAuth-lambdaSecretsPolicy`, { role: authLambdaRole.id, policy: sftpSecret.arn.apply(sftpSecretArn => JSON.stringify({ Version: "2012-10-17", Statement: [{ Action: ["secretsmanager:GetSecretValue"], Effect: "Allow", Resource: sftpSecretArn, }], })), }); // Zip auth Lambda source and dependencies const authLambdaDir = "../functions/testSftpAuth"; const authLambdaZipPath = `${authLambdaDir}/testSftpAuth.zip`; packageLambda(authLambdaDir, authLambdaZipPath); // Create a Lambda to authenticate SFTP logins const authLambda = new aws.lambda.Function(`${config.prefix}-testSftpAuth`, { code: new pulumi.asset.AssetArchive({ ".": new pulumi.asset.FileArchive(authLambdaZipPath) }), role: authLambdaRole.arn, handler: "function.handler", runtime: aws.lambda.Runtime.Python3d12, environment: { variables: { SECRET_ARN: sftpSecret.arn }, }, tags: config.tags, }); // Create an API gateway for auth Lambda const authApi = new aws.apigatewayv2.Api(`${config.prefix}-testSftpAuth-api`, { protocolType: "HTTP", tags: config.tags, }); // Associate the API gateway with the Lambda const authApiIntegration = new aws.apigatewayv2.Integration(`${config.prefix}-testSftpAuth-integration`, { apiId: authApi.id, integrationType: "AWS_PROXY", integrationUri: authLambda.arn, }); // Create a route for the auth API const authApiRoute = new aws.apigatewayv2.Route(`${config.prefix}-testSftpAuth-route`, { apiId: authApi.id, routeKey: "POST /auth", target: authApiIntegration.id.apply(authApiIntegrationId => `integrations/${authApiIntegrationId}`), }); // Create a stage for the auth API const authApiStage = new aws.apigatewayv2.Stage(`${config.prefix}-testSftpAuth-stage`, { apiId: authApi.id, autoDeploy: true, tags: config.tags, }); // Create IAM role and policy for the SFTP server to access S3 const sftpRole = new aws.iam.Role(`${config.prefix}-testSftp-role`, { assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ Service: "transfer.amazonaws.com" }), }); const sftpServerPolicy = new aws.iam.RolePolicy(`${config.prefix}-testSftp-policy`, { role: sftpRole.id, policy: pulumi.all([authApiStage.executionArn, sftpBucket.arn]).apply(([authApiStageExecutionArn, sftpBucketArn]) => JSON.stringify({ Version: "2012-10-17", Statement: [ { Action: ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"], Effect: "Allow", Resource: `${sftpBucketArn}/*`, }, { Action: ["execute-api:Invoke"], Effect: "Allow", Resource: `${authApiStageExecutionArn}/POST/auth` } ], }) ) }); const sftpServer = new aws.transfer.Server(`${config.prefix}-testSftp-server`, { endpointType: "PUBLIC", identityProviderType: "API_GATEWAY", invocationRole: sftpRole.arn, url: authApiStage.invokeUrl.apply(invokeUrl => `${invokeUrl}/auth`), loggingRole: sftpRole.arn, }); What could be the problem?
TypeScriptThe issue seems to be related to the IAM role policies and the API Gateway URL not being correctly set up or accessible by the AWS Transfer Server. Let's review and adjust the IAM policies and ensure that the API Gateway URL is correctly used.
Here is the corrected Pulumi code:
import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; // Configuration setup const config = new pulumi.Config(); const prefix = config.require("prefix"); const tags = { "Project": "SFTPServer" }; // Create an S3 bucket to store files accessible via SFTP const sftpBucket = new aws.s3.Bucket(`${prefix}-testSftp-bucket`, { acl: "private", tags: tags, }); // Create a secret for the SFTP login const sftpSecret = new aws.secretsmanager.Secret(`${prefix}-testSftp-secret`, { tags: tags, }); // Define the IAM role and policy that allows the Lambda function to access S3 resources const authLambdaRole = new aws.iam.Role(`${prefix}-testSftpAuth-lambda-role`, { assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ Service: "lambda.amazonaws.com" }), }); const authLambdaSecretsPolicy = new aws.iam.RolePolicy(`${prefix}-testSftpAuth-lambdaSecretsPolicy`, { role: authLambdaRole.id, policy: sftpSecret.arn.apply(sftpSecretArn => JSON.stringify({ Version: "2012-10-17", Statement: [{ Action: ["secretsmanager:GetSecretValue"], Effect: "Allow", Resource: sftpSecretArn, }], })), }); // Zip auth Lambda source and dependencies const authLambdaDir = "../functions/testSftpAuth"; const authLambdaZipPath = `${authLambdaDir}/testSftpAuth.zip`; packageLambda(authLambdaDir, authLambdaZipPath); // Create a Lambda to authenticate SFTP logins const authLambda = new aws.lambda.Function(`${prefix}-testSftpAuth`, { code: new pulumi.asset.AssetArchive({ ".": new pulumi.asset.FileArchive(authLambdaZipPath) }), role: authLambdaRole.arn, handler: "function.handler", runtime: aws.lambda.Runtime.Python3d12, environment: { variables: { SECRET_ARN: sftpSecret.arn }, }, tags: tags, }); // Create an API gateway for auth Lambda const authApi = new aws.apigatewayv2.Api(`${prefix}-testSftpAuth-api`, { protocolType: "HTTP", tags: tags, }); // Associate the API gateway with the Lambda const authApiIntegration = new aws.apigatewayv2.Integration(`${prefix}-testSftpAuth-integration`, { apiId: authApi.id, integrationType: "AWS_PROXY", integrationUri: authLambda.arn, }); // Create a route for the auth API const authApiRoute = new aws.apigatewayv2.Route(`${prefix}-testSftpAuth-route`, { apiId: authApi.id, routeKey: "POST /auth", target: authApiIntegration.id.apply(authApiIntegrationId => `integrations/${authApiIntegrationId}`), }); // Create a stage for the auth API const authApiStage = new aws.apigatewayv2.Stage(`${prefix}-testSftpAuth-stage`, { apiId: authApi.id, autoDeploy: true, tags: tags, }); // Create IAM role and policy for the SFTP server to access S3 and API Gateway const sftpRole = new aws.iam.Role(`${prefix}-testSftp-role`, { assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ Service: "transfer.amazonaws.com" }), }); const sftpServerPolicy = new aws.iam.RolePolicy(`${prefix}-testSftp-policy`, { role: sftpRole.id, policy: pulumi.all([authApiStage.executionArn, sftpBucket.arn]).apply(([authApiStageExecutionArn, sftpBucketArn]) => JSON.stringify({ Version: "2012-10-17", Statement: [ { Action: ["s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject"], Effect: "Allow", Resource: `${sftpBucketArn}/*`, }, { Action: ["execute-api:Invoke"], Effect: "Allow", Resource: `${authApiStageExecutionArn}/*` } ], }) ) }); const sftpServer = new aws.transfer.Server(`${prefix}-testSftp-server`, { endpointType: "PUBLIC", identityProviderType: "API_GATEWAY", invocationRole: sftpRole.arn, url: authApiStage.invokeUrl.apply(invokeUrl => `${invokeUrl}/auth`), loggingRole: sftpRole.arn, }); // Export the API Gateway URL for verification export const apiGatewayUrl = authApiStage.invokeUrl;
Key changes:
- Updated the
sftpServerPolicy
to allow invoking the API Gateway by using${authApiStageExecutionArn}/*
instead of${authApiStageExecutionArn}/POST/auth
. - Ensured the
url
insftpServer
correctly points to the API Gateway invoke URL.
This should resolve the issue of the AWS Transfer server being unable to verify access to the API Gateway.
- Updated the