如何使用 AWS CDK(首选 typescript)向 API 网关的现有 AWS 云前端发行版添加行为?

问题描述 投票:0回答:2

我正在尝试实现一个 CDK 项目,该项目将在 s3 存储桶中部署静态网站以及 CloudFront 发行版。我还有一个 API 网关,需要通过相同的云前端 URL 进行访问。我可以从 AWS 管理控制台执行此操作。但是当我尝试使用 CDK 实现此功能时,我遇到了循环依赖错误。

    const cdn = new cloudfront.Distribution(this, "websitecdn", {
        defaultBehavior: {origin: new origins.S3Origin(s3_bucket)}
    });
    const api = new apigw.RestApi(this, 'someapi', {defaultCorsPreflightOptions: enableCors})
    const loginApi = api.root.addResource('login', {defaultCorsPreflightOptions: enableCors})
    loginApi.addMethod('POST', new apigw.LambdaIntegration(loginLambda, {
    proxy: false,
    integrationResponses: [LambdaIntegrationResponses]}), 
    {
        methodResponses: [LambdaMethodResponses]
    })
    const apiOrigin = new origins.RestApiOrigin(api)
    cdn.addBehavior("/prod/*",apiOrigin,{
        allowedMethods: cloudfront.AllowedMethods.ALLOW_ALL,
        cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
        viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
    })

一切正常,直到我尝试在 CDN 中添加 API 网关的行为。但是当我添加它时,它开始抛出循环依赖错误。

我尝试使用 AWS CDK typescript 执行以下操作:

  1. 部署静态 s3 网站

  2. 为此网站创建一个 CloudFront 发行版 -> 我们称之为 cdn_x

  3. 部署后端API(带有API网关的Lambda函数)

  4. 将 API 网关 URL 作为行为添加到 cdn_x,以便我也可以使用相同的 URL 进行 API 调用(我没有自定义域)

我预计部署会顺利进行,因为我能够在 AWS 管理控制台(AWS 的 Web UI)中进行部署。但尝试使用 AWS CDK 执行相同操作会引发循环依赖错误。

amazon-s3 aws-cloudformation aws-api-gateway amazon-cloudfront aws-cdk
2个回答
0
投票

从您的示例中尚不清楚 CDK 项目中的堆栈和资源是如何创建和关联的。我无法使用您的代码示例。

与此同时,我使用 CloudFront 中的多个行为以及 Amazon API Gateway 在

