@aws/nx-plugin 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generators.json +22 -1
- package/package.json +1 -1
- package/sdk/ts.d.ts +2 -0
- package/sdk/ts.js +6 -3
- package/sdk/ts.js.map +1 -1
- package/src/api-connection/generator.d.ts +2 -2
- package/src/api-connection/generator.js +20 -0
- package/src/api-connection/generator.js.map +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +124 -14
- package/src/infra/app/files/app/README.md.template +5 -5
- package/src/infra/app/files/app/checkov.yml.template +12 -0
- package/src/infra/app/files/app/src/main.ts.template +2 -4
- package/src/infra/app/generator.js +13 -8
- package/src/infra/app/generator.js.map +1 -1
- package/src/infra/app/schema.d.ts +0 -8
- package/src/infra/app/schema.json +0 -16
- package/src/license/config.js +3 -3
- package/src/license/config.js.map +1 -1
- package/src/open-api/ts-hooks/__snapshots__/generator.spec.tsx.snap +114 -0
- package/src/open-api/ts-hooks/generator.spec.tsx +176 -0
- package/src/open-api/utils/codegen-data.js +42 -5
- package/src/open-api/utils/codegen-data.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +55 -2
- package/src/py/fast-api/generator.js +8 -55
- package/src/py/fast-api/generator.js.map +1 -1
- package/src/py/fast-api/react/generator.js +9 -111
- package/src/py/fast-api/react/generator.js.map +1 -1
- package/src/py/lambda-function/generator.js +1 -1
- package/src/py/lambda-function/generator.js.map +1 -1
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +3 -2
- package/src/py/mcp-server/generator.js +1 -1
- package/src/py/mcp-server/generator.js.map +1 -1
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +3 -2
- package/src/py/strands-agent/generator.js +1 -1
- package/src/py/strands-agent/generator.js.map +1 -1
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +576 -0
- package/src/smithy/project/files/build.Dockerfile.template +97 -0
- package/src/smithy/project/files/smithy-build.json.template +25 -0
- package/src/smithy/project/files/src/main.smithy.template +19 -0
- package/src/smithy/project/files/src/operations/echo.smithy.template +18 -0
- package/src/smithy/project/generator.d.ts +10 -0
- package/src/smithy/project/generator.js +70 -0
- package/src/smithy/project/generator.js.map +1 -0
- package/src/smithy/project/schema.d.ts +11 -0
- package/src/smithy/project/schema.json +42 -0
- package/src/smithy/react-connection/__snapshots__/generator.spec.ts.snap +270 -0
- package/src/smithy/react-connection/files/model/extensions.smithy.template +33 -0
- package/src/smithy/react-connection/generator.d.ts +10 -0
- package/src/smithy/react-connection/generator.js +100 -0
- package/src/smithy/react-connection/generator.js.map +1 -0
- package/src/smithy/react-connection/schema.d.ts +8 -0
- package/src/smithy/react-connection/schema.json +26 -0
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +3023 -0
- package/src/smithy/ts/api/files/context.ts.template +12 -0
- package/src/smithy/ts/api/files/handler.ts.template +50 -0
- package/src/smithy/ts/api/files/index.ts.template +0 -0
- package/src/smithy/ts/api/files/local-server.ts.template +41 -0
- package/src/smithy/ts/api/files/operations/echo.ts.template +7 -0
- package/src/smithy/ts/api/files/service.ts.template +8 -0
- package/src/smithy/ts/api/generator.d.ts +10 -0
- package/src/smithy/ts/api/generator.js +154 -0
- package/src/smithy/ts/api/generator.js.map +1 -0
- package/src/smithy/ts/api/schema.d.ts +14 -0
- package/src/smithy/ts/api/schema.json +56 -0
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +61 -2
- package/src/trpc/backend/generator.js +6 -20
- package/src/trpc/backend/generator.js.map +1 -1
- package/src/trpc/backend/schema.d.ts +2 -1
- package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/ts/lambda-function/generator.js +10 -10
- package/src/ts/lambda-function/generator.js.map +1 -1
- package/src/ts/lib/eslint.d.ts +7 -0
- package/src/ts/lib/eslint.js +37 -29
- package/src/ts/lib/eslint.js.map +1 -1
- package/src/ts/lib/generator.js +2 -2
- package/src/ts/lib/generator.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +3 -2
- package/src/ts/mcp-server/files/Dockerfile.template +1 -1
- package/src/ts/mcp-server/generator.js +20 -14
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/nx-generator/__snapshots__/generator.spec.ts.snap +6 -6
- package/src/ts/nx-generator/generator.js +3 -2
- package/src/ts/nx-generator/generator.js.map +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +264 -3
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +10 -0
- package/src/utils/__snapshots__/shared-constructs.spec.ts.snap +49 -0
- package/src/utils/agent-core-constructs/files/cdk/app/agent-core/__nameKebabCase__/__nameKebabCase__.ts.template +1 -1
- package/src/utils/agent-core-constructs/files/terraform/core/agent-core/runtime.tf.template +1 -1
- package/src/utils/api-connection/open-api/react.d.ts +43 -0
- package/src/utils/api-connection/open-api/react.js +132 -0
- package/src/utils/api-connection/open-api/react.js.map +1 -0
- package/src/utils/api-constructs/api-constructs.d.ts +6 -2
- package/src/utils/api-constructs/api-constructs.js.map +1 -1
- package/src/utils/api-constructs/files/cdk/app/apis/http/__apiNameKebabCase__.ts.template +4 -4
- package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +7 -4
- package/src/utils/api-constructs/files/cdk/core/api/http/http-api.ts.template +37 -2
- package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +15 -0
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -1
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -1
- package/src/utils/api-constructs/open-api-metadata.d.ts +17 -0
- package/src/utils/api-constructs/open-api-metadata.js +68 -0
- package/src/utils/api-constructs/open-api-metadata.js.map +1 -0
- package/src/utils/bundle/bundle.d.ts +35 -0
- package/src/utils/bundle/bundle.js +107 -0
- package/src/utils/bundle/bundle.js.map +1 -0
- package/src/utils/bundle/files/ts/rolldown.config.ts.template +3 -0
- package/src/utils/files/common/constructs/src/core/checkov.ts.template +44 -0
- package/src/utils/files/common/constructs/src/core/index.ts.template +1 -0
- package/src/utils/fs.d.ts +16 -0
- package/src/utils/fs.js +32 -0
- package/src/utils/fs.js.map +1 -0
- package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +8 -0
- package/src/utils/nx.d.ts +10 -3
- package/src/utils/nx.js +18 -3
- package/src/utils/nx.js.map +1 -1
- package/src/utils/versions.d.ts +6 -2
- package/src/utils/versions.js +5 -1
- package/src/utils/versions.js.map +1 -1
- package/src/utils/website-constructs/files/cdk/core/static-website.ts.template +56 -2
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/aws-prototyping.guard +0 -1282
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/cfn-nag.guard +0 -6839
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/hipaa-security.guard +0 -2807
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/nist-csf.guard +0 -2585
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/pci-dss-3-2-1.guard +0 -2236
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-reliability-pillar.guard +0 -885
- package/src/infra/app/files/common/constructs/src/core/cfn-guard-rules/wa-security-pillar.guard +0 -2205
- package/src/infra/app/files/common/constructs/src/core/cfn-guard.ts.template +0 -67
- package/src/utils/bundle.d.ts +0 -16
- package/src/utils/bundle.js +0 -48
- package/src/utils/bundle.js.map +0 -1
- package/src/utils/esbuild.d.ts +0 -15
- package/src/utils/esbuild.js +0 -46
- package/src/utils/esbuild.js.map +0 -1
- /package/src/{py/fast-api/react/files/website → utils/api-connection/open-api/files}/components/__apiNameClassName__Provider.tsx.template +0 -0
- /package/src/{py/fast-api/react/files/website → utils/api-connection/open-api/files}/hooks/use__apiNameClassName__.tsx.template +0 -0
- /package/src/{py/fast-api/react/files/website → utils/api-connection/open-api/files}/hooks/use__apiNameClassName__Client.tsx.template +0 -0
|
@@ -0,0 +1,3023 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`tsSmithyApiGenerator > should configure git and eslint ignores for generated code > eslint.config.mjs 1`] = `
|
|
4
|
+
"import baseConfig from '../../eslint.config.mjs';
|
|
5
|
+
|
|
6
|
+
export default [
|
|
7
|
+
...baseConfig,
|
|
8
|
+
{
|
|
9
|
+
files: ['**/*.json'],
|
|
10
|
+
rules: {
|
|
11
|
+
'@nx/dependency-checks': [
|
|
12
|
+
'warn',
|
|
13
|
+
{
|
|
14
|
+
ignoredFiles: [
|
|
15
|
+
'{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}',
|
|
16
|
+
'{projectRoot}/vite.config.{js,ts,mjs,mts}',
|
|
17
|
+
],
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
languageOptions: {
|
|
22
|
+
parser: await import('jsonc-eslint-parser'),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
ignores: ['**/generated'],
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
"
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
exports[`tsSmithyApiGenerator > should configure git and eslint ignores for generated code > gitignore 1`] = `"src/generated"`;
|
|
33
|
+
|
|
34
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with Cognito auth > cognito-auth-infra.ts 1`] = `
|
|
35
|
+
"import { Construct } from 'constructs';
|
|
36
|
+
import * as url from 'url';
|
|
37
|
+
import {
|
|
38
|
+
Code,
|
|
39
|
+
Runtime,
|
|
40
|
+
Function,
|
|
41
|
+
FunctionProps,
|
|
42
|
+
Tracing,
|
|
43
|
+
} from 'aws-cdk-lib/aws-lambda';
|
|
44
|
+
import {
|
|
45
|
+
AuthorizationType,
|
|
46
|
+
Cors,
|
|
47
|
+
LambdaIntegration,
|
|
48
|
+
CognitoUserPoolsAuthorizer,
|
|
49
|
+
} from 'aws-cdk-lib/aws-apigateway';
|
|
50
|
+
import { Duration } from 'aws-cdk-lib';
|
|
51
|
+
import {
|
|
52
|
+
PolicyDocument,
|
|
53
|
+
PolicyStatement,
|
|
54
|
+
Effect,
|
|
55
|
+
AnyPrincipal,
|
|
56
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
57
|
+
import { IUserPool } from 'aws-cdk-lib/aws-cognito';
|
|
58
|
+
import {
|
|
59
|
+
IntegrationBuilder,
|
|
60
|
+
RestApiIntegration,
|
|
61
|
+
} from '../../core/api/utils.js';
|
|
62
|
+
import { RestApi } from '../../core/api/rest-api.js';
|
|
63
|
+
import {
|
|
64
|
+
OPERATION_DETAILS,
|
|
65
|
+
Operations,
|
|
66
|
+
} from '../../generated/test-api/metadata.gen.js';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Properties for creating a TestApi construct
|
|
70
|
+
*
|
|
71
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
72
|
+
*/
|
|
73
|
+
export interface TestApiProps<
|
|
74
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
75
|
+
> {
|
|
76
|
+
/**
|
|
77
|
+
* Map of operation names to their API Gateway integrations
|
|
78
|
+
*/
|
|
79
|
+
integrations: TIntegrations;
|
|
80
|
+
/**
|
|
81
|
+
* Identity details for Cognito Authentication
|
|
82
|
+
*/
|
|
83
|
+
identity: {
|
|
84
|
+
userPool: IUserPool;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A CDK construct that creates and configures an AWS API Gateway REST API
|
|
90
|
+
* specifically for TestApi.
|
|
91
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
92
|
+
*/
|
|
93
|
+
export class TestApi<
|
|
94
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
95
|
+
> extends RestApi<Operations, TIntegrations> {
|
|
96
|
+
/**
|
|
97
|
+
* Creates default integrations for all operations, which implement each operation as
|
|
98
|
+
* its own individual lambda function.
|
|
99
|
+
*
|
|
100
|
+
* @param scope - The CDK construct scope
|
|
101
|
+
* @returns An IntegrationBuilder with default lambda integrations
|
|
102
|
+
*/
|
|
103
|
+
public static defaultIntegrations = (scope: Construct) => {
|
|
104
|
+
return IntegrationBuilder.rest({
|
|
105
|
+
operations: OPERATION_DETAILS,
|
|
106
|
+
defaultIntegrationOptions: {
|
|
107
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
108
|
+
handler: 'index.handler',
|
|
109
|
+
code: Code.fromAsset(
|
|
110
|
+
url.fileURLToPath(
|
|
111
|
+
new URL(
|
|
112
|
+
'../../../../../../dist/test-api/backend/bundle',
|
|
113
|
+
import.meta.url,
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
),
|
|
117
|
+
timeout: Duration.seconds(30),
|
|
118
|
+
tracing: Tracing.ACTIVE,
|
|
119
|
+
environment: {
|
|
120
|
+
AWS_CONNECTION_REUSE_ENABLED: '1',
|
|
121
|
+
},
|
|
122
|
+
} satisfies FunctionProps,
|
|
123
|
+
buildDefaultIntegration: (op, props: FunctionProps) => {
|
|
124
|
+
const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
|
|
125
|
+
return { handler, integration: new LambdaIntegration(handler) };
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
constructor(
|
|
131
|
+
scope: Construct,
|
|
132
|
+
id: string,
|
|
133
|
+
props: TestApiProps<TIntegrations>,
|
|
134
|
+
) {
|
|
135
|
+
super(scope, id, {
|
|
136
|
+
apiName: 'TestApi',
|
|
137
|
+
defaultMethodOptions: {
|
|
138
|
+
authorizationType: AuthorizationType.COGNITO,
|
|
139
|
+
authorizer: new CognitoUserPoolsAuthorizer(scope, 'TestApiAuthorizer', {
|
|
140
|
+
cognitoUserPools: [props.identity.userPool],
|
|
141
|
+
}),
|
|
142
|
+
},
|
|
143
|
+
defaultCorsPreflightOptions: {
|
|
144
|
+
allowOrigins: Cors.ALL_ORIGINS,
|
|
145
|
+
allowMethods: Cors.ALL_METHODS,
|
|
146
|
+
},
|
|
147
|
+
deployOptions: {
|
|
148
|
+
tracingEnabled: true,
|
|
149
|
+
},
|
|
150
|
+
policy: new PolicyDocument({
|
|
151
|
+
statements: [
|
|
152
|
+
// Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
|
|
153
|
+
new PolicyStatement({
|
|
154
|
+
effect: Effect.ALLOW,
|
|
155
|
+
principals: [new AnyPrincipal()],
|
|
156
|
+
actions: ['execute-api:Invoke'],
|
|
157
|
+
resources: ['execute-api:/*'],
|
|
158
|
+
}),
|
|
159
|
+
],
|
|
160
|
+
}),
|
|
161
|
+
operations: OPERATION_DETAILS,
|
|
162
|
+
...props,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
"
|
|
167
|
+
`;
|
|
168
|
+
|
|
169
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with IAM auth > iam-auth-infra.ts 1`] = `
|
|
170
|
+
"import { Construct } from 'constructs';
|
|
171
|
+
import * as url from 'url';
|
|
172
|
+
import {
|
|
173
|
+
Code,
|
|
174
|
+
Runtime,
|
|
175
|
+
Function,
|
|
176
|
+
FunctionProps,
|
|
177
|
+
Tracing,
|
|
178
|
+
} from 'aws-cdk-lib/aws-lambda';
|
|
179
|
+
import {
|
|
180
|
+
AuthorizationType,
|
|
181
|
+
Cors,
|
|
182
|
+
LambdaIntegration,
|
|
183
|
+
} from 'aws-cdk-lib/aws-apigateway';
|
|
184
|
+
import { Duration, Stack } from 'aws-cdk-lib';
|
|
185
|
+
import {
|
|
186
|
+
PolicyDocument,
|
|
187
|
+
PolicyStatement,
|
|
188
|
+
Effect,
|
|
189
|
+
AnyPrincipal,
|
|
190
|
+
AccountPrincipal,
|
|
191
|
+
IGrantable,
|
|
192
|
+
Grant,
|
|
193
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
194
|
+
import {
|
|
195
|
+
IntegrationBuilder,
|
|
196
|
+
RestApiIntegration,
|
|
197
|
+
} from '../../core/api/utils.js';
|
|
198
|
+
import { RestApi } from '../../core/api/rest-api.js';
|
|
199
|
+
import {
|
|
200
|
+
OPERATION_DETAILS,
|
|
201
|
+
Operations,
|
|
202
|
+
} from '../../generated/test-api/metadata.gen.js';
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Properties for creating a TestApi construct
|
|
206
|
+
*
|
|
207
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
208
|
+
*/
|
|
209
|
+
export interface TestApiProps<
|
|
210
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
211
|
+
> {
|
|
212
|
+
/**
|
|
213
|
+
* Map of operation names to their API Gateway integrations
|
|
214
|
+
*/
|
|
215
|
+
integrations: TIntegrations;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* A CDK construct that creates and configures an AWS API Gateway REST API
|
|
220
|
+
* specifically for TestApi.
|
|
221
|
+
* @template TIntegrations - Map of operation names to their integrations
|
|
222
|
+
*/
|
|
223
|
+
export class TestApi<
|
|
224
|
+
TIntegrations extends Record<Operations, RestApiIntegration>,
|
|
225
|
+
> extends RestApi<Operations, TIntegrations> {
|
|
226
|
+
/**
|
|
227
|
+
* Creates default integrations for all operations, which implement each operation as
|
|
228
|
+
* its own individual lambda function.
|
|
229
|
+
*
|
|
230
|
+
* @param scope - The CDK construct scope
|
|
231
|
+
* @returns An IntegrationBuilder with default lambda integrations
|
|
232
|
+
*/
|
|
233
|
+
public static defaultIntegrations = (scope: Construct) => {
|
|
234
|
+
return IntegrationBuilder.rest({
|
|
235
|
+
operations: OPERATION_DETAILS,
|
|
236
|
+
defaultIntegrationOptions: {
|
|
237
|
+
runtime: Runtime.NODEJS_LATEST,
|
|
238
|
+
handler: 'index.handler',
|
|
239
|
+
code: Code.fromAsset(
|
|
240
|
+
url.fileURLToPath(
|
|
241
|
+
new URL(
|
|
242
|
+
'../../../../../../dist/test-api/backend/bundle',
|
|
243
|
+
import.meta.url,
|
|
244
|
+
),
|
|
245
|
+
),
|
|
246
|
+
),
|
|
247
|
+
timeout: Duration.seconds(30),
|
|
248
|
+
tracing: Tracing.ACTIVE,
|
|
249
|
+
environment: {
|
|
250
|
+
AWS_CONNECTION_REUSE_ENABLED: '1',
|
|
251
|
+
},
|
|
252
|
+
} satisfies FunctionProps,
|
|
253
|
+
buildDefaultIntegration: (op, props: FunctionProps) => {
|
|
254
|
+
const handler = new Function(scope, \`TestApi\${op}Handler\`, props);
|
|
255
|
+
return { handler, integration: new LambdaIntegration(handler) };
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
constructor(
|
|
261
|
+
scope: Construct,
|
|
262
|
+
id: string,
|
|
263
|
+
props: TestApiProps<TIntegrations>,
|
|
264
|
+
) {
|
|
265
|
+
super(scope, id, {
|
|
266
|
+
apiName: 'TestApi',
|
|
267
|
+
defaultMethodOptions: {
|
|
268
|
+
authorizationType: AuthorizationType.IAM,
|
|
269
|
+
},
|
|
270
|
+
defaultCorsPreflightOptions: {
|
|
271
|
+
allowOrigins: Cors.ALL_ORIGINS,
|
|
272
|
+
allowMethods: Cors.ALL_METHODS,
|
|
273
|
+
},
|
|
274
|
+
deployOptions: {
|
|
275
|
+
tracingEnabled: true,
|
|
276
|
+
},
|
|
277
|
+
policy: new PolicyDocument({
|
|
278
|
+
statements: [
|
|
279
|
+
// Here we grant any AWS credentials from the account that the project is deployed in to call the api.
|
|
280
|
+
// Machine to machine fine-grained access can be defined here using more specific principals (eg roles or
|
|
281
|
+
// users) and resources (eg which api paths may be invoked by which principal) if required.
|
|
282
|
+
new PolicyStatement({
|
|
283
|
+
effect: Effect.ALLOW,
|
|
284
|
+
principals: [new AccountPrincipal(Stack.of(scope).account)],
|
|
285
|
+
actions: ['execute-api:Invoke'],
|
|
286
|
+
resources: ['execute-api:/*'],
|
|
287
|
+
}),
|
|
288
|
+
// Open up OPTIONS to allow browsers to make unauthenticated preflight requests
|
|
289
|
+
new PolicyStatement({
|
|
290
|
+
effect: Effect.ALLOW,
|
|
291
|
+
principals: [new AnyPrincipal()],
|
|
292
|
+
actions: ['execute-api:Invoke'],
|
|
293
|
+
resources: ['execute-api:/*/OPTIONS/*'],
|
|
294
|
+
}),
|
|
295
|
+
],
|
|
296
|
+
}),
|
|
297
|
+
operations: OPERATION_DETAILS,
|
|
298
|
+
...props,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Grants IAM permissions to invoke any method on this API.
|
|
304
|
+
*
|
|
305
|
+
* @param grantee - The IAM principal to grant permissions to
|
|
306
|
+
*/
|
|
307
|
+
public grantInvokeAccess(grantee: IGrantable) {
|
|
308
|
+
Grant.addToPrincipal({
|
|
309
|
+
grantee,
|
|
310
|
+
actions: ['execute-api:Invoke'],
|
|
311
|
+
resourceArns: [this.api.arnForExecuteApi('*', '/*', '*')],
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
"
|
|
316
|
+
`;
|
|
317
|
+
|
|
318
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with Terraform provider > terraform-infra.tf 1`] = `
|
|
319
|
+
"# Core REST API Gateway module
|
|
320
|
+
# This module creates the API Gateway REST API, deployment, stage, and logging resources
|
|
321
|
+
|
|
322
|
+
terraform {
|
|
323
|
+
required_version = ">= 1.0"
|
|
324
|
+
|
|
325
|
+
required_providers {
|
|
326
|
+
aws = {
|
|
327
|
+
source = "hashicorp/aws"
|
|
328
|
+
version = "~> 6.0"
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# Core REST API Gateway Variables
|
|
334
|
+
|
|
335
|
+
variable "api_name" {
|
|
336
|
+
description = "Name of the REST API Gateway"
|
|
337
|
+
type = string
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
variable "api_description" {
|
|
341
|
+
description = "Description of the REST API Gateway"
|
|
342
|
+
type = string
|
|
343
|
+
default = "REST API Gateway"
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
variable "stage_name" {
|
|
347
|
+
description = "Name of the API Gateway stage"
|
|
348
|
+
type = string
|
|
349
|
+
default = "prod"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
variable "stage_auto_deploy" {
|
|
353
|
+
description = "Whether to automatically deploy the API stage"
|
|
354
|
+
type = bool
|
|
355
|
+
default = true
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
# CORS Configuration
|
|
359
|
+
|
|
360
|
+
variable "cors_allow_headers" {
|
|
361
|
+
description = "List of allowed headers for CORS"
|
|
362
|
+
type = list(string)
|
|
363
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
variable "cors_allow_methods" {
|
|
367
|
+
description = "List of allowed HTTP methods for CORS"
|
|
368
|
+
type = list(string)
|
|
369
|
+
default = ["*"]
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
variable "cors_allow_origins" {
|
|
373
|
+
description = "List of allowed origins for CORS"
|
|
374
|
+
type = list(string)
|
|
375
|
+
default = ["*"]
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
# Tags
|
|
379
|
+
|
|
380
|
+
variable "tags" {
|
|
381
|
+
description = "Tags to apply to all resources"
|
|
382
|
+
type = map(string)
|
|
383
|
+
default = {}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
# Data sources
|
|
387
|
+
data "aws_region" "current" {}
|
|
388
|
+
data "aws_caller_identity" "current" {}
|
|
389
|
+
|
|
390
|
+
# Resources
|
|
391
|
+
|
|
392
|
+
# Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
|
|
393
|
+
|
|
394
|
+
# REST API Gateway
|
|
395
|
+
resource "aws_api_gateway_rest_api" "rest_api" {
|
|
396
|
+
name = var.api_name
|
|
397
|
+
description = var.api_description
|
|
398
|
+
|
|
399
|
+
endpoint_configuration {
|
|
400
|
+
types = ["REGIONAL"]
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
lifecycle {
|
|
404
|
+
create_before_destroy = true
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
tags = var.tags
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
# Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
|
|
411
|
+
# after all methods and integrations are defined
|
|
412
|
+
|
|
413
|
+
# Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
|
|
414
|
+
|
|
415
|
+
# Gateway Response for CORS (4XX errors)
|
|
416
|
+
resource "aws_api_gateway_gateway_response" "cors_4xx" {
|
|
417
|
+
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
418
|
+
response_type = "DEFAULT_4XX"
|
|
419
|
+
|
|
420
|
+
response_parameters = {
|
|
421
|
+
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
422
|
+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
423
|
+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
# Gateway Response for CORS (5XX errors)
|
|
428
|
+
resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
429
|
+
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
430
|
+
response_type = "DEFAULT_5XX"
|
|
431
|
+
|
|
432
|
+
response_parameters = {
|
|
433
|
+
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
434
|
+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
435
|
+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
# Outputs
|
|
440
|
+
|
|
441
|
+
output "api_id" {
|
|
442
|
+
description = "ID of the REST API Gateway"
|
|
443
|
+
value = aws_api_gateway_rest_api.rest_api.id
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
output "api_arn" {
|
|
447
|
+
description = "ARN of the REST API Gateway"
|
|
448
|
+
value = aws_api_gateway_rest_api.rest_api.arn
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
output "api_endpoint" {
|
|
452
|
+
description = "Base URL of the REST API Gateway"
|
|
453
|
+
value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
output "api_execution_arn" {
|
|
457
|
+
description = "Execution ARN of the REST API Gateway"
|
|
458
|
+
value = aws_api_gateway_rest_api.rest_api.execution_arn
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
output "api_root_resource_id" {
|
|
462
|
+
description = "Root resource ID of the REST API Gateway"
|
|
463
|
+
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
# Note: Stage and deployment outputs are provided by the consuming module
|
|
467
|
+
|
|
468
|
+
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement"
|
|
469
|
+
`;
|
|
470
|
+
|
|
471
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with Terraform provider and None auth > none-auth-terraform.tf 1`] = `
|
|
472
|
+
"terraform {
|
|
473
|
+
required_version = ">= 1.0"
|
|
474
|
+
|
|
475
|
+
required_providers {
|
|
476
|
+
aws = {
|
|
477
|
+
source = "hashicorp/aws"
|
|
478
|
+
version = "~> 6.0"
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
variable "env" {
|
|
485
|
+
description = "Environment variables for the Lambda function"
|
|
486
|
+
type = map(string)
|
|
487
|
+
default = {}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
variable "additional_iam_policy_statements" {
|
|
491
|
+
description = "Additional IAM policy statements for the Lambda function"
|
|
492
|
+
type = list(object({
|
|
493
|
+
Effect = string
|
|
494
|
+
Action = list(string)
|
|
495
|
+
Resource = list(string)
|
|
496
|
+
}))
|
|
497
|
+
default = []
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
# CORS Configuration (passed to core module)
|
|
501
|
+
|
|
502
|
+
variable "cors_allow_headers" {
|
|
503
|
+
description = "List of allowed headers for CORS"
|
|
504
|
+
type = list(string)
|
|
505
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
variable "cors_allow_methods" {
|
|
509
|
+
description = "List of allowed HTTP methods for CORS"
|
|
510
|
+
type = list(string)
|
|
511
|
+
default = ["*"]
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
variable "cors_allow_origins" {
|
|
515
|
+
description = "List of allowed origins for CORS"
|
|
516
|
+
type = list(string)
|
|
517
|
+
default = ["*"]
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
# Tags
|
|
521
|
+
variable "tags" {
|
|
522
|
+
description = "Tags to apply to all resources"
|
|
523
|
+
type = map(string)
|
|
524
|
+
default = {}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
# Get current AWS region and account ID
|
|
528
|
+
data "aws_region" "current" {}
|
|
529
|
+
data "aws_caller_identity" "current" {}
|
|
530
|
+
|
|
531
|
+
# Resources
|
|
532
|
+
|
|
533
|
+
# Create Lambda ZIP file from the FastAPI bundle directory
|
|
534
|
+
data "archive_file" "lambda_zip" {
|
|
535
|
+
type = "zip"
|
|
536
|
+
source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
|
|
537
|
+
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
# Use the core REST API module
|
|
541
|
+
module "rest_api" {
|
|
542
|
+
source = "../../../core/api/rest-api"
|
|
543
|
+
|
|
544
|
+
api_name = "TestApi"
|
|
545
|
+
api_description = "TestApi REST API"
|
|
546
|
+
stage_name = "prod"
|
|
547
|
+
stage_auto_deploy = true
|
|
548
|
+
|
|
549
|
+
# CORS Configuration
|
|
550
|
+
cors_allow_headers = var.cors_allow_headers
|
|
551
|
+
cors_allow_methods = var.cors_allow_methods
|
|
552
|
+
cors_allow_origins = var.cors_allow_origins
|
|
553
|
+
|
|
554
|
+
# Tags
|
|
555
|
+
tags = var.tags
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
# Lambda function
|
|
559
|
+
resource "aws_lambda_function" "api_lambda" {
|
|
560
|
+
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
561
|
+
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
562
|
+
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
563
|
+
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
564
|
+
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
565
|
+
filename = data.archive_file.lambda_zip.output_path
|
|
566
|
+
function_name = "TestApiHandler"
|
|
567
|
+
role = aws_iam_role.lambda_execution_role.arn
|
|
568
|
+
handler = "index.handler"
|
|
569
|
+
runtime = "nodejs22.x"
|
|
570
|
+
timeout = 30
|
|
571
|
+
memory_size = 128
|
|
572
|
+
|
|
573
|
+
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
574
|
+
|
|
575
|
+
# Enable X-Ray tracing
|
|
576
|
+
tracing_config {
|
|
577
|
+
mode = "Active"
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
environment {
|
|
581
|
+
variables = merge({
|
|
582
|
+
AWS_CONNECTION_REUSE_ENABLED = "1"
|
|
583
|
+
}, var.env)
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
tags = var.tags
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
# IAM role for Lambda execution
|
|
590
|
+
resource "aws_iam_role" "lambda_execution_role" {
|
|
591
|
+
name = "TestApiHandler-execution-role"
|
|
592
|
+
|
|
593
|
+
assume_role_policy = jsonencode({
|
|
594
|
+
Version = "2012-10-17"
|
|
595
|
+
Statement = [
|
|
596
|
+
{
|
|
597
|
+
Action = "sts:AssumeRole"
|
|
598
|
+
Effect = "Allow"
|
|
599
|
+
Principal = {
|
|
600
|
+
Service = "lambda.amazonaws.com"
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
]
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
tags = var.tags
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
# Attach basic execution policy to Lambda role
|
|
610
|
+
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
611
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
612
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
# Attach X-Ray tracing policy to Lambda role
|
|
616
|
+
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
617
|
+
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
618
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
# Additional IAM policies for Lambda (if provided)
|
|
622
|
+
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
623
|
+
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
624
|
+
name = "TestApiHandler-additional-policies"
|
|
625
|
+
role = aws_iam_role.lambda_execution_role.id
|
|
626
|
+
|
|
627
|
+
policy = jsonencode({
|
|
628
|
+
Version = "2012-10-17"
|
|
629
|
+
Statement = var.additional_iam_policy_statements
|
|
630
|
+
})
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
# CloudWatch Log Group for Lambda
|
|
634
|
+
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
635
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
636
|
+
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
637
|
+
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
638
|
+
name = "/aws/lambda/TestApiHandler"
|
|
639
|
+
tags = var.tags
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
# Create proxy resource (captures all paths)
|
|
644
|
+
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
645
|
+
rest_api_id = module.rest_api.api_id
|
|
646
|
+
parent_id = module.rest_api.api_root_resource_id
|
|
647
|
+
path_part = "{proxy+}"
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
# Lambda integration for REST API
|
|
651
|
+
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
652
|
+
rest_api_id = module.rest_api.api_id
|
|
653
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
654
|
+
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
655
|
+
|
|
656
|
+
integration_http_method = "POST"
|
|
657
|
+
type = "AWS_PROXY"
|
|
658
|
+
uri = aws_lambda_function.api_lambda.invoke_arn
|
|
659
|
+
|
|
660
|
+
depends_on = [aws_lambda_function.api_lambda]
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
# Method for proxy integration
|
|
664
|
+
resource "aws_api_gateway_method" "proxy_method" {
|
|
665
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
666
|
+
rest_api_id = module.rest_api.api_id
|
|
667
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
668
|
+
http_method = "ANY"
|
|
669
|
+
|
|
670
|
+
# Note: you may wish to suppress the checkov rule CKV_AWS_59 if you are absolutely sure you
|
|
671
|
+
# need a public API without authentication
|
|
672
|
+
authorization = "NONE"
|
|
673
|
+
|
|
674
|
+
request_parameters = {
|
|
675
|
+
"method.request.path.proxy" = true
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
depends_on = []
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
# OPTIONS method for CORS preflight
|
|
682
|
+
resource "aws_api_gateway_method" "options_method" {
|
|
683
|
+
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
684
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
685
|
+
rest_api_id = module.rest_api.api_id
|
|
686
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
687
|
+
http_method = "OPTIONS"
|
|
688
|
+
authorization = "NONE"
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
# CORS integration for OPTIONS method
|
|
692
|
+
resource "aws_api_gateway_integration" "options_integration" {
|
|
693
|
+
rest_api_id = module.rest_api.api_id
|
|
694
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
695
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
696
|
+
|
|
697
|
+
type = "MOCK"
|
|
698
|
+
request_templates = {
|
|
699
|
+
"application/json" = "{\\"statusCode\\": 204}"
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
# OPTIONS method response
|
|
704
|
+
resource "aws_api_gateway_method_response" "options_response" {
|
|
705
|
+
rest_api_id = module.rest_api.api_id
|
|
706
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
707
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
708
|
+
status_code = "204"
|
|
709
|
+
|
|
710
|
+
response_parameters = {
|
|
711
|
+
"method.response.header.Access-Control-Allow-Headers" = true
|
|
712
|
+
"method.response.header.Access-Control-Allow-Methods" = true
|
|
713
|
+
"method.response.header.Access-Control-Allow-Origin" = true
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
# OPTIONS integration response
|
|
718
|
+
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
719
|
+
rest_api_id = module.rest_api.api_id
|
|
720
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
721
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
722
|
+
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
723
|
+
|
|
724
|
+
response_parameters = {
|
|
725
|
+
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
726
|
+
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
727
|
+
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
# API Gateway deployment
|
|
732
|
+
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
733
|
+
rest_api_id = module.rest_api.api_id
|
|
734
|
+
|
|
735
|
+
triggers = {
|
|
736
|
+
redeployment = sha1(jsonencode([
|
|
737
|
+
aws_api_gateway_resource.proxy_resource.id,
|
|
738
|
+
aws_api_gateway_method.proxy_method.id,
|
|
739
|
+
aws_api_gateway_integration.lambda_integration.id,
|
|
740
|
+
aws_api_gateway_method.options_method.id,
|
|
741
|
+
aws_api_gateway_integration.options_integration.id,
|
|
742
|
+
]))
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
lifecycle {
|
|
746
|
+
create_before_destroy = true
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
depends_on = [
|
|
750
|
+
aws_api_gateway_method.proxy_method,
|
|
751
|
+
aws_api_gateway_integration.lambda_integration,
|
|
752
|
+
aws_api_gateway_method.options_method,
|
|
753
|
+
aws_api_gateway_integration.options_integration,
|
|
754
|
+
aws_api_gateway_method_response.options_response,
|
|
755
|
+
aws_api_gateway_integration_response.options_integration_response,
|
|
756
|
+
]
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
# API Gateway stage
|
|
760
|
+
resource "aws_api_gateway_stage" "api_stage" {
|
|
761
|
+
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
762
|
+
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
763
|
+
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
764
|
+
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
765
|
+
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
766
|
+
rest_api_id = module.rest_api.api_id
|
|
767
|
+
stage_name = "prod"
|
|
768
|
+
xray_tracing_enabled = true
|
|
769
|
+
|
|
770
|
+
tags = var.tags
|
|
771
|
+
|
|
772
|
+
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
# API Gateway Resource Policy
|
|
776
|
+
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
777
|
+
rest_api_id = module.rest_api.api_id
|
|
778
|
+
|
|
779
|
+
policy = jsonencode({
|
|
780
|
+
Version = "2012-10-17"
|
|
781
|
+
Statement = [
|
|
782
|
+
{
|
|
783
|
+
# Allow all callers to invoke the API in the resource policy
|
|
784
|
+
Effect = "Allow"
|
|
785
|
+
Principal = "*"
|
|
786
|
+
Action = "execute-api:Invoke"
|
|
787
|
+
Resource = "execute-api:/*"
|
|
788
|
+
}
|
|
789
|
+
]
|
|
790
|
+
})
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
# Lambda permission for API Gateway to invoke the function
|
|
794
|
+
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
795
|
+
statement_id = "AllowExecutionFromAPIGateway"
|
|
796
|
+
action = "lambda:InvokeFunction"
|
|
797
|
+
function_name = aws_lambda_function.api_lambda.function_name
|
|
798
|
+
principal = "apigateway.amazonaws.com"
|
|
799
|
+
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
800
|
+
|
|
801
|
+
depends_on = [module.rest_api, aws_lambda_function.api_lambda]
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
# Add API url to runtime config
|
|
805
|
+
module "add_url_to_runtime_config" {
|
|
806
|
+
source = "../../../core/runtime-config/entry"
|
|
807
|
+
|
|
808
|
+
key_path = "apis.TestApi"
|
|
809
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
810
|
+
|
|
811
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
# Outputs
|
|
815
|
+
|
|
816
|
+
# API Gateway Outputs (from core module)
|
|
817
|
+
output "api_id" {
|
|
818
|
+
description = "ID of the REST API Gateway"
|
|
819
|
+
value = module.rest_api.api_id
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
output "api_arn" {
|
|
823
|
+
description = "ARN of the REST API Gateway"
|
|
824
|
+
value = module.rest_api.api_arn
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
output "api_endpoint" {
|
|
828
|
+
description = "Base URL of the REST API Gateway"
|
|
829
|
+
value = module.rest_api.api_endpoint
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
output "api_execution_arn" {
|
|
833
|
+
description = "Execution ARN of the REST API Gateway"
|
|
834
|
+
value = module.rest_api.api_execution_arn
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
output "stage_invoke_url" {
|
|
838
|
+
description = "Invoke URL of the API Gateway stage"
|
|
839
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
output "stage_arn" {
|
|
843
|
+
description = "ARN of the API Gateway stage"
|
|
844
|
+
value = aws_api_gateway_stage.api_stage.arn
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
output "stage_execution_arn" {
|
|
848
|
+
description = "Execution ARN of the API Gateway stage"
|
|
849
|
+
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
output "deployment_id" {
|
|
853
|
+
description = "ID of the API Gateway deployment"
|
|
854
|
+
value = aws_api_gateway_deployment.api_deployment.id
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
output "stage_id" {
|
|
858
|
+
description = "ID of the API Gateway stage"
|
|
859
|
+
value = aws_api_gateway_stage.api_stage.id
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
# Lambda Function Outputs
|
|
863
|
+
output "lambda_function_name" {
|
|
864
|
+
description = "Name of the Lambda function"
|
|
865
|
+
value = aws_lambda_function.api_lambda.function_name
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
output "lambda_function_arn" {
|
|
869
|
+
description = "ARN of the Lambda function"
|
|
870
|
+
value = aws_lambda_function.api_lambda.arn
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
output "lambda_invoke_arn" {
|
|
874
|
+
description = "Invoke ARN of the Lambda function"
|
|
875
|
+
value = aws_lambda_function.api_lambda.invoke_arn
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
output "lambda_qualified_arn" {
|
|
879
|
+
description = "Qualified ARN of the Lambda function"
|
|
880
|
+
value = aws_lambda_function.api_lambda.qualified_arn
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
output "lambda_version" {
|
|
884
|
+
description = "Version of the Lambda function"
|
|
885
|
+
value = aws_lambda_function.api_lambda.version
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
output "lambda_source_code_hash" {
|
|
889
|
+
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
890
|
+
value = aws_lambda_function.api_lambda.source_code_hash
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
output "lambda_source_code_size" {
|
|
894
|
+
description = "Size of the Lambda deployment package in bytes"
|
|
895
|
+
value = aws_lambda_function.api_lambda.source_code_size
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
# IAM Role Outputs
|
|
899
|
+
output "lambda_execution_role_arn" {
|
|
900
|
+
description = "ARN of the Lambda execution role"
|
|
901
|
+
value = aws_iam_role.lambda_execution_role.arn
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
output "lambda_execution_role_name" {
|
|
905
|
+
description = "Name of the Lambda execution role"
|
|
906
|
+
value = aws_iam_role.lambda_execution_role.name
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
# Integration Outputs
|
|
910
|
+
output "integration_id" {
|
|
911
|
+
description = "ID of the Lambda integration"
|
|
912
|
+
value = aws_api_gateway_integration.lambda_integration.id
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
output "proxy_resource_id" {
|
|
916
|
+
description = "ID of the proxy resource"
|
|
917
|
+
value = aws_api_gateway_resource.proxy_resource.id
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
output "proxy_method_id" {
|
|
921
|
+
description = "ID of the proxy method"
|
|
922
|
+
value = aws_api_gateway_method.proxy_method.id
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
# CloudWatch Log Groups
|
|
926
|
+
output "lambda_log_group_name" {
|
|
927
|
+
description = "Name of the Lambda CloudWatch log group"
|
|
928
|
+
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
output "lambda_log_group_arn" {
|
|
932
|
+
description = "ARN of the Lambda CloudWatch log group"
|
|
933
|
+
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
934
|
+
}
|
|
935
|
+
"
|
|
936
|
+
`;
|
|
937
|
+
|
|
938
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with custom namespace > custom-namespace-main.smithy 1`] = `
|
|
939
|
+
"$version: "2.0"
|
|
940
|
+
|
|
941
|
+
namespace com.example.custom
|
|
942
|
+
|
|
943
|
+
use aws.protocols#restJson1
|
|
944
|
+
use smithy.framework#ValidationException
|
|
945
|
+
|
|
946
|
+
/// TODO: describe your service here
|
|
947
|
+
@title("TestApi")
|
|
948
|
+
@restJson1
|
|
949
|
+
service TestApi {
|
|
950
|
+
version: "1.0.0"
|
|
951
|
+
operations: [
|
|
952
|
+
Echo
|
|
953
|
+
]
|
|
954
|
+
errors: [
|
|
955
|
+
ValidationException
|
|
956
|
+
]
|
|
957
|
+
}
|
|
958
|
+
"
|
|
959
|
+
`;
|
|
960
|
+
|
|
961
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > backend-project.json 1`] = `
|
|
962
|
+
"{
|
|
963
|
+
"name": "@proj/test-api",
|
|
964
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
965
|
+
"sourceRoot": "test-api/backend/src",
|
|
966
|
+
"projectType": "library",
|
|
967
|
+
"tags": [],
|
|
968
|
+
"metadata": {
|
|
969
|
+
"generator": "ts#smithy-api",
|
|
970
|
+
"apiName": "test-api",
|
|
971
|
+
"auth": "None",
|
|
972
|
+
"modelProject": "@proj/test-api-model",
|
|
973
|
+
"ports": [3001]
|
|
974
|
+
},
|
|
975
|
+
"targets": {
|
|
976
|
+
"build": {
|
|
977
|
+
"dependsOn": ["lint", "compile", "test", "bundle"]
|
|
978
|
+
},
|
|
979
|
+
"compile": {
|
|
980
|
+
"executor": "nx:run-commands",
|
|
981
|
+
"outputs": ["{workspaceRoot}/dist/test-api/backend/tsc"],
|
|
982
|
+
"options": {
|
|
983
|
+
"command": "tsc --build tsconfig.lib.json",
|
|
984
|
+
"cwd": "{projectRoot}"
|
|
985
|
+
},
|
|
986
|
+
"dependsOn": ["copy-ssdk"]
|
|
987
|
+
},
|
|
988
|
+
"test": {
|
|
989
|
+
"executor": "@nx/vite:test",
|
|
990
|
+
"outputs": ["{options.reportsDirectory}"],
|
|
991
|
+
"options": {
|
|
992
|
+
"reportsDirectory": "../../coverage/test-api/backend"
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
"bundle": {
|
|
996
|
+
"cache": true,
|
|
997
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}/bundle"],
|
|
998
|
+
"executor": "nx:run-commands",
|
|
999
|
+
"options": {
|
|
1000
|
+
"command": "rolldown -c rolldown.config.ts",
|
|
1001
|
+
"cwd": "{projectRoot}"
|
|
1002
|
+
},
|
|
1003
|
+
"dependsOn": ["compile"]
|
|
1004
|
+
},
|
|
1005
|
+
"copy-ssdk": {
|
|
1006
|
+
"cache": true,
|
|
1007
|
+
"inputs": [
|
|
1008
|
+
{
|
|
1009
|
+
"dependentTasksOutputFiles": "**/*"
|
|
1010
|
+
}
|
|
1011
|
+
],
|
|
1012
|
+
"executor": "nx:run-commands",
|
|
1013
|
+
"options": {
|
|
1014
|
+
"commands": [
|
|
1015
|
+
"rimraf test-api/backend/src/generated",
|
|
1016
|
+
"make-dir test-api/backend/src/generated",
|
|
1017
|
+
"ncp dist/test-api/model/build/ssdk test-api/backend/src/generated/ssdk"
|
|
1018
|
+
],
|
|
1019
|
+
"parallel": false
|
|
1020
|
+
},
|
|
1021
|
+
"outputs": ["{projectRoot}/src/generated"],
|
|
1022
|
+
"dependsOn": ["@proj/test-api-model:build"]
|
|
1023
|
+
},
|
|
1024
|
+
"watch-copy-ssdk": {
|
|
1025
|
+
"executor": "nx:run-commands",
|
|
1026
|
+
"options": {
|
|
1027
|
+
"command": "nx watch --projects=@proj/test-api-model --includeDependentProjects -- nx run @proj/test-api:copy-ssdk"
|
|
1028
|
+
},
|
|
1029
|
+
"continuous": true
|
|
1030
|
+
},
|
|
1031
|
+
"serve": {
|
|
1032
|
+
"executor": "nx:run-commands",
|
|
1033
|
+
"options": {
|
|
1034
|
+
"command": "tsx --watch src/local-server.ts",
|
|
1035
|
+
"cwd": "{projectRoot}"
|
|
1036
|
+
},
|
|
1037
|
+
"continuous": true,
|
|
1038
|
+
"dependsOn": ["copy-ssdk", "watch-copy-ssdk"]
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
"
|
|
1043
|
+
`;
|
|
1044
|
+
|
|
1045
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > context.ts 1`] = `
|
|
1046
|
+
"import { Logger } from '@aws-lambda-powertools/logger';
|
|
1047
|
+
import { Metrics } from '@aws-lambda-powertools/metrics';
|
|
1048
|
+
import { Tracer } from '@aws-lambda-powertools/tracer';
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Context provided to all operations.
|
|
1052
|
+
*/
|
|
1053
|
+
export interface ServiceContext {
|
|
1054
|
+
tracer: Tracer;
|
|
1055
|
+
logger: Logger;
|
|
1056
|
+
metrics: Metrics;
|
|
1057
|
+
}
|
|
1058
|
+
"
|
|
1059
|
+
`;
|
|
1060
|
+
|
|
1061
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > echo.ts 1`] = `
|
|
1062
|
+
"import { ServiceContext } from '../context.js';
|
|
1063
|
+
import { Echo as EchoOperation } from '../generated/ssdk/index.js';
|
|
1064
|
+
|
|
1065
|
+
export const Echo: EchoOperation<ServiceContext> = async (input, ctx) => {
|
|
1066
|
+
ctx.logger.info('Starting Echo operation');
|
|
1067
|
+
return input;
|
|
1068
|
+
};
|
|
1069
|
+
"
|
|
1070
|
+
`;
|
|
1071
|
+
|
|
1072
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > handler.ts 1`] = `
|
|
1073
|
+
"import {
|
|
1074
|
+
convertEvent,
|
|
1075
|
+
convertVersion1Response,
|
|
1076
|
+
} from '@aws-smithy/server-apigateway';
|
|
1077
|
+
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
|
1078
|
+
import middy from '@middy/core';
|
|
1079
|
+
import { Tracer } from '@aws-lambda-powertools/tracer';
|
|
1080
|
+
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
|
|
1081
|
+
import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware';
|
|
1082
|
+
import { Logger } from '@aws-lambda-powertools/logger';
|
|
1083
|
+
import { Metrics } from '@aws-lambda-powertools/metrics';
|
|
1084
|
+
import { logMetrics } from '@aws-lambda-powertools/metrics/middleware';
|
|
1085
|
+
import { Service } from './service.js';
|
|
1086
|
+
import { getTestApiServiceHandler } from './generated/ssdk/index.js';
|
|
1087
|
+
|
|
1088
|
+
process.env.POWERTOOLS_METRICS_NAMESPACE = 'TestApi';
|
|
1089
|
+
process.env.POWERTOOLS_SERVICE_NAME = 'TestApi';
|
|
1090
|
+
|
|
1091
|
+
const tracer = new Tracer();
|
|
1092
|
+
const logger = new Logger();
|
|
1093
|
+
const metrics = new Metrics();
|
|
1094
|
+
|
|
1095
|
+
const serviceHandler = getTestApiServiceHandler(Service);
|
|
1096
|
+
|
|
1097
|
+
export const lambdaHandler = async (
|
|
1098
|
+
event: APIGatewayProxyEvent,
|
|
1099
|
+
): Promise<APIGatewayProxyResult> => {
|
|
1100
|
+
const httpRequest = convertEvent(event);
|
|
1101
|
+
const httpResponse = await serviceHandler.handle(httpRequest, {
|
|
1102
|
+
tracer,
|
|
1103
|
+
logger,
|
|
1104
|
+
metrics,
|
|
1105
|
+
});
|
|
1106
|
+
const apiGatewayResponse = convertVersion1Response(httpResponse);
|
|
1107
|
+
|
|
1108
|
+
return {
|
|
1109
|
+
...apiGatewayResponse,
|
|
1110
|
+
headers: {
|
|
1111
|
+
'Access-Control-Allow-Origin': '*',
|
|
1112
|
+
'Access-Control-Allow-Methods': '*',
|
|
1113
|
+
...apiGatewayResponse.headers,
|
|
1114
|
+
},
|
|
1115
|
+
};
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
|
|
1119
|
+
.use(captureLambdaHandler(tracer))
|
|
1120
|
+
.use(injectLambdaContext(logger))
|
|
1121
|
+
.use(logMetrics(metrics))
|
|
1122
|
+
.handler(lambdaHandler);
|
|
1123
|
+
"
|
|
1124
|
+
`;
|
|
1125
|
+
|
|
1126
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > local-server.ts 1`] = `
|
|
1127
|
+
"import { IncomingMessage, ServerResponse, createServer } from 'http';
|
|
1128
|
+
import { convertRequest, writeResponse } from '@aws-smithy/server-node';
|
|
1129
|
+
import { Logger } from '@aws-lambda-powertools/logger';
|
|
1130
|
+
import { Metrics } from '@aws-lambda-powertools/metrics';
|
|
1131
|
+
import { Tracer } from '@aws-lambda-powertools/tracer';
|
|
1132
|
+
import { Service } from './service.js';
|
|
1133
|
+
import { getTestApiServiceHandler } from './generated/ssdk/index.js';
|
|
1134
|
+
|
|
1135
|
+
const PORT = 3001;
|
|
1136
|
+
|
|
1137
|
+
const tracer = new Tracer();
|
|
1138
|
+
const logger = new Logger();
|
|
1139
|
+
const metrics = new Metrics();
|
|
1140
|
+
|
|
1141
|
+
const serviceHandler = getTestApiServiceHandler(Service);
|
|
1142
|
+
|
|
1143
|
+
const server = createServer(async function (
|
|
1144
|
+
req: IncomingMessage,
|
|
1145
|
+
res: ServerResponse<IncomingMessage> & { req: IncomingMessage },
|
|
1146
|
+
) {
|
|
1147
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
1148
|
+
res.setHeader('Access-Control-Allow-Methods', '*');
|
|
1149
|
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
1150
|
+
|
|
1151
|
+
if (req.method === 'OPTIONS') {
|
|
1152
|
+
res.writeHead(204);
|
|
1153
|
+
res.end();
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
const httpRequest = convertRequest(req);
|
|
1158
|
+
const httpResponse = await serviceHandler.handle(httpRequest, {
|
|
1159
|
+
tracer,
|
|
1160
|
+
logger,
|
|
1161
|
+
metrics,
|
|
1162
|
+
});
|
|
1163
|
+
return writeResponse(httpResponse, res);
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
server.listen(PORT);
|
|
1167
|
+
console.log(\`Started server on port \${PORT}...\`);
|
|
1168
|
+
"
|
|
1169
|
+
`;
|
|
1170
|
+
|
|
1171
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > model-project.json 1`] = `
|
|
1172
|
+
"{
|
|
1173
|
+
"name": "@proj/test-api-model",
|
|
1174
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
1175
|
+
"sourceRoot": "test-api/model/src",
|
|
1176
|
+
"projectType": "library",
|
|
1177
|
+
"metadata": {
|
|
1178
|
+
"generator": "smithy#project",
|
|
1179
|
+
"apiName": "test-api-model",
|
|
1180
|
+
"backendProject": "@proj/test-api"
|
|
1181
|
+
},
|
|
1182
|
+
"targets": {
|
|
1183
|
+
"build": {
|
|
1184
|
+
"dependsOn": ["compile"]
|
|
1185
|
+
},
|
|
1186
|
+
"compile": {
|
|
1187
|
+
"cache": true,
|
|
1188
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}/build"],
|
|
1189
|
+
"executor": "nx:run-commands",
|
|
1190
|
+
"options": {
|
|
1191
|
+
"commands": [
|
|
1192
|
+
"rimraf dist/test-api/model/build",
|
|
1193
|
+
"make-dir dist/test-api/model/build",
|
|
1194
|
+
"docker build -f test-api/model/build.Dockerfile --target export --output type=local,dest=dist/test-api/model/build test-api/model"
|
|
1195
|
+
],
|
|
1196
|
+
"parallel": false,
|
|
1197
|
+
"cwd": "{workspaceRoot}"
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
"
|
|
1203
|
+
`;
|
|
1204
|
+
|
|
1205
|
+
exports[`tsSmithyApiGenerator > should generate smithy ts api with default options > service.ts 1`] = `
|
|
1206
|
+
"import { ServiceContext } from './context.js';
|
|
1207
|
+
import { TestApiService } from './generated/ssdk/index.js';
|
|
1208
|
+
import { Echo } from './operations/echo.js';
|
|
1209
|
+
|
|
1210
|
+
// Register operations to the service here
|
|
1211
|
+
export const Service: TestApiService<ServiceContext> = {
|
|
1212
|
+
Echo,
|
|
1213
|
+
};
|
|
1214
|
+
"
|
|
1215
|
+
`;
|
|
1216
|
+
|
|
1217
|
+
exports[`tsSmithyApiGenerator > should handle kebab-case API names correctly > kebab-case-handler.ts 1`] = `
|
|
1218
|
+
"import {
|
|
1219
|
+
convertEvent,
|
|
1220
|
+
convertVersion1Response,
|
|
1221
|
+
} from '@aws-smithy/server-apigateway';
|
|
1222
|
+
import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
|
|
1223
|
+
import middy from '@middy/core';
|
|
1224
|
+
import { Tracer } from '@aws-lambda-powertools/tracer';
|
|
1225
|
+
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer/middleware';
|
|
1226
|
+
import { injectLambdaContext } from '@aws-lambda-powertools/logger/middleware';
|
|
1227
|
+
import { Logger } from '@aws-lambda-powertools/logger';
|
|
1228
|
+
import { Metrics } from '@aws-lambda-powertools/metrics';
|
|
1229
|
+
import { logMetrics } from '@aws-lambda-powertools/metrics/middleware';
|
|
1230
|
+
import { Service } from './service.js';
|
|
1231
|
+
import { getMyTestApiServiceHandler } from './generated/ssdk/index.js';
|
|
1232
|
+
|
|
1233
|
+
process.env.POWERTOOLS_METRICS_NAMESPACE = 'MyTestApi';
|
|
1234
|
+
process.env.POWERTOOLS_SERVICE_NAME = 'MyTestApi';
|
|
1235
|
+
|
|
1236
|
+
const tracer = new Tracer();
|
|
1237
|
+
const logger = new Logger();
|
|
1238
|
+
const metrics = new Metrics();
|
|
1239
|
+
|
|
1240
|
+
const serviceHandler = getMyTestApiServiceHandler(Service);
|
|
1241
|
+
|
|
1242
|
+
export const lambdaHandler = async (
|
|
1243
|
+
event: APIGatewayProxyEvent,
|
|
1244
|
+
): Promise<APIGatewayProxyResult> => {
|
|
1245
|
+
const httpRequest = convertEvent(event);
|
|
1246
|
+
const httpResponse = await serviceHandler.handle(httpRequest, {
|
|
1247
|
+
tracer,
|
|
1248
|
+
logger,
|
|
1249
|
+
metrics,
|
|
1250
|
+
});
|
|
1251
|
+
const apiGatewayResponse = convertVersion1Response(httpResponse);
|
|
1252
|
+
|
|
1253
|
+
return {
|
|
1254
|
+
...apiGatewayResponse,
|
|
1255
|
+
headers: {
|
|
1256
|
+
'Access-Control-Allow-Origin': '*',
|
|
1257
|
+
'Access-Control-Allow-Methods': '*',
|
|
1258
|
+
...apiGatewayResponse.headers,
|
|
1259
|
+
},
|
|
1260
|
+
};
|
|
1261
|
+
};
|
|
1262
|
+
|
|
1263
|
+
export const handler = middy<APIGatewayProxyEvent, APIGatewayProxyResult>()
|
|
1264
|
+
.use(captureLambdaHandler(tracer))
|
|
1265
|
+
.use(injectLambdaContext(logger))
|
|
1266
|
+
.use(logMetrics(metrics))
|
|
1267
|
+
.handler(lambdaHandler);
|
|
1268
|
+
"
|
|
1269
|
+
`;
|
|
1270
|
+
|
|
1271
|
+
exports[`tsSmithyApiGenerator > should handle kebab-case API names correctly > kebab-case-main.smithy 1`] = `
|
|
1272
|
+
"$version: "2.0"
|
|
1273
|
+
|
|
1274
|
+
namespace proj
|
|
1275
|
+
|
|
1276
|
+
use aws.protocols#restJson1
|
|
1277
|
+
use smithy.framework#ValidationException
|
|
1278
|
+
|
|
1279
|
+
/// TODO: describe your service here
|
|
1280
|
+
@title("MyTestApi")
|
|
1281
|
+
@restJson1
|
|
1282
|
+
service MyTestApi {
|
|
1283
|
+
version: "1.0.0"
|
|
1284
|
+
operations: [
|
|
1285
|
+
Echo
|
|
1286
|
+
]
|
|
1287
|
+
errors: [
|
|
1288
|
+
ValidationException
|
|
1289
|
+
]
|
|
1290
|
+
}
|
|
1291
|
+
"
|
|
1292
|
+
`;
|
|
1293
|
+
|
|
1294
|
+
exports[`tsSmithyApiGenerator > terraform iacProvider > should generate terraform files for REST API with Cognito auth and snapshot them > terraform-rest-cognito-files 1`] = `
|
|
1295
|
+
{
|
|
1296
|
+
"rest-api.tf": "# Core REST API Gateway module
|
|
1297
|
+
# This module creates the API Gateway REST API, deployment, stage, and logging resources
|
|
1298
|
+
|
|
1299
|
+
terraform {
|
|
1300
|
+
required_version = ">= 1.0"
|
|
1301
|
+
|
|
1302
|
+
required_providers {
|
|
1303
|
+
aws = {
|
|
1304
|
+
source = "hashicorp/aws"
|
|
1305
|
+
version = "~> 6.0"
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
# Core REST API Gateway Variables
|
|
1311
|
+
|
|
1312
|
+
variable "api_name" {
|
|
1313
|
+
description = "Name of the REST API Gateway"
|
|
1314
|
+
type = string
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
variable "api_description" {
|
|
1318
|
+
description = "Description of the REST API Gateway"
|
|
1319
|
+
type = string
|
|
1320
|
+
default = "REST API Gateway"
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
variable "stage_name" {
|
|
1324
|
+
description = "Name of the API Gateway stage"
|
|
1325
|
+
type = string
|
|
1326
|
+
default = "prod"
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
variable "stage_auto_deploy" {
|
|
1330
|
+
description = "Whether to automatically deploy the API stage"
|
|
1331
|
+
type = bool
|
|
1332
|
+
default = true
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
# CORS Configuration
|
|
1336
|
+
|
|
1337
|
+
variable "cors_allow_headers" {
|
|
1338
|
+
description = "List of allowed headers for CORS"
|
|
1339
|
+
type = list(string)
|
|
1340
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
variable "cors_allow_methods" {
|
|
1344
|
+
description = "List of allowed HTTP methods for CORS"
|
|
1345
|
+
type = list(string)
|
|
1346
|
+
default = ["*"]
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
variable "cors_allow_origins" {
|
|
1350
|
+
description = "List of allowed origins for CORS"
|
|
1351
|
+
type = list(string)
|
|
1352
|
+
default = ["*"]
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
# Tags
|
|
1356
|
+
|
|
1357
|
+
variable "tags" {
|
|
1358
|
+
description = "Tags to apply to all resources"
|
|
1359
|
+
type = map(string)
|
|
1360
|
+
default = {}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
# Data sources
|
|
1364
|
+
data "aws_region" "current" {}
|
|
1365
|
+
data "aws_caller_identity" "current" {}
|
|
1366
|
+
|
|
1367
|
+
# Resources
|
|
1368
|
+
|
|
1369
|
+
# Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
|
|
1370
|
+
|
|
1371
|
+
# REST API Gateway
|
|
1372
|
+
resource "aws_api_gateway_rest_api" "rest_api" {
|
|
1373
|
+
name = var.api_name
|
|
1374
|
+
description = var.api_description
|
|
1375
|
+
|
|
1376
|
+
endpoint_configuration {
|
|
1377
|
+
types = ["REGIONAL"]
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
lifecycle {
|
|
1381
|
+
create_before_destroy = true
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
tags = var.tags
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
# Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
|
|
1388
|
+
# after all methods and integrations are defined
|
|
1389
|
+
|
|
1390
|
+
# Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
|
|
1391
|
+
|
|
1392
|
+
# Gateway Response for CORS (4XX errors)
|
|
1393
|
+
resource "aws_api_gateway_gateway_response" "cors_4xx" {
|
|
1394
|
+
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
1395
|
+
response_type = "DEFAULT_4XX"
|
|
1396
|
+
|
|
1397
|
+
response_parameters = {
|
|
1398
|
+
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
1399
|
+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
1400
|
+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
# Gateway Response for CORS (5XX errors)
|
|
1405
|
+
resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
1406
|
+
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
1407
|
+
response_type = "DEFAULT_5XX"
|
|
1408
|
+
|
|
1409
|
+
response_parameters = {
|
|
1410
|
+
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
1411
|
+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
1412
|
+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
# Outputs
|
|
1417
|
+
|
|
1418
|
+
output "api_id" {
|
|
1419
|
+
description = "ID of the REST API Gateway"
|
|
1420
|
+
value = aws_api_gateway_rest_api.rest_api.id
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
output "api_arn" {
|
|
1424
|
+
description = "ARN of the REST API Gateway"
|
|
1425
|
+
value = aws_api_gateway_rest_api.rest_api.arn
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
output "api_endpoint" {
|
|
1429
|
+
description = "Base URL of the REST API Gateway"
|
|
1430
|
+
value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
output "api_execution_arn" {
|
|
1434
|
+
description = "Execution ARN of the REST API Gateway"
|
|
1435
|
+
value = aws_api_gateway_rest_api.rest_api.execution_arn
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
output "api_root_resource_id" {
|
|
1439
|
+
description = "Root resource ID of the REST API Gateway"
|
|
1440
|
+
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
# Note: Stage and deployment outputs are provided by the consuming module
|
|
1444
|
+
|
|
1445
|
+
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
1446
|
+
"test-api.tf": "terraform {
|
|
1447
|
+
required_version = ">= 1.0"
|
|
1448
|
+
|
|
1449
|
+
required_providers {
|
|
1450
|
+
aws = {
|
|
1451
|
+
source = "hashicorp/aws"
|
|
1452
|
+
version = "~> 6.0"
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
# Authentication Configuration
|
|
1458
|
+
variable "user_pool_id" {
|
|
1459
|
+
description = "Cognito User Pool ID for authentication"
|
|
1460
|
+
type = string
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
variable "user_pool_client_ids" {
|
|
1464
|
+
description = "List of Cognito User Pool Client IDs"
|
|
1465
|
+
type = list(string)
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
variable "env" {
|
|
1469
|
+
description = "Environment variables for the Lambda function"
|
|
1470
|
+
type = map(string)
|
|
1471
|
+
default = {}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
variable "additional_iam_policy_statements" {
|
|
1475
|
+
description = "Additional IAM policy statements for the Lambda function"
|
|
1476
|
+
type = list(object({
|
|
1477
|
+
Effect = string
|
|
1478
|
+
Action = list(string)
|
|
1479
|
+
Resource = list(string)
|
|
1480
|
+
}))
|
|
1481
|
+
default = []
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
# CORS Configuration (passed to core module)
|
|
1485
|
+
|
|
1486
|
+
variable "cors_allow_headers" {
|
|
1487
|
+
description = "List of allowed headers for CORS"
|
|
1488
|
+
type = list(string)
|
|
1489
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
variable "cors_allow_methods" {
|
|
1493
|
+
description = "List of allowed HTTP methods for CORS"
|
|
1494
|
+
type = list(string)
|
|
1495
|
+
default = ["*"]
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
variable "cors_allow_origins" {
|
|
1499
|
+
description = "List of allowed origins for CORS"
|
|
1500
|
+
type = list(string)
|
|
1501
|
+
default = ["*"]
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
# Tags
|
|
1505
|
+
variable "tags" {
|
|
1506
|
+
description = "Tags to apply to all resources"
|
|
1507
|
+
type = map(string)
|
|
1508
|
+
default = {}
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
# Get current AWS region and account ID
|
|
1512
|
+
data "aws_region" "current" {}
|
|
1513
|
+
data "aws_caller_identity" "current" {}
|
|
1514
|
+
|
|
1515
|
+
# Resources
|
|
1516
|
+
|
|
1517
|
+
# Create Lambda ZIP file from the FastAPI bundle directory
|
|
1518
|
+
data "archive_file" "lambda_zip" {
|
|
1519
|
+
type = "zip"
|
|
1520
|
+
source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
|
|
1521
|
+
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
# Use the core REST API module
|
|
1525
|
+
module "rest_api" {
|
|
1526
|
+
source = "../../../core/api/rest-api"
|
|
1527
|
+
|
|
1528
|
+
api_name = "TestApi"
|
|
1529
|
+
api_description = "TestApi REST API"
|
|
1530
|
+
stage_name = "prod"
|
|
1531
|
+
stage_auto_deploy = true
|
|
1532
|
+
|
|
1533
|
+
# CORS Configuration
|
|
1534
|
+
cors_allow_headers = var.cors_allow_headers
|
|
1535
|
+
cors_allow_methods = var.cors_allow_methods
|
|
1536
|
+
cors_allow_origins = var.cors_allow_origins
|
|
1537
|
+
|
|
1538
|
+
# Tags
|
|
1539
|
+
tags = var.tags
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
# Lambda function
|
|
1543
|
+
resource "aws_lambda_function" "api_lambda" {
|
|
1544
|
+
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
1545
|
+
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
1546
|
+
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
1547
|
+
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
1548
|
+
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
1549
|
+
filename = data.archive_file.lambda_zip.output_path
|
|
1550
|
+
function_name = "TestApiHandler"
|
|
1551
|
+
role = aws_iam_role.lambda_execution_role.arn
|
|
1552
|
+
handler = "index.handler"
|
|
1553
|
+
runtime = "nodejs22.x"
|
|
1554
|
+
timeout = 30
|
|
1555
|
+
memory_size = 128
|
|
1556
|
+
|
|
1557
|
+
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
1558
|
+
|
|
1559
|
+
# Enable X-Ray tracing
|
|
1560
|
+
tracing_config {
|
|
1561
|
+
mode = "Active"
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
environment {
|
|
1565
|
+
variables = merge({
|
|
1566
|
+
AWS_CONNECTION_REUSE_ENABLED = "1"
|
|
1567
|
+
}, var.env)
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
tags = var.tags
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
# IAM role for Lambda execution
|
|
1574
|
+
resource "aws_iam_role" "lambda_execution_role" {
|
|
1575
|
+
name = "TestApiHandler-execution-role"
|
|
1576
|
+
|
|
1577
|
+
assume_role_policy = jsonencode({
|
|
1578
|
+
Version = "2012-10-17"
|
|
1579
|
+
Statement = [
|
|
1580
|
+
{
|
|
1581
|
+
Action = "sts:AssumeRole"
|
|
1582
|
+
Effect = "Allow"
|
|
1583
|
+
Principal = {
|
|
1584
|
+
Service = "lambda.amazonaws.com"
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
]
|
|
1588
|
+
})
|
|
1589
|
+
|
|
1590
|
+
tags = var.tags
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
# Attach basic execution policy to Lambda role
|
|
1594
|
+
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
1595
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
1596
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
# Attach X-Ray tracing policy to Lambda role
|
|
1600
|
+
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
1601
|
+
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
1602
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
# Additional IAM policies for Lambda (if provided)
|
|
1606
|
+
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
1607
|
+
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
1608
|
+
name = "TestApiHandler-additional-policies"
|
|
1609
|
+
role = aws_iam_role.lambda_execution_role.id
|
|
1610
|
+
|
|
1611
|
+
policy = jsonencode({
|
|
1612
|
+
Version = "2012-10-17"
|
|
1613
|
+
Statement = var.additional_iam_policy_statements
|
|
1614
|
+
})
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
# CloudWatch Log Group for Lambda
|
|
1618
|
+
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
1619
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
1620
|
+
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
1621
|
+
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
1622
|
+
name = "/aws/lambda/TestApiHandler"
|
|
1623
|
+
tags = var.tags
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
# Cognito User Pool Authorizer
|
|
1627
|
+
resource "aws_api_gateway_authorizer" "cognito_authorizer" {
|
|
1628
|
+
name = "TestApiAuthorizer"
|
|
1629
|
+
rest_api_id = module.rest_api.api_id
|
|
1630
|
+
type = "COGNITO_USER_POOLS"
|
|
1631
|
+
provider_arns = ["arn:aws:cognito-idp:\${data.aws_region.current.name}:\${data.aws_caller_identity.current.account_id}:userpool/\${var.user_pool_id}"]
|
|
1632
|
+
identity_source = "method.request.header.Authorization"
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
# Create proxy resource (captures all paths)
|
|
1636
|
+
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
1637
|
+
rest_api_id = module.rest_api.api_id
|
|
1638
|
+
parent_id = module.rest_api.api_root_resource_id
|
|
1639
|
+
path_part = "{proxy+}"
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
# Lambda integration for REST API
|
|
1643
|
+
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
1644
|
+
rest_api_id = module.rest_api.api_id
|
|
1645
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
1646
|
+
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
1647
|
+
|
|
1648
|
+
integration_http_method = "POST"
|
|
1649
|
+
type = "AWS_PROXY"
|
|
1650
|
+
uri = aws_lambda_function.api_lambda.invoke_arn
|
|
1651
|
+
|
|
1652
|
+
depends_on = [aws_lambda_function.api_lambda]
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
# Method for proxy integration
|
|
1656
|
+
resource "aws_api_gateway_method" "proxy_method" {
|
|
1657
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
1658
|
+
rest_api_id = module.rest_api.api_id
|
|
1659
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
1660
|
+
http_method = "ANY"
|
|
1661
|
+
|
|
1662
|
+
authorization = "COGNITO_USER_POOLS"
|
|
1663
|
+
authorizer_id = aws_api_gateway_authorizer.cognito_authorizer.id
|
|
1664
|
+
|
|
1665
|
+
request_parameters = {
|
|
1666
|
+
"method.request.path.proxy" = true
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
depends_on = [aws_api_gateway_authorizer.cognito_authorizer]
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
# OPTIONS method for CORS preflight
|
|
1673
|
+
resource "aws_api_gateway_method" "options_method" {
|
|
1674
|
+
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
1675
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
1676
|
+
rest_api_id = module.rest_api.api_id
|
|
1677
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
1678
|
+
http_method = "OPTIONS"
|
|
1679
|
+
authorization = "NONE"
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
# CORS integration for OPTIONS method
|
|
1683
|
+
resource "aws_api_gateway_integration" "options_integration" {
|
|
1684
|
+
rest_api_id = module.rest_api.api_id
|
|
1685
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
1686
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
1687
|
+
|
|
1688
|
+
type = "MOCK"
|
|
1689
|
+
request_templates = {
|
|
1690
|
+
"application/json" = "{\\"statusCode\\": 204}"
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
# OPTIONS method response
|
|
1695
|
+
resource "aws_api_gateway_method_response" "options_response" {
|
|
1696
|
+
rest_api_id = module.rest_api.api_id
|
|
1697
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
1698
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
1699
|
+
status_code = "204"
|
|
1700
|
+
|
|
1701
|
+
response_parameters = {
|
|
1702
|
+
"method.response.header.Access-Control-Allow-Headers" = true
|
|
1703
|
+
"method.response.header.Access-Control-Allow-Methods" = true
|
|
1704
|
+
"method.response.header.Access-Control-Allow-Origin" = true
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
# OPTIONS integration response
|
|
1709
|
+
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
1710
|
+
rest_api_id = module.rest_api.api_id
|
|
1711
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
1712
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
1713
|
+
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
1714
|
+
|
|
1715
|
+
response_parameters = {
|
|
1716
|
+
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
1717
|
+
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
1718
|
+
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
# API Gateway deployment
|
|
1723
|
+
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
1724
|
+
rest_api_id = module.rest_api.api_id
|
|
1725
|
+
|
|
1726
|
+
triggers = {
|
|
1727
|
+
redeployment = sha1(jsonencode([
|
|
1728
|
+
aws_api_gateway_resource.proxy_resource.id,
|
|
1729
|
+
aws_api_gateway_method.proxy_method.id,
|
|
1730
|
+
aws_api_gateway_integration.lambda_integration.id,
|
|
1731
|
+
aws_api_gateway_method.options_method.id,
|
|
1732
|
+
aws_api_gateway_integration.options_integration.id,
|
|
1733
|
+
]))
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
lifecycle {
|
|
1737
|
+
create_before_destroy = true
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
depends_on = [
|
|
1741
|
+
aws_api_gateway_method.proxy_method,
|
|
1742
|
+
aws_api_gateway_integration.lambda_integration,
|
|
1743
|
+
aws_api_gateway_method.options_method,
|
|
1744
|
+
aws_api_gateway_integration.options_integration,
|
|
1745
|
+
aws_api_gateway_method_response.options_response,
|
|
1746
|
+
aws_api_gateway_integration_response.options_integration_response,
|
|
1747
|
+
]
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
# API Gateway stage
|
|
1751
|
+
resource "aws_api_gateway_stage" "api_stage" {
|
|
1752
|
+
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
1753
|
+
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
1754
|
+
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
1755
|
+
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
1756
|
+
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
1757
|
+
rest_api_id = module.rest_api.api_id
|
|
1758
|
+
stage_name = "prod"
|
|
1759
|
+
xray_tracing_enabled = true
|
|
1760
|
+
|
|
1761
|
+
tags = var.tags
|
|
1762
|
+
|
|
1763
|
+
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
# API Gateway Resource Policy
|
|
1767
|
+
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
1768
|
+
rest_api_id = module.rest_api.api_id
|
|
1769
|
+
|
|
1770
|
+
policy = jsonencode({
|
|
1771
|
+
Version = "2012-10-17"
|
|
1772
|
+
Statement = [
|
|
1773
|
+
{
|
|
1774
|
+
# Allow all callers to invoke the API in the resource policy, since auth is handled by Cognito
|
|
1775
|
+
Effect = "Allow"
|
|
1776
|
+
Principal = "*"
|
|
1777
|
+
Action = "execute-api:Invoke"
|
|
1778
|
+
Resource = "execute-api:/*"
|
|
1779
|
+
}
|
|
1780
|
+
]
|
|
1781
|
+
})
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
# Lambda permission for API Gateway to invoke the function
|
|
1785
|
+
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
1786
|
+
statement_id = "AllowExecutionFromAPIGateway"
|
|
1787
|
+
action = "lambda:InvokeFunction"
|
|
1788
|
+
function_name = aws_lambda_function.api_lambda.function_name
|
|
1789
|
+
principal = "apigateway.amazonaws.com"
|
|
1790
|
+
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
1791
|
+
|
|
1792
|
+
depends_on = [module.rest_api, aws_lambda_function.api_lambda]
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
# Add API url to runtime config
|
|
1796
|
+
module "add_url_to_runtime_config" {
|
|
1797
|
+
source = "../../../core/runtime-config/entry"
|
|
1798
|
+
|
|
1799
|
+
key_path = "apis.TestApi"
|
|
1800
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
1801
|
+
|
|
1802
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
# Outputs
|
|
1806
|
+
|
|
1807
|
+
# API Gateway Outputs (from core module)
|
|
1808
|
+
output "api_id" {
|
|
1809
|
+
description = "ID of the REST API Gateway"
|
|
1810
|
+
value = module.rest_api.api_id
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
output "api_arn" {
|
|
1814
|
+
description = "ARN of the REST API Gateway"
|
|
1815
|
+
value = module.rest_api.api_arn
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
output "api_endpoint" {
|
|
1819
|
+
description = "Base URL of the REST API Gateway"
|
|
1820
|
+
value = module.rest_api.api_endpoint
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
output "api_execution_arn" {
|
|
1824
|
+
description = "Execution ARN of the REST API Gateway"
|
|
1825
|
+
value = module.rest_api.api_execution_arn
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
output "stage_invoke_url" {
|
|
1829
|
+
description = "Invoke URL of the API Gateway stage"
|
|
1830
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
output "stage_arn" {
|
|
1834
|
+
description = "ARN of the API Gateway stage"
|
|
1835
|
+
value = aws_api_gateway_stage.api_stage.arn
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
output "stage_execution_arn" {
|
|
1839
|
+
description = "Execution ARN of the API Gateway stage"
|
|
1840
|
+
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
output "deployment_id" {
|
|
1844
|
+
description = "ID of the API Gateway deployment"
|
|
1845
|
+
value = aws_api_gateway_deployment.api_deployment.id
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
output "stage_id" {
|
|
1849
|
+
description = "ID of the API Gateway stage"
|
|
1850
|
+
value = aws_api_gateway_stage.api_stage.id
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
# Lambda Function Outputs
|
|
1854
|
+
output "lambda_function_name" {
|
|
1855
|
+
description = "Name of the Lambda function"
|
|
1856
|
+
value = aws_lambda_function.api_lambda.function_name
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
output "lambda_function_arn" {
|
|
1860
|
+
description = "ARN of the Lambda function"
|
|
1861
|
+
value = aws_lambda_function.api_lambda.arn
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
output "lambda_invoke_arn" {
|
|
1865
|
+
description = "Invoke ARN of the Lambda function"
|
|
1866
|
+
value = aws_lambda_function.api_lambda.invoke_arn
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
output "lambda_qualified_arn" {
|
|
1870
|
+
description = "Qualified ARN of the Lambda function"
|
|
1871
|
+
value = aws_lambda_function.api_lambda.qualified_arn
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
output "lambda_version" {
|
|
1875
|
+
description = "Version of the Lambda function"
|
|
1876
|
+
value = aws_lambda_function.api_lambda.version
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
output "lambda_source_code_hash" {
|
|
1880
|
+
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
1881
|
+
value = aws_lambda_function.api_lambda.source_code_hash
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
output "lambda_source_code_size" {
|
|
1885
|
+
description = "Size of the Lambda deployment package in bytes"
|
|
1886
|
+
value = aws_lambda_function.api_lambda.source_code_size
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
# IAM Role Outputs
|
|
1890
|
+
output "lambda_execution_role_arn" {
|
|
1891
|
+
description = "ARN of the Lambda execution role"
|
|
1892
|
+
value = aws_iam_role.lambda_execution_role.arn
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
output "lambda_execution_role_name" {
|
|
1896
|
+
description = "Name of the Lambda execution role"
|
|
1897
|
+
value = aws_iam_role.lambda_execution_role.name
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
# Integration Outputs
|
|
1901
|
+
output "integration_id" {
|
|
1902
|
+
description = "ID of the Lambda integration"
|
|
1903
|
+
value = aws_api_gateway_integration.lambda_integration.id
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
output "proxy_resource_id" {
|
|
1907
|
+
description = "ID of the proxy resource"
|
|
1908
|
+
value = aws_api_gateway_resource.proxy_resource.id
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
output "proxy_method_id" {
|
|
1912
|
+
description = "ID of the proxy method"
|
|
1913
|
+
value = aws_api_gateway_method.proxy_method.id
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
# CloudWatch Log Groups
|
|
1917
|
+
output "lambda_log_group_name" {
|
|
1918
|
+
description = "Name of the Lambda CloudWatch log group"
|
|
1919
|
+
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
output "lambda_log_group_arn" {
|
|
1923
|
+
description = "ARN of the Lambda CloudWatch log group"
|
|
1924
|
+
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
1925
|
+
}
|
|
1926
|
+
",
|
|
1927
|
+
}
|
|
1928
|
+
`;
|
|
1929
|
+
|
|
1930
|
+
exports[`tsSmithyApiGenerator > terraform iacProvider > should generate terraform files for REST API with IAM auth and snapshot them > terraform-rest-iam-files 1`] = `
|
|
1931
|
+
{
|
|
1932
|
+
"rest-api.tf": "# Core REST API Gateway module
|
|
1933
|
+
# This module creates the API Gateway REST API, deployment, stage, and logging resources
|
|
1934
|
+
|
|
1935
|
+
terraform {
|
|
1936
|
+
required_version = ">= 1.0"
|
|
1937
|
+
|
|
1938
|
+
required_providers {
|
|
1939
|
+
aws = {
|
|
1940
|
+
source = "hashicorp/aws"
|
|
1941
|
+
version = "~> 6.0"
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
# Core REST API Gateway Variables
|
|
1947
|
+
|
|
1948
|
+
variable "api_name" {
|
|
1949
|
+
description = "Name of the REST API Gateway"
|
|
1950
|
+
type = string
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
variable "api_description" {
|
|
1954
|
+
description = "Description of the REST API Gateway"
|
|
1955
|
+
type = string
|
|
1956
|
+
default = "REST API Gateway"
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
variable "stage_name" {
|
|
1960
|
+
description = "Name of the API Gateway stage"
|
|
1961
|
+
type = string
|
|
1962
|
+
default = "prod"
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
variable "stage_auto_deploy" {
|
|
1966
|
+
description = "Whether to automatically deploy the API stage"
|
|
1967
|
+
type = bool
|
|
1968
|
+
default = true
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
# CORS Configuration
|
|
1972
|
+
|
|
1973
|
+
variable "cors_allow_headers" {
|
|
1974
|
+
description = "List of allowed headers for CORS"
|
|
1975
|
+
type = list(string)
|
|
1976
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
variable "cors_allow_methods" {
|
|
1980
|
+
description = "List of allowed HTTP methods for CORS"
|
|
1981
|
+
type = list(string)
|
|
1982
|
+
default = ["*"]
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
variable "cors_allow_origins" {
|
|
1986
|
+
description = "List of allowed origins for CORS"
|
|
1987
|
+
type = list(string)
|
|
1988
|
+
default = ["*"]
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
# Tags
|
|
1992
|
+
|
|
1993
|
+
variable "tags" {
|
|
1994
|
+
description = "Tags to apply to all resources"
|
|
1995
|
+
type = map(string)
|
|
1996
|
+
default = {}
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
# Data sources
|
|
2000
|
+
data "aws_region" "current" {}
|
|
2001
|
+
data "aws_caller_identity" "current" {}
|
|
2002
|
+
|
|
2003
|
+
# Resources
|
|
2004
|
+
|
|
2005
|
+
# Note: CloudWatch logging removed due to account-level CloudWatch Logs role ARN requirement
|
|
2006
|
+
|
|
2007
|
+
# REST API Gateway
|
|
2008
|
+
resource "aws_api_gateway_rest_api" "rest_api" {
|
|
2009
|
+
name = var.api_name
|
|
2010
|
+
description = var.api_description
|
|
2011
|
+
|
|
2012
|
+
endpoint_configuration {
|
|
2013
|
+
types = ["REGIONAL"]
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
lifecycle {
|
|
2017
|
+
create_before_destroy = true
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
tags = var.tags
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
# Note: Deployment and stage are created in the consuming module (e.g., foo-api.tf)
|
|
2024
|
+
# after all methods and integrations are defined
|
|
2025
|
+
|
|
2026
|
+
# Note: CloudWatch Log Group removed due to account-level CloudWatch Logs role ARN requirement
|
|
2027
|
+
|
|
2028
|
+
# Gateway Response for CORS (4XX errors)
|
|
2029
|
+
resource "aws_api_gateway_gateway_response" "cors_4xx" {
|
|
2030
|
+
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
2031
|
+
response_type = "DEFAULT_4XX"
|
|
2032
|
+
|
|
2033
|
+
response_parameters = {
|
|
2034
|
+
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
2035
|
+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
2036
|
+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
# Gateway Response for CORS (5XX errors)
|
|
2041
|
+
resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
2042
|
+
rest_api_id = aws_api_gateway_rest_api.rest_api.id
|
|
2043
|
+
response_type = "DEFAULT_5XX"
|
|
2044
|
+
|
|
2045
|
+
response_parameters = {
|
|
2046
|
+
"gatewayresponse.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
2047
|
+
"gatewayresponse.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
2048
|
+
"gatewayresponse.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
# Outputs
|
|
2053
|
+
|
|
2054
|
+
output "api_id" {
|
|
2055
|
+
description = "ID of the REST API Gateway"
|
|
2056
|
+
value = aws_api_gateway_rest_api.rest_api.id
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
output "api_arn" {
|
|
2060
|
+
description = "ARN of the REST API Gateway"
|
|
2061
|
+
value = aws_api_gateway_rest_api.rest_api.arn
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
output "api_endpoint" {
|
|
2065
|
+
description = "Base URL of the REST API Gateway"
|
|
2066
|
+
value = "https://\${aws_api_gateway_rest_api.rest_api.id}.execute-api.\${data.aws_region.current.id}.amazonaws.com"
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
output "api_execution_arn" {
|
|
2070
|
+
description = "Execution ARN of the REST API Gateway"
|
|
2071
|
+
value = aws_api_gateway_rest_api.rest_api.execution_arn
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
output "api_root_resource_id" {
|
|
2075
|
+
description = "Root resource ID of the REST API Gateway"
|
|
2076
|
+
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
# Note: Stage and deployment outputs are provided by the consuming module
|
|
2080
|
+
|
|
2081
|
+
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement",
|
|
2082
|
+
"test-api.tf": "terraform {
|
|
2083
|
+
required_version = ">= 1.0"
|
|
2084
|
+
|
|
2085
|
+
required_providers {
|
|
2086
|
+
aws = {
|
|
2087
|
+
source = "hashicorp/aws"
|
|
2088
|
+
version = "~> 6.0"
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
variable "env" {
|
|
2095
|
+
description = "Environment variables for the Lambda function"
|
|
2096
|
+
type = map(string)
|
|
2097
|
+
default = {}
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
variable "additional_iam_policy_statements" {
|
|
2101
|
+
description = "Additional IAM policy statements for the Lambda function"
|
|
2102
|
+
type = list(object({
|
|
2103
|
+
Effect = string
|
|
2104
|
+
Action = list(string)
|
|
2105
|
+
Resource = list(string)
|
|
2106
|
+
}))
|
|
2107
|
+
default = []
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
# CORS Configuration (passed to core module)
|
|
2111
|
+
|
|
2112
|
+
variable "cors_allow_headers" {
|
|
2113
|
+
description = "List of allowed headers for CORS"
|
|
2114
|
+
type = list(string)
|
|
2115
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
variable "cors_allow_methods" {
|
|
2119
|
+
description = "List of allowed HTTP methods for CORS"
|
|
2120
|
+
type = list(string)
|
|
2121
|
+
default = ["*"]
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
variable "cors_allow_origins" {
|
|
2125
|
+
description = "List of allowed origins for CORS"
|
|
2126
|
+
type = list(string)
|
|
2127
|
+
default = ["*"]
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
# Tags
|
|
2131
|
+
variable "tags" {
|
|
2132
|
+
description = "Tags to apply to all resources"
|
|
2133
|
+
type = map(string)
|
|
2134
|
+
default = {}
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
# Get current AWS region and account ID
|
|
2138
|
+
data "aws_region" "current" {}
|
|
2139
|
+
data "aws_caller_identity" "current" {}
|
|
2140
|
+
|
|
2141
|
+
# Resources
|
|
2142
|
+
|
|
2143
|
+
# Create Lambda ZIP file from the FastAPI bundle directory
|
|
2144
|
+
data "archive_file" "lambda_zip" {
|
|
2145
|
+
type = "zip"
|
|
2146
|
+
source_dir = "\${path.module}/../../../../../../../dist/test-api/backend/bundle"
|
|
2147
|
+
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/test-api/lambda.zip"
|
|
2148
|
+
}
|
|
2149
|
+
|
|
2150
|
+
# Use the core REST API module
|
|
2151
|
+
module "rest_api" {
|
|
2152
|
+
source = "../../../core/api/rest-api"
|
|
2153
|
+
|
|
2154
|
+
api_name = "TestApi"
|
|
2155
|
+
api_description = "TestApi REST API"
|
|
2156
|
+
stage_name = "prod"
|
|
2157
|
+
stage_auto_deploy = true
|
|
2158
|
+
|
|
2159
|
+
# CORS Configuration
|
|
2160
|
+
cors_allow_headers = var.cors_allow_headers
|
|
2161
|
+
cors_allow_methods = var.cors_allow_methods
|
|
2162
|
+
cors_allow_origins = var.cors_allow_origins
|
|
2163
|
+
|
|
2164
|
+
# Tags
|
|
2165
|
+
tags = var.tags
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
# Lambda function
|
|
2169
|
+
resource "aws_lambda_function" "api_lambda" {
|
|
2170
|
+
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
2171
|
+
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
2172
|
+
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
2173
|
+
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
2174
|
+
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
2175
|
+
filename = data.archive_file.lambda_zip.output_path
|
|
2176
|
+
function_name = "TestApiHandler"
|
|
2177
|
+
role = aws_iam_role.lambda_execution_role.arn
|
|
2178
|
+
handler = "index.handler"
|
|
2179
|
+
runtime = "nodejs22.x"
|
|
2180
|
+
timeout = 30
|
|
2181
|
+
memory_size = 128
|
|
2182
|
+
|
|
2183
|
+
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
2184
|
+
|
|
2185
|
+
# Enable X-Ray tracing
|
|
2186
|
+
tracing_config {
|
|
2187
|
+
mode = "Active"
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
environment {
|
|
2191
|
+
variables = merge({
|
|
2192
|
+
AWS_CONNECTION_REUSE_ENABLED = "1"
|
|
2193
|
+
}, var.env)
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
tags = var.tags
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
# IAM role for Lambda execution
|
|
2200
|
+
resource "aws_iam_role" "lambda_execution_role" {
|
|
2201
|
+
name = "TestApiHandler-execution-role"
|
|
2202
|
+
|
|
2203
|
+
assume_role_policy = jsonencode({
|
|
2204
|
+
Version = "2012-10-17"
|
|
2205
|
+
Statement = [
|
|
2206
|
+
{
|
|
2207
|
+
Action = "sts:AssumeRole"
|
|
2208
|
+
Effect = "Allow"
|
|
2209
|
+
Principal = {
|
|
2210
|
+
Service = "lambda.amazonaws.com"
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
]
|
|
2214
|
+
})
|
|
2215
|
+
|
|
2216
|
+
tags = var.tags
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
# Attach basic execution policy to Lambda role
|
|
2220
|
+
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
2221
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
2222
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
# Attach X-Ray tracing policy to Lambda role
|
|
2226
|
+
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
2227
|
+
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
2228
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
# Additional IAM policies for Lambda (if provided)
|
|
2232
|
+
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
2233
|
+
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
2234
|
+
name = "TestApiHandler-additional-policies"
|
|
2235
|
+
role = aws_iam_role.lambda_execution_role.id
|
|
2236
|
+
|
|
2237
|
+
policy = jsonencode({
|
|
2238
|
+
Version = "2012-10-17"
|
|
2239
|
+
Statement = var.additional_iam_policy_statements
|
|
2240
|
+
})
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
# CloudWatch Log Group for Lambda
|
|
2244
|
+
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
2245
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
2246
|
+
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
2247
|
+
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
2248
|
+
name = "/aws/lambda/TestApiHandler"
|
|
2249
|
+
tags = var.tags
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
|
|
2253
|
+
# Create proxy resource (captures all paths)
|
|
2254
|
+
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
2255
|
+
rest_api_id = module.rest_api.api_id
|
|
2256
|
+
parent_id = module.rest_api.api_root_resource_id
|
|
2257
|
+
path_part = "{proxy+}"
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
# Lambda integration for REST API
|
|
2261
|
+
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
2262
|
+
rest_api_id = module.rest_api.api_id
|
|
2263
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2264
|
+
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
2265
|
+
|
|
2266
|
+
integration_http_method = "POST"
|
|
2267
|
+
type = "AWS_PROXY"
|
|
2268
|
+
uri = aws_lambda_function.api_lambda.invoke_arn
|
|
2269
|
+
|
|
2270
|
+
depends_on = [aws_lambda_function.api_lambda]
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
# Method for proxy integration
|
|
2274
|
+
resource "aws_api_gateway_method" "proxy_method" {
|
|
2275
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
2276
|
+
rest_api_id = module.rest_api.api_id
|
|
2277
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2278
|
+
http_method = "ANY"
|
|
2279
|
+
|
|
2280
|
+
authorization = "AWS_IAM"
|
|
2281
|
+
|
|
2282
|
+
request_parameters = {
|
|
2283
|
+
"method.request.path.proxy" = true
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
depends_on = []
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
# OPTIONS method for CORS preflight
|
|
2290
|
+
resource "aws_api_gateway_method" "options_method" {
|
|
2291
|
+
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
2292
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
2293
|
+
rest_api_id = module.rest_api.api_id
|
|
2294
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2295
|
+
http_method = "OPTIONS"
|
|
2296
|
+
authorization = "NONE"
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
# CORS integration for OPTIONS method
|
|
2300
|
+
resource "aws_api_gateway_integration" "options_integration" {
|
|
2301
|
+
rest_api_id = module.rest_api.api_id
|
|
2302
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2303
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
2304
|
+
|
|
2305
|
+
type = "MOCK"
|
|
2306
|
+
request_templates = {
|
|
2307
|
+
"application/json" = "{\\"statusCode\\": 204}"
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
# OPTIONS method response
|
|
2312
|
+
resource "aws_api_gateway_method_response" "options_response" {
|
|
2313
|
+
rest_api_id = module.rest_api.api_id
|
|
2314
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2315
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
2316
|
+
status_code = "204"
|
|
2317
|
+
|
|
2318
|
+
response_parameters = {
|
|
2319
|
+
"method.response.header.Access-Control-Allow-Headers" = true
|
|
2320
|
+
"method.response.header.Access-Control-Allow-Methods" = true
|
|
2321
|
+
"method.response.header.Access-Control-Allow-Origin" = true
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
# OPTIONS integration response
|
|
2326
|
+
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
2327
|
+
rest_api_id = module.rest_api.api_id
|
|
2328
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2329
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
2330
|
+
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
2331
|
+
|
|
2332
|
+
response_parameters = {
|
|
2333
|
+
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
2334
|
+
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
2335
|
+
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
# API Gateway deployment
|
|
2340
|
+
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
2341
|
+
rest_api_id = module.rest_api.api_id
|
|
2342
|
+
|
|
2343
|
+
triggers = {
|
|
2344
|
+
redeployment = sha1(jsonencode([
|
|
2345
|
+
aws_api_gateway_resource.proxy_resource.id,
|
|
2346
|
+
aws_api_gateway_method.proxy_method.id,
|
|
2347
|
+
aws_api_gateway_integration.lambda_integration.id,
|
|
2348
|
+
aws_api_gateway_method.options_method.id,
|
|
2349
|
+
aws_api_gateway_integration.options_integration.id,
|
|
2350
|
+
]))
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
lifecycle {
|
|
2354
|
+
create_before_destroy = true
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
depends_on = [
|
|
2358
|
+
aws_api_gateway_method.proxy_method,
|
|
2359
|
+
aws_api_gateway_integration.lambda_integration,
|
|
2360
|
+
aws_api_gateway_method.options_method,
|
|
2361
|
+
aws_api_gateway_integration.options_integration,
|
|
2362
|
+
aws_api_gateway_method_response.options_response,
|
|
2363
|
+
aws_api_gateway_integration_response.options_integration_response,
|
|
2364
|
+
]
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
# API Gateway stage
|
|
2368
|
+
resource "aws_api_gateway_stage" "api_stage" {
|
|
2369
|
+
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
2370
|
+
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
2371
|
+
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
2372
|
+
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
2373
|
+
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
2374
|
+
rest_api_id = module.rest_api.api_id
|
|
2375
|
+
stage_name = "prod"
|
|
2376
|
+
xray_tracing_enabled = true
|
|
2377
|
+
|
|
2378
|
+
tags = var.tags
|
|
2379
|
+
|
|
2380
|
+
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
# API Gateway Resource Policy
|
|
2384
|
+
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
2385
|
+
rest_api_id = module.rest_api.api_id
|
|
2386
|
+
|
|
2387
|
+
policy = jsonencode({
|
|
2388
|
+
Version = "2012-10-17"
|
|
2389
|
+
Statement = [
|
|
2390
|
+
{
|
|
2391
|
+
# Grant any AWS credentials from the account to call the API
|
|
2392
|
+
# Machine to machine fine-grained access can be defined here using more specific principals
|
|
2393
|
+
# (eg roles or users) and resources (eg which api paths may be invoked by which principal) if required
|
|
2394
|
+
Effect = "Allow"
|
|
2395
|
+
Principal = {
|
|
2396
|
+
AWS = "arn:aws:iam::\${data.aws_caller_identity.current.account_id}:root"
|
|
2397
|
+
}
|
|
2398
|
+
Action = "execute-api:Invoke"
|
|
2399
|
+
Resource = "execute-api:/*"
|
|
2400
|
+
},
|
|
2401
|
+
{
|
|
2402
|
+
# Open up OPTIONS to allow browsers to make unauthenticated preflight requests
|
|
2403
|
+
Effect = "Allow"
|
|
2404
|
+
Principal = "*"
|
|
2405
|
+
Action = "execute-api:Invoke"
|
|
2406
|
+
Resource = "execute-api:/*/OPTIONS/*"
|
|
2407
|
+
}
|
|
2408
|
+
]
|
|
2409
|
+
})
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
# Lambda permission for API Gateway to invoke the function
|
|
2413
|
+
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
2414
|
+
statement_id = "AllowExecutionFromAPIGateway"
|
|
2415
|
+
action = "lambda:InvokeFunction"
|
|
2416
|
+
function_name = aws_lambda_function.api_lambda.function_name
|
|
2417
|
+
principal = "apigateway.amazonaws.com"
|
|
2418
|
+
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
2419
|
+
|
|
2420
|
+
depends_on = [module.rest_api, aws_lambda_function.api_lambda]
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
# Add API url to runtime config
|
|
2424
|
+
module "add_url_to_runtime_config" {
|
|
2425
|
+
source = "../../../core/runtime-config/entry"
|
|
2426
|
+
|
|
2427
|
+
key_path = "apis.TestApi"
|
|
2428
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
2429
|
+
|
|
2430
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
# Outputs
|
|
2434
|
+
|
|
2435
|
+
# API Gateway Outputs (from core module)
|
|
2436
|
+
output "api_id" {
|
|
2437
|
+
description = "ID of the REST API Gateway"
|
|
2438
|
+
value = module.rest_api.api_id
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
output "api_arn" {
|
|
2442
|
+
description = "ARN of the REST API Gateway"
|
|
2443
|
+
value = module.rest_api.api_arn
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
output "api_endpoint" {
|
|
2447
|
+
description = "Base URL of the REST API Gateway"
|
|
2448
|
+
value = module.rest_api.api_endpoint
|
|
2449
|
+
}
|
|
2450
|
+
|
|
2451
|
+
output "api_execution_arn" {
|
|
2452
|
+
description = "Execution ARN of the REST API Gateway"
|
|
2453
|
+
value = module.rest_api.api_execution_arn
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
output "stage_invoke_url" {
|
|
2457
|
+
description = "Invoke URL of the API Gateway stage"
|
|
2458
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
output "stage_arn" {
|
|
2462
|
+
description = "ARN of the API Gateway stage"
|
|
2463
|
+
value = aws_api_gateway_stage.api_stage.arn
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
output "stage_execution_arn" {
|
|
2467
|
+
description = "Execution ARN of the API Gateway stage"
|
|
2468
|
+
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
output "deployment_id" {
|
|
2472
|
+
description = "ID of the API Gateway deployment"
|
|
2473
|
+
value = aws_api_gateway_deployment.api_deployment.id
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
output "stage_id" {
|
|
2477
|
+
description = "ID of the API Gateway stage"
|
|
2478
|
+
value = aws_api_gateway_stage.api_stage.id
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
# Lambda Function Outputs
|
|
2482
|
+
output "lambda_function_name" {
|
|
2483
|
+
description = "Name of the Lambda function"
|
|
2484
|
+
value = aws_lambda_function.api_lambda.function_name
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
output "lambda_function_arn" {
|
|
2488
|
+
description = "ARN of the Lambda function"
|
|
2489
|
+
value = aws_lambda_function.api_lambda.arn
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
output "lambda_invoke_arn" {
|
|
2493
|
+
description = "Invoke ARN of the Lambda function"
|
|
2494
|
+
value = aws_lambda_function.api_lambda.invoke_arn
|
|
2495
|
+
}
|
|
2496
|
+
|
|
2497
|
+
output "lambda_qualified_arn" {
|
|
2498
|
+
description = "Qualified ARN of the Lambda function"
|
|
2499
|
+
value = aws_lambda_function.api_lambda.qualified_arn
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
output "lambda_version" {
|
|
2503
|
+
description = "Version of the Lambda function"
|
|
2504
|
+
value = aws_lambda_function.api_lambda.version
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
output "lambda_source_code_hash" {
|
|
2508
|
+
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
2509
|
+
value = aws_lambda_function.api_lambda.source_code_hash
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
output "lambda_source_code_size" {
|
|
2513
|
+
description = "Size of the Lambda deployment package in bytes"
|
|
2514
|
+
value = aws_lambda_function.api_lambda.source_code_size
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
# IAM Role Outputs
|
|
2518
|
+
output "lambda_execution_role_arn" {
|
|
2519
|
+
description = "ARN of the Lambda execution role"
|
|
2520
|
+
value = aws_iam_role.lambda_execution_role.arn
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
output "lambda_execution_role_name" {
|
|
2524
|
+
description = "Name of the Lambda execution role"
|
|
2525
|
+
value = aws_iam_role.lambda_execution_role.name
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
# Integration Outputs
|
|
2529
|
+
output "integration_id" {
|
|
2530
|
+
description = "ID of the Lambda integration"
|
|
2531
|
+
value = aws_api_gateway_integration.lambda_integration.id
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
output "proxy_resource_id" {
|
|
2535
|
+
description = "ID of the proxy resource"
|
|
2536
|
+
value = aws_api_gateway_resource.proxy_resource.id
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
output "proxy_method_id" {
|
|
2540
|
+
description = "ID of the proxy method"
|
|
2541
|
+
value = aws_api_gateway_method.proxy_method.id
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
# CloudWatch Log Groups
|
|
2545
|
+
output "lambda_log_group_name" {
|
|
2546
|
+
description = "Name of the Lambda CloudWatch log group"
|
|
2547
|
+
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
output "lambda_log_group_arn" {
|
|
2551
|
+
description = "ARN of the Lambda CloudWatch log group"
|
|
2552
|
+
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
2553
|
+
}
|
|
2554
|
+
",
|
|
2555
|
+
}
|
|
2556
|
+
`;
|
|
2557
|
+
|
|
2558
|
+
exports[`tsSmithyApiGenerator > terraform iacProvider > should generate terraform with custom namespace > terraform-custom-namespace.tf 1`] = `
|
|
2559
|
+
"terraform {
|
|
2560
|
+
required_version = ">= 1.0"
|
|
2561
|
+
|
|
2562
|
+
required_providers {
|
|
2563
|
+
aws = {
|
|
2564
|
+
source = "hashicorp/aws"
|
|
2565
|
+
version = "~> 6.0"
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
|
|
2571
|
+
variable "env" {
|
|
2572
|
+
description = "Environment variables for the Lambda function"
|
|
2573
|
+
type = map(string)
|
|
2574
|
+
default = {}
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
variable "additional_iam_policy_statements" {
|
|
2578
|
+
description = "Additional IAM policy statements for the Lambda function"
|
|
2579
|
+
type = list(object({
|
|
2580
|
+
Effect = string
|
|
2581
|
+
Action = list(string)
|
|
2582
|
+
Resource = list(string)
|
|
2583
|
+
}))
|
|
2584
|
+
default = []
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
# CORS Configuration (passed to core module)
|
|
2588
|
+
|
|
2589
|
+
variable "cors_allow_headers" {
|
|
2590
|
+
description = "List of allowed headers for CORS"
|
|
2591
|
+
type = list(string)
|
|
2592
|
+
default = ["authorization", "content-type", "x-amz-content-sha256", "x-amz-date", "x-amz-security-token"]
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
variable "cors_allow_methods" {
|
|
2596
|
+
description = "List of allowed HTTP methods for CORS"
|
|
2597
|
+
type = list(string)
|
|
2598
|
+
default = ["*"]
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
variable "cors_allow_origins" {
|
|
2602
|
+
description = "List of allowed origins for CORS"
|
|
2603
|
+
type = list(string)
|
|
2604
|
+
default = ["*"]
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
# Tags
|
|
2608
|
+
variable "tags" {
|
|
2609
|
+
description = "Tags to apply to all resources"
|
|
2610
|
+
type = map(string)
|
|
2611
|
+
default = {}
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
# Get current AWS region and account ID
|
|
2615
|
+
data "aws_region" "current" {}
|
|
2616
|
+
data "aws_caller_identity" "current" {}
|
|
2617
|
+
|
|
2618
|
+
# Resources
|
|
2619
|
+
|
|
2620
|
+
# Create Lambda ZIP file from the FastAPI bundle directory
|
|
2621
|
+
data "archive_file" "lambda_zip" {
|
|
2622
|
+
type = "zip"
|
|
2623
|
+
source_dir = "\${path.module}/../../../../../../../dist/custom-api/backend/bundle"
|
|
2624
|
+
output_path = "\${path.module}/../../../../../../../dist/packages/common/terraform/apis/custom-api/lambda.zip"
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
# Use the core REST API module
|
|
2628
|
+
module "rest_api" {
|
|
2629
|
+
source = "../../../core/api/rest-api"
|
|
2630
|
+
|
|
2631
|
+
api_name = "CustomApi"
|
|
2632
|
+
api_description = "CustomApi REST API"
|
|
2633
|
+
stage_name = "prod"
|
|
2634
|
+
stage_auto_deploy = true
|
|
2635
|
+
|
|
2636
|
+
# CORS Configuration
|
|
2637
|
+
cors_allow_headers = var.cors_allow_headers
|
|
2638
|
+
cors_allow_methods = var.cors_allow_methods
|
|
2639
|
+
cors_allow_origins = var.cors_allow_origins
|
|
2640
|
+
|
|
2641
|
+
# Tags
|
|
2642
|
+
tags = var.tags
|
|
2643
|
+
}
|
|
2644
|
+
|
|
2645
|
+
# Lambda function
|
|
2646
|
+
resource "aws_lambda_function" "api_lambda" {
|
|
2647
|
+
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
2648
|
+
#checkov:skip=CKV_AWS_116:Dead Letter Queue not required for this simple API use case
|
|
2649
|
+
#checkov:skip=CKV_AWS_272:Code signing not required for this use case
|
|
2650
|
+
#checkov:skip=CKV_AWS_115:Concurrent execution limit not required for this use case
|
|
2651
|
+
#checkov:skip=CKV_AWS_173:Lambda environment variables encrypted by managed key
|
|
2652
|
+
filename = data.archive_file.lambda_zip.output_path
|
|
2653
|
+
function_name = "CustomApiHandler"
|
|
2654
|
+
role = aws_iam_role.lambda_execution_role.arn
|
|
2655
|
+
handler = "index.handler"
|
|
2656
|
+
runtime = "nodejs22.x"
|
|
2657
|
+
timeout = 30
|
|
2658
|
+
memory_size = 128
|
|
2659
|
+
|
|
2660
|
+
source_code_hash = data.archive_file.lambda_zip.output_base64sha256
|
|
2661
|
+
|
|
2662
|
+
# Enable X-Ray tracing
|
|
2663
|
+
tracing_config {
|
|
2664
|
+
mode = "Active"
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
environment {
|
|
2668
|
+
variables = merge({
|
|
2669
|
+
AWS_CONNECTION_REUSE_ENABLED = "1"
|
|
2670
|
+
}, var.env)
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
tags = var.tags
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
# IAM role for Lambda execution
|
|
2677
|
+
resource "aws_iam_role" "lambda_execution_role" {
|
|
2678
|
+
name = "CustomApiHandler-execution-role"
|
|
2679
|
+
|
|
2680
|
+
assume_role_policy = jsonencode({
|
|
2681
|
+
Version = "2012-10-17"
|
|
2682
|
+
Statement = [
|
|
2683
|
+
{
|
|
2684
|
+
Action = "sts:AssumeRole"
|
|
2685
|
+
Effect = "Allow"
|
|
2686
|
+
Principal = {
|
|
2687
|
+
Service = "lambda.amazonaws.com"
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
]
|
|
2691
|
+
})
|
|
2692
|
+
|
|
2693
|
+
tags = var.tags
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
# Attach basic execution policy to Lambda role
|
|
2697
|
+
resource "aws_iam_role_policy_attachment" "lambda_basic_execution" {
|
|
2698
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
|
|
2699
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
# Attach X-Ray tracing policy to Lambda role
|
|
2703
|
+
resource "aws_iam_role_policy_attachment" "lambda_xray_execution" {
|
|
2704
|
+
policy_arn = "arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess"
|
|
2705
|
+
role = aws_iam_role.lambda_execution_role.name
|
|
2706
|
+
}
|
|
2707
|
+
|
|
2708
|
+
# Additional IAM policies for Lambda (if provided)
|
|
2709
|
+
resource "aws_iam_role_policy" "lambda_additional_policies" {
|
|
2710
|
+
count = length(var.additional_iam_policy_statements) > 0 ? 1 : 0
|
|
2711
|
+
name = "CustomApiHandler-additional-policies"
|
|
2712
|
+
role = aws_iam_role.lambda_execution_role.id
|
|
2713
|
+
|
|
2714
|
+
policy = jsonencode({
|
|
2715
|
+
Version = "2012-10-17"
|
|
2716
|
+
Statement = var.additional_iam_policy_statements
|
|
2717
|
+
})
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
# CloudWatch Log Group for Lambda
|
|
2721
|
+
resource "aws_cloudwatch_log_group" "lambda_logs" {
|
|
2722
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
2723
|
+
#checkov:skip=CKV_AWS_338:Log retention set to forever
|
|
2724
|
+
#checkov:skip=CKV_AWS_66:Log retention set to forever
|
|
2725
|
+
name = "/aws/lambda/CustomApiHandler"
|
|
2726
|
+
tags = var.tags
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
|
|
2730
|
+
# Create proxy resource (captures all paths)
|
|
2731
|
+
resource "aws_api_gateway_resource" "proxy_resource" {
|
|
2732
|
+
rest_api_id = module.rest_api.api_id
|
|
2733
|
+
parent_id = module.rest_api.api_root_resource_id
|
|
2734
|
+
path_part = "{proxy+}"
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2737
|
+
# Lambda integration for REST API
|
|
2738
|
+
resource "aws_api_gateway_integration" "lambda_integration" {
|
|
2739
|
+
rest_api_id = module.rest_api.api_id
|
|
2740
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2741
|
+
http_method = aws_api_gateway_method.proxy_method.http_method
|
|
2742
|
+
|
|
2743
|
+
integration_http_method = "POST"
|
|
2744
|
+
type = "AWS_PROXY"
|
|
2745
|
+
uri = aws_lambda_function.api_lambda.invoke_arn
|
|
2746
|
+
|
|
2747
|
+
depends_on = [aws_lambda_function.api_lambda]
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
# Method for proxy integration
|
|
2751
|
+
resource "aws_api_gateway_method" "proxy_method" {
|
|
2752
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for proxy integration as Lambda handles validation
|
|
2753
|
+
rest_api_id = module.rest_api.api_id
|
|
2754
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2755
|
+
http_method = "ANY"
|
|
2756
|
+
|
|
2757
|
+
# Note: you may wish to suppress the checkov rule CKV_AWS_59 if you are absolutely sure you
|
|
2758
|
+
# need a public API without authentication
|
|
2759
|
+
authorization = "NONE"
|
|
2760
|
+
|
|
2761
|
+
request_parameters = {
|
|
2762
|
+
"method.request.path.proxy" = true
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
depends_on = []
|
|
2766
|
+
}
|
|
2767
|
+
|
|
2768
|
+
# OPTIONS method for CORS preflight
|
|
2769
|
+
resource "aws_api_gateway_method" "options_method" {
|
|
2770
|
+
#checkov:skip=CKV2_AWS_70:OPTIONS method must be unauthenticated for CORS preflight requests
|
|
2771
|
+
#checkov:skip=CKV2_AWS_53:Request validation not required for OPTIONS CORS preflight method
|
|
2772
|
+
rest_api_id = module.rest_api.api_id
|
|
2773
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2774
|
+
http_method = "OPTIONS"
|
|
2775
|
+
authorization = "NONE"
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2778
|
+
# CORS integration for OPTIONS method
|
|
2779
|
+
resource "aws_api_gateway_integration" "options_integration" {
|
|
2780
|
+
rest_api_id = module.rest_api.api_id
|
|
2781
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2782
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
2783
|
+
|
|
2784
|
+
type = "MOCK"
|
|
2785
|
+
request_templates = {
|
|
2786
|
+
"application/json" = "{\\"statusCode\\": 204}"
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
|
|
2790
|
+
# OPTIONS method response
|
|
2791
|
+
resource "aws_api_gateway_method_response" "options_response" {
|
|
2792
|
+
rest_api_id = module.rest_api.api_id
|
|
2793
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2794
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
2795
|
+
status_code = "204"
|
|
2796
|
+
|
|
2797
|
+
response_parameters = {
|
|
2798
|
+
"method.response.header.Access-Control-Allow-Headers" = true
|
|
2799
|
+
"method.response.header.Access-Control-Allow-Methods" = true
|
|
2800
|
+
"method.response.header.Access-Control-Allow-Origin" = true
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
# OPTIONS integration response
|
|
2805
|
+
resource "aws_api_gateway_integration_response" "options_integration_response" {
|
|
2806
|
+
rest_api_id = module.rest_api.api_id
|
|
2807
|
+
resource_id = aws_api_gateway_resource.proxy_resource.id
|
|
2808
|
+
http_method = aws_api_gateway_method.options_method.http_method
|
|
2809
|
+
status_code = aws_api_gateway_method_response.options_response.status_code
|
|
2810
|
+
|
|
2811
|
+
response_parameters = {
|
|
2812
|
+
"method.response.header.Access-Control-Allow-Headers" = "'\${join(",", var.cors_allow_headers)}'"
|
|
2813
|
+
"method.response.header.Access-Control-Allow-Methods" = "'\${join(",", var.cors_allow_methods)}'"
|
|
2814
|
+
"method.response.header.Access-Control-Allow-Origin" = "'\${join(",", var.cors_allow_origins)}'"
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
# API Gateway deployment
|
|
2819
|
+
resource "aws_api_gateway_deployment" "api_deployment" {
|
|
2820
|
+
rest_api_id = module.rest_api.api_id
|
|
2821
|
+
|
|
2822
|
+
triggers = {
|
|
2823
|
+
redeployment = sha1(jsonencode([
|
|
2824
|
+
aws_api_gateway_resource.proxy_resource.id,
|
|
2825
|
+
aws_api_gateway_method.proxy_method.id,
|
|
2826
|
+
aws_api_gateway_integration.lambda_integration.id,
|
|
2827
|
+
aws_api_gateway_method.options_method.id,
|
|
2828
|
+
aws_api_gateway_integration.options_integration.id,
|
|
2829
|
+
]))
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
lifecycle {
|
|
2833
|
+
create_before_destroy = true
|
|
2834
|
+
}
|
|
2835
|
+
|
|
2836
|
+
depends_on = [
|
|
2837
|
+
aws_api_gateway_method.proxy_method,
|
|
2838
|
+
aws_api_gateway_integration.lambda_integration,
|
|
2839
|
+
aws_api_gateway_method.options_method,
|
|
2840
|
+
aws_api_gateway_integration.options_integration,
|
|
2841
|
+
aws_api_gateway_method_response.options_response,
|
|
2842
|
+
aws_api_gateway_integration_response.options_integration_response,
|
|
2843
|
+
]
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
# API Gateway stage
|
|
2847
|
+
resource "aws_api_gateway_stage" "api_stage" {
|
|
2848
|
+
#checkov:skip=CKV_AWS_120:API Gateway caching not required for this use case
|
|
2849
|
+
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
2850
|
+
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
2851
|
+
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
2852
|
+
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
2853
|
+
rest_api_id = module.rest_api.api_id
|
|
2854
|
+
stage_name = "prod"
|
|
2855
|
+
xray_tracing_enabled = true
|
|
2856
|
+
|
|
2857
|
+
tags = var.tags
|
|
2858
|
+
|
|
2859
|
+
depends_on = [aws_api_gateway_deployment.api_deployment]
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
# API Gateway Resource Policy
|
|
2863
|
+
resource "aws_api_gateway_rest_api_policy" "api_policy" {
|
|
2864
|
+
rest_api_id = module.rest_api.api_id
|
|
2865
|
+
|
|
2866
|
+
policy = jsonencode({
|
|
2867
|
+
Version = "2012-10-17"
|
|
2868
|
+
Statement = [
|
|
2869
|
+
{
|
|
2870
|
+
# Allow all callers to invoke the API in the resource policy
|
|
2871
|
+
Effect = "Allow"
|
|
2872
|
+
Principal = "*"
|
|
2873
|
+
Action = "execute-api:Invoke"
|
|
2874
|
+
Resource = "execute-api:/*"
|
|
2875
|
+
}
|
|
2876
|
+
]
|
|
2877
|
+
})
|
|
2878
|
+
}
|
|
2879
|
+
|
|
2880
|
+
# Lambda permission for API Gateway to invoke the function
|
|
2881
|
+
resource "aws_lambda_permission" "api_gateway_invoke" {
|
|
2882
|
+
statement_id = "AllowExecutionFromAPIGateway"
|
|
2883
|
+
action = "lambda:InvokeFunction"
|
|
2884
|
+
function_name = aws_lambda_function.api_lambda.function_name
|
|
2885
|
+
principal = "apigateway.amazonaws.com"
|
|
2886
|
+
source_arn = "\${module.rest_api.api_execution_arn}/*/*"
|
|
2887
|
+
|
|
2888
|
+
depends_on = [module.rest_api, aws_lambda_function.api_lambda]
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2891
|
+
# Add API url to runtime config
|
|
2892
|
+
module "add_url_to_runtime_config" {
|
|
2893
|
+
source = "../../../core/runtime-config/entry"
|
|
2894
|
+
|
|
2895
|
+
key_path = "apis.CustomApi"
|
|
2896
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
2897
|
+
|
|
2898
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
# Outputs
|
|
2902
|
+
|
|
2903
|
+
# API Gateway Outputs (from core module)
|
|
2904
|
+
output "api_id" {
|
|
2905
|
+
description = "ID of the REST API Gateway"
|
|
2906
|
+
value = module.rest_api.api_id
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
output "api_arn" {
|
|
2910
|
+
description = "ARN of the REST API Gateway"
|
|
2911
|
+
value = module.rest_api.api_arn
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
output "api_endpoint" {
|
|
2915
|
+
description = "Base URL of the REST API Gateway"
|
|
2916
|
+
value = module.rest_api.api_endpoint
|
|
2917
|
+
}
|
|
2918
|
+
|
|
2919
|
+
output "api_execution_arn" {
|
|
2920
|
+
description = "Execution ARN of the REST API Gateway"
|
|
2921
|
+
value = module.rest_api.api_execution_arn
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2924
|
+
output "stage_invoke_url" {
|
|
2925
|
+
description = "Invoke URL of the API Gateway stage"
|
|
2926
|
+
value = aws_api_gateway_stage.api_stage.invoke_url
|
|
2927
|
+
}
|
|
2928
|
+
|
|
2929
|
+
output "stage_arn" {
|
|
2930
|
+
description = "ARN of the API Gateway stage"
|
|
2931
|
+
value = aws_api_gateway_stage.api_stage.arn
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
output "stage_execution_arn" {
|
|
2935
|
+
description = "Execution ARN of the API Gateway stage"
|
|
2936
|
+
value = aws_api_gateway_stage.api_stage.execution_arn
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
output "deployment_id" {
|
|
2940
|
+
description = "ID of the API Gateway deployment"
|
|
2941
|
+
value = aws_api_gateway_deployment.api_deployment.id
|
|
2942
|
+
}
|
|
2943
|
+
|
|
2944
|
+
output "stage_id" {
|
|
2945
|
+
description = "ID of the API Gateway stage"
|
|
2946
|
+
value = aws_api_gateway_stage.api_stage.id
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
# Lambda Function Outputs
|
|
2950
|
+
output "lambda_function_name" {
|
|
2951
|
+
description = "Name of the Lambda function"
|
|
2952
|
+
value = aws_lambda_function.api_lambda.function_name
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
output "lambda_function_arn" {
|
|
2956
|
+
description = "ARN of the Lambda function"
|
|
2957
|
+
value = aws_lambda_function.api_lambda.arn
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
output "lambda_invoke_arn" {
|
|
2961
|
+
description = "Invoke ARN of the Lambda function"
|
|
2962
|
+
value = aws_lambda_function.api_lambda.invoke_arn
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
output "lambda_qualified_arn" {
|
|
2966
|
+
description = "Qualified ARN of the Lambda function"
|
|
2967
|
+
value = aws_lambda_function.api_lambda.qualified_arn
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
output "lambda_version" {
|
|
2971
|
+
description = "Version of the Lambda function"
|
|
2972
|
+
value = aws_lambda_function.api_lambda.version
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
output "lambda_source_code_hash" {
|
|
2976
|
+
description = "Base64-encoded SHA256 hash of the Lambda deployment package"
|
|
2977
|
+
value = aws_lambda_function.api_lambda.source_code_hash
|
|
2978
|
+
}
|
|
2979
|
+
|
|
2980
|
+
output "lambda_source_code_size" {
|
|
2981
|
+
description = "Size of the Lambda deployment package in bytes"
|
|
2982
|
+
value = aws_lambda_function.api_lambda.source_code_size
|
|
2983
|
+
}
|
|
2984
|
+
|
|
2985
|
+
# IAM Role Outputs
|
|
2986
|
+
output "lambda_execution_role_arn" {
|
|
2987
|
+
description = "ARN of the Lambda execution role"
|
|
2988
|
+
value = aws_iam_role.lambda_execution_role.arn
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
output "lambda_execution_role_name" {
|
|
2992
|
+
description = "Name of the Lambda execution role"
|
|
2993
|
+
value = aws_iam_role.lambda_execution_role.name
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2996
|
+
# Integration Outputs
|
|
2997
|
+
output "integration_id" {
|
|
2998
|
+
description = "ID of the Lambda integration"
|
|
2999
|
+
value = aws_api_gateway_integration.lambda_integration.id
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
output "proxy_resource_id" {
|
|
3003
|
+
description = "ID of the proxy resource"
|
|
3004
|
+
value = aws_api_gateway_resource.proxy_resource.id
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
output "proxy_method_id" {
|
|
3008
|
+
description = "ID of the proxy method"
|
|
3009
|
+
value = aws_api_gateway_method.proxy_method.id
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
# CloudWatch Log Groups
|
|
3013
|
+
output "lambda_log_group_name" {
|
|
3014
|
+
description = "Name of the Lambda CloudWatch log group"
|
|
3015
|
+
value = aws_cloudwatch_log_group.lambda_logs.name
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
output "lambda_log_group_arn" {
|
|
3019
|
+
description = "ARN of the Lambda CloudWatch log group"
|
|
3020
|
+
value = aws_cloudwatch_log_group.lambda_logs.arn
|
|
3021
|
+
}
|
|
3022
|
+
"
|
|
3023
|
+
`;
|