我有两个 AWS 堆栈:
一个有一个 dynamoDB 表并“导出”(到 appConfig)tableArn、tableName 和 tableRoleArn(理想情况下应该允许访问该表)。
import { App, Stack, StackProps } from '@aws-cdk/core';
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import * as cdk from '@aws-cdk/core';
import * as appconfig from '@aws-cdk/aws-appconfig';
import { Effect, PolicyStatement, Role, ServicePrincipal } from '@aws-cdk/aws-iam';
export class ExportingStack extends Stack {
constructor(scope: App, id: string, props: StackProps) {
super(scope, id, props);
const table = new dynamodb.Table(this, id, {
billingMode: dynamodb.BillingMode.PROVISIONED,
readCapacity: 1,
writeCapacity: 1,
removalPolicy: cdk.RemovalPolicy.DESTROY,
partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
sortKey: { name: 'createdAt', type: dynamodb.AttributeType.NUMBER },
pointInTimeRecovery: true
});
const tablePolicy = new PolicyStatement({
effect: Effect.ALLOW,
resources: [table.tableArn],
actions: ['*']
});
const role = new Role(this, 'tableRoleArn', {
assumedBy: new ServicePrincipal('lambda.amazonaws.com')
});
role.addToPolicy(
tablePolicy
);
const app = '***';
const environment = '***';
const profile = '***';
const strategy = 'v';
const newConfig = new appconfig.CfnHostedConfigurationVersion(this, 'ConfigurationName', {
applicationId: app,
configurationProfileId: profile,
contentType: 'application/json',
content: JSON.stringify({
tableArn: table.tableArn,
tableName: table.tableName,
tableRoleArn: role.roleArn
}),
description: 'table config'
});
const cfnDeployment = new appconfig.CfnDeployment(this, 'MyCfnDeployment', {
applicationId: app,
configurationProfileId: profile,
environmentId: environment,
configurationVersion: newConfig.ref,
deploymentStrategyId: strategy
});
}
}
第二个有一个功能,我希望能够使用 appConfig 配置来动态访问表。
import { App, CfnOutput, Stack, StackProps } from '@aws-cdk/core';
import { LayerVersion, Runtime } from '@aws-cdk/aws-lambda';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import { Effect, PolicyStatement } from '@aws-cdk/aws-iam';
export class ConsumingStack extends Stack {
constructor(scope: App, id: string, props: StackProps) {
super(scope, id, props);
const fn = new NodejsFunction(this, 'foo', {
runtime: Runtime.NODEJS_12_X,
handler: 'foo',
entry: `stack/foo.ts`
});
fn.addToRolePolicy(
new PolicyStatement({
effect: Effect.ALLOW,
resources: ['*'],
actions: [
'ssm:*',
'appconfig:*',
'sts:*',
]
})
);
new CfnOutput(this, 'functionArn', { value: fn.functionArn});
// https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-integration-lambda-extensions.html
// https://github.com/aws-samples/aws-appconfig-codepipeline-cdk/blob/main/infrastructure/src/main/kotlin/com/app/config/ServerlessAppStack.kt
const appConfigLayer = LayerVersion.fromLayerVersionArn(
this,
'appconfigLayer',
'arn:aws:lambda:eu-west-2:282860088358:layer:AWS-AppConfig-Extension:47'
);
fn.addLayers(appConfigLayer);
}
}
和处理程序
import type { Context } from 'aws-lambda';
import fetch from 'node-fetch';
import { DynamoDB, STS } from 'aws-sdk';
import { Agent } from 'https';
export const foo = async (event: any, lambdaContext: Context): Promise<void> => {
const application = '*****';
const environment = '*****';
const configuration = '*****';
const response = await fetch(
`http://localhost:2772/applications/${application}/environments/${environment}/configurations/${configuration}`
);
const configurationData = await response.json();
console.log(configurationData);
const credentials = await assumeRole(configurationData.tableRoleArn);
const db = new DynamoDB({
credentials: {
sessionToken: credentials.sessionToken,
secretAccessKey: credentials.secretAccessKey,
accessKeyId: credentials.accessKeyId
},
apiVersion: '2012-08-10',
region: '*****',
httpOptions: {
agent: new Agent({ keepAlive: true }),
connectTimeout: 1000,
timeout: 5000
},
signatureVersion: 'v4',
maxRetries: 3
});
const item = await db
.getItem({ TableName: configurationData.tableName, Key: { id: { S: 'coolPeople' }, createdAt: { N: '0' } } }, (e) => {
console.log('e', e);
})
.promise();
console.log('item:', item?.Item?.value?.L);
};
/**
* Assume Role for cross account operations
*/
export const assumeRole = async (tableRoleArn: string): Promise<any> => {
let params = {
RoleArn: tableRoleArn,
RoleSessionName: 'RoleSessionName12345'
};
console.info('Assuming Role with params:', params);
let sts = new STS();
return new Promise((resolve, reject) => {
sts.assumeRole(params, (error, data) => {
if (error) {
console.log(`Could not assume role, error : ${JSON.stringify(error)}`);
reject({
statusCode: 400,
message: error['message']
});
} else {
console.log(`Successfully Assumed Role details data=${JSON.stringify(data)}`);
resolve({
statusCode: 200,
body: data
});
}
});
});
};
问题是我在尝试assumeRole
使用 lambda 时收到此错误。
Could not assume role, error : {"message":"User: arn:aws:sts::****:assumed-role/ConsumingStack-fooServiceRole****-***/ConsumingStack-foo****-*** is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::****:role/ExportingStack-tableRoleArn****-***","code":"AccessDenied","time":"2022-02-21T16:06:44.474Z","requestId":"****-***-****-****","statusCode":403,"retryable":false,"retryDelay":26.827985116659757}
那么 Lambda 是否有可能动态地承担角色以从不同的堆栈访问表?