/api/*
路径下创建了一个 TypeScript 示例,并将 S3 存储桶作为默认行为来提供静态资产
/*

最终的CDK结构如下:

enter image description here

CDK 代码库使用 多个堆栈:

  • cloudfront-stack.ts
  • rest-api-stack.ts
  • s3-stack.ts
  • waf-stack.ts

资源作为引用传递

bin/infra.ts

const app = new cdk.App();
const s3Stack = new S3Stack(app, "S3Stack");
const restApiStack = new RestApiStack(app, "RestApiStack");
const wafStack = new WafStack(app, "WafStack", {
  restApi: restApiStack.restApi,
});
const cloudFrontStack = new CloudFrontStack(app, "CloudFrontStack", {
  bucketAssets: s3Stack.bucketAssets,
  restApi: restApiStack.restApi,
  wafCloudFrontAclArn: wafStack.wafCloudFrontAclArn,
  wafRestApiOriginVerifyHeader: wafStack.wafRestApiOriginVerifyHeader,
  wafRestApiOriginVerifyHeaderValue: wafStack.wafRestApiOriginVerifyHeaderValue,
});

GitHub 存储库:

我相信上面的例子会澄清你的一些问题。


0
投票

我整理了一篇文章,准确描述了如何使用 CDK(以及 Pulumi)执行此操作,您可以在这里阅读它,我相信它会回答您的很多问题。

extremely简短的总结中:您需要为每个源(s3和您的api)设置一个源,并且您需要授予正确访问这些源的权限,并且您需要配置行为相应地分配给您。

// CDK example

const domainName = "abc.com";
const subdomainName = `www.${domainName}`;

const siteBucket = new Bucket(this, "static-website", {
    bucketName: domainName,
    encryption: BucketEncryption.S3_MANAGED,
    publicReadAccess: false,

    // the following is NOT recommended for production code
    removalPolicy: RemovalPolicy.DESTROY,
    autoDeleteObjects: true,
});

const originAccessIdentity = new OriginAccessIdentity(this, 'OriginAccessIdentity', {
    comment: 'abc.com website OAI'
});
const s3Origin = new S3Origin(siteBucket, {originAccessIdentity: originAccessIdentity})

// assumes relevant certificateArn, see
// https://therightstuff.medium.com/aws-cdk-dnsvalidatedcertificate-is-deprecated-c347fdc0a3e6
// for more details
const certificate = acm.Certificate.fromCertificateArn(this, 'Certificate', certificateArn);

// assumes api created (RestApi construct from aws-cdk-lib/aws-apigateway)
// and region available.
// NOTE: it amazes me that manually reconstructing this url is required.
const apiOriginName = `${api.restApiId}.execute-api.${region}.amazonaws.com`;

// origin path must be set to the stage name, and this assumes that after
// the matched path there is a matching resource that requests will be mapped
// to. in other words, if the cloudfront distribution path matches
// /api/my-resource, which in our example is abc.com/api/my-resource, then the
// request will redirect to the /{stage}/api/my-resource resource in the
// API Gateway endpoint
const apiOriginPath = `/${api.deploymentStage.stageName}`;

// CloudFront distribution that provides HTTPS (including S3 and API access)
const distribution = new Distribution(
    this,
    "SiteDistribution",
    {
        domainNames: [domainName, subdomainName],
        certificate,
        defaultBehavior: {
            origin: s3Origin,
            allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
            cachePolicy: CachePolicy.CACHING_OPTIMIZED,
            viewerProtocolPolicy: ViewerProtocolPolicy.ALLOW_ALL,
        },
        additionalBehaviors: {
            "api/*": {
                origin: new HttpOrigin(apiOriginName, {
                    originId: apiOriginName,
                    protocolPolicy: OriginProtocolPolicy.HTTPS_ONLY,
                    httpPort: 80,
                    httpsPort: 443,
                    originPath: apiOriginPath,
                }),
                viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
                cachePolicy: CachePolicy.CACHING_DISABLED,
                allowedMethods: AllowedMethods.ALLOW_ALL,
                originRequestPolicy: OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
            },
        },
        defaultRootObject: "index.html",
        errorResponses: [
            {
                httpStatus: 403,
                responseHttpStatus: 200,
                responsePagePath: "/index.html",
            },
            {
                httpStatus: 404,
                responseHttpStatus: 200,
                responsePagePath: "/index.html",
            },
        ],
    }
);

// Route53 alias record for the naked domain's CloudFront distribution
new ARecord(this, "SiteAliasRecord", {
    recordName: domainName,
    target: RecordTarget.fromAlias(
        new targets.CloudFrontTarget(distribution)
    ),
    // assumes zone (IHostedZone construct from 'aws-cdk-lib/aws-route53')
    // available
    zone: zone,
});

// Route53 alias record for a subdomain's CloudFront distribution
new CnameRecord(this, "SiteCnameRecord", {
    recordName: subdomainName,
    domainName: distribution.distributionDomainName,
    // assumes zone (IHostedZone construct from 'aws-cdk-lib/aws-route53')
    // available
    zone: zone,
});

// Deploy the static website to the site bucket
new BucketDeployment(this, "static-website-deployment", {
    // assumes that bucket content is in the `static-website` folder
    // under the project root
    sources: [Source.asset("./static-website")],
    destinationBucket: siteBucket,
    distribution: distribution
});
© www.soinside.com 2019 - 2024. All rights reserved.