create-aws-project 1.2.1

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.
Files changed (181) hide show
  1. package/README.md +118 -0
  2. package/dist/__tests__/generator/replace-tokens.spec.d.ts +1 -0
  3. package/dist/__tests__/generator/replace-tokens.spec.js +281 -0
  4. package/dist/__tests__/generator.spec.d.ts +1 -0
  5. package/dist/__tests__/generator.spec.js +162 -0
  6. package/dist/__tests__/validation/project-name.spec.d.ts +1 -0
  7. package/dist/__tests__/validation/project-name.spec.js +57 -0
  8. package/dist/__tests__/wizard.spec.d.ts +1 -0
  9. package/dist/__tests__/wizard.spec.js +232 -0
  10. package/dist/aws/iam.d.ts +75 -0
  11. package/dist/aws/iam.js +264 -0
  12. package/dist/aws/organizations.d.ts +79 -0
  13. package/dist/aws/organizations.js +168 -0
  14. package/dist/cli.d.ts +4 -0
  15. package/dist/cli.js +206 -0
  16. package/dist/commands/setup-github.d.ts +4 -0
  17. package/dist/commands/setup-github.js +185 -0
  18. package/dist/generator/copy-file.d.ts +15 -0
  19. package/dist/generator/copy-file.js +56 -0
  20. package/dist/generator/generate-project.d.ts +14 -0
  21. package/dist/generator/generate-project.js +81 -0
  22. package/dist/generator/index.d.ts +4 -0
  23. package/dist/generator/index.js +3 -0
  24. package/dist/generator/replace-tokens.d.ts +29 -0
  25. package/dist/generator/replace-tokens.js +68 -0
  26. package/dist/github/secrets.d.ts +109 -0
  27. package/dist/github/secrets.js +275 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.js +6 -0
  30. package/dist/prompts/auth.d.ts +3 -0
  31. package/dist/prompts/auth.js +23 -0
  32. package/dist/prompts/aws-config.d.ts +2 -0
  33. package/dist/prompts/aws-config.js +14 -0
  34. package/dist/prompts/features.d.ts +2 -0
  35. package/dist/prompts/features.js +10 -0
  36. package/dist/prompts/github-setup.d.ts +53 -0
  37. package/dist/prompts/github-setup.js +208 -0
  38. package/dist/prompts/org-structure.d.ts +9 -0
  39. package/dist/prompts/org-structure.js +93 -0
  40. package/dist/prompts/platforms.d.ts +2 -0
  41. package/dist/prompts/platforms.js +12 -0
  42. package/dist/prompts/project-name.d.ts +2 -0
  43. package/dist/prompts/project-name.js +8 -0
  44. package/dist/prompts/theme.d.ts +2 -0
  45. package/dist/prompts/theme.js +14 -0
  46. package/dist/templates/index.d.ts +4 -0
  47. package/dist/templates/index.js +2 -0
  48. package/dist/templates/manifest.d.ts +11 -0
  49. package/dist/templates/manifest.js +99 -0
  50. package/dist/templates/tokens.d.ts +39 -0
  51. package/dist/templates/tokens.js +37 -0
  52. package/dist/templates/types.d.ts +52 -0
  53. package/dist/templates/types.js +1 -0
  54. package/dist/types.d.ts +27 -0
  55. package/dist/types.js +1 -0
  56. package/dist/validation/project-name.d.ts +1 -0
  57. package/dist/validation/project-name.js +12 -0
  58. package/dist/wizard.d.ts +2 -0
  59. package/dist/wizard.js +81 -0
  60. package/package.json +68 -0
  61. package/templates/.github/actions/build-and-test/action.yml +24 -0
  62. package/templates/.github/actions/deploy-cdk/action.yml +46 -0
  63. package/templates/.github/actions/deploy-web/action.yml +72 -0
  64. package/templates/.github/actions/setup/action.yml +29 -0
  65. package/templates/.github/pull_request_template.md +15 -0
  66. package/templates/.github/workflows/deploy-dev.yml +80 -0
  67. package/templates/.github/workflows/deploy-prod.yml +67 -0
  68. package/templates/.github/workflows/deploy-stage.yml +77 -0
  69. package/templates/.github/workflows/pull-request.yml +72 -0
  70. package/templates/.vscode/extensions.json +7 -0
  71. package/templates/.vscode/settings.json +67 -0
  72. package/templates/apps/api/.eslintrc.json +18 -0
  73. package/templates/apps/api/cdk/app.ts +93 -0
  74. package/templates/apps/api/cdk/auth/cognito-stack.ts +164 -0
  75. package/templates/apps/api/cdk/cdk.json +73 -0
  76. package/templates/apps/api/cdk/deployment-user-stack.ts +187 -0
  77. package/templates/apps/api/cdk/org-stack.ts +67 -0
  78. package/templates/apps/api/cdk/static-stack.ts +361 -0
  79. package/templates/apps/api/cdk/tsconfig.json +39 -0
  80. package/templates/apps/api/cdk/user-stack.ts +255 -0
  81. package/templates/apps/api/jest.config.ts +38 -0
  82. package/templates/apps/api/lambdas.yml +84 -0
  83. package/templates/apps/api/project.json.template +58 -0
  84. package/templates/apps/api/src/__tests__/setup.ts +10 -0
  85. package/templates/apps/api/src/handlers/users/create-user.ts +52 -0
  86. package/templates/apps/api/src/handlers/users/delete-user.ts +45 -0
  87. package/templates/apps/api/src/handlers/users/get-me.ts +72 -0
  88. package/templates/apps/api/src/handlers/users/get-user.ts +45 -0
  89. package/templates/apps/api/src/handlers/users/get-users.ts +23 -0
  90. package/templates/apps/api/src/handlers/users/index.ts +17 -0
  91. package/templates/apps/api/src/handlers/users/update-user.ts +72 -0
  92. package/templates/apps/api/src/lib/dynamo/dynamo-model.ts +504 -0
  93. package/templates/apps/api/src/lib/dynamo/index.ts +12 -0
  94. package/templates/apps/api/src/lib/dynamo/utils.ts +39 -0
  95. package/templates/apps/api/src/middleware/auth0-auth.ts +97 -0
  96. package/templates/apps/api/src/middleware/cognito-auth.ts +90 -0
  97. package/templates/apps/api/src/models/UserModel.ts +109 -0
  98. package/templates/apps/api/src/schemas/user.schema.ts +44 -0
  99. package/templates/apps/api/src/services/user-service.ts +108 -0
  100. package/templates/apps/api/src/utils/auth-context.ts +60 -0
  101. package/templates/apps/api/src/utils/common/helpers.ts +26 -0
  102. package/templates/apps/api/src/utils/lambda-handler.ts +148 -0
  103. package/templates/apps/api/src/utils/response.ts +52 -0
  104. package/templates/apps/api/src/utils/validator.ts +75 -0
  105. package/templates/apps/api/tsconfig.app.json +15 -0
  106. package/templates/apps/api/tsconfig.json +19 -0
  107. package/templates/apps/api/tsconfig.spec.json +17 -0
  108. package/templates/apps/mobile/.env.example +5 -0
  109. package/templates/apps/mobile/.eslintrc.json +33 -0
  110. package/templates/apps/mobile/app.json +33 -0
  111. package/templates/apps/mobile/assets/.gitkeep +0 -0
  112. package/templates/apps/mobile/babel.config.js +19 -0
  113. package/templates/apps/mobile/index.js +7 -0
  114. package/templates/apps/mobile/jest.config.ts +22 -0
  115. package/templates/apps/mobile/metro.config.js +35 -0
  116. package/templates/apps/mobile/package.json +22 -0
  117. package/templates/apps/mobile/project.json.template +64 -0
  118. package/templates/apps/mobile/src/App.tsx +367 -0
  119. package/templates/apps/mobile/src/__tests__/App.spec.tsx +46 -0
  120. package/templates/apps/mobile/src/__tests__/store/user-store.spec.ts +156 -0
  121. package/templates/apps/mobile/src/config/api.ts +16 -0
  122. package/templates/apps/mobile/src/store/user-store.ts +56 -0
  123. package/templates/apps/mobile/src/test-setup.ts +10 -0
  124. package/templates/apps/mobile/tsconfig.json +22 -0
  125. package/templates/apps/web/.env.example +13 -0
  126. package/templates/apps/web/.eslintrc.json +26 -0
  127. package/templates/apps/web/index.html +13 -0
  128. package/templates/apps/web/jest.config.ts +24 -0
  129. package/templates/apps/web/package.json +15 -0
  130. package/templates/apps/web/project.json.template +66 -0
  131. package/templates/apps/web/src/App.tsx +352 -0
  132. package/templates/apps/web/src/__mocks__/config/api.ts +41 -0
  133. package/templates/apps/web/src/__tests__/App.spec.tsx +240 -0
  134. package/templates/apps/web/src/__tests__/store/user-store.spec.ts +185 -0
  135. package/templates/apps/web/src/auth/auth0-provider.tsx +103 -0
  136. package/templates/apps/web/src/auth/cognito-provider.tsx +143 -0
  137. package/templates/apps/web/src/auth/index.ts +7 -0
  138. package/templates/apps/web/src/auth/use-auth.ts +16 -0
  139. package/templates/apps/web/src/config/amplify-config.ts +31 -0
  140. package/templates/apps/web/src/config/api.ts +38 -0
  141. package/templates/apps/web/src/config/auth0-config.ts +17 -0
  142. package/templates/apps/web/src/main.tsx +41 -0
  143. package/templates/apps/web/src/store/user-store.ts +56 -0
  144. package/templates/apps/web/src/styles.css +165 -0
  145. package/templates/apps/web/src/test-setup.ts +1 -0
  146. package/templates/apps/web/src/theme/index.ts +30 -0
  147. package/templates/apps/web/src/vite-env.d.ts +19 -0
  148. package/templates/apps/web/tsconfig.app.json +24 -0
  149. package/templates/apps/web/tsconfig.json +22 -0
  150. package/templates/apps/web/tsconfig.spec.json +28 -0
  151. package/templates/apps/web/vite.config.ts +87 -0
  152. package/templates/manifest.json +28 -0
  153. package/templates/packages/api-client/.eslintrc.json +18 -0
  154. package/templates/packages/api-client/jest.config.ts +13 -0
  155. package/templates/packages/api-client/package.json +8 -0
  156. package/templates/packages/api-client/project.json.template +34 -0
  157. package/templates/packages/api-client/src/__tests__/api-client.spec.ts +408 -0
  158. package/templates/packages/api-client/src/api-client.ts +201 -0
  159. package/templates/packages/api-client/src/config.ts +193 -0
  160. package/templates/packages/api-client/src/index.ts +9 -0
  161. package/templates/packages/api-client/tsconfig.json +22 -0
  162. package/templates/packages/api-client/tsconfig.lib.json +11 -0
  163. package/templates/packages/api-client/tsconfig.spec.json +14 -0
  164. package/templates/packages/common-types/.eslintrc.json +18 -0
  165. package/templates/packages/common-types/package.json +6 -0
  166. package/templates/packages/common-types/project.json.template +26 -0
  167. package/templates/packages/common-types/src/api.types.ts +24 -0
  168. package/templates/packages/common-types/src/auth.types.ts +36 -0
  169. package/templates/packages/common-types/src/common.types.ts +46 -0
  170. package/templates/packages/common-types/src/index.ts +19 -0
  171. package/templates/packages/common-types/src/lambda.types.ts +39 -0
  172. package/templates/packages/common-types/src/user.types.ts +31 -0
  173. package/templates/packages/common-types/tsconfig.json +19 -0
  174. package/templates/packages/common-types/tsconfig.lib.json +11 -0
  175. package/templates/root/.editorconfig +23 -0
  176. package/templates/root/.nvmrc +1 -0
  177. package/templates/root/eslint.config.js +61 -0
  178. package/templates/root/jest.preset.js +16 -0
  179. package/templates/root/nx.json +29 -0
  180. package/templates/root/package.json +131 -0
  181. package/templates/root/tsconfig.base.json +29 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "recommendations": [
3
+ "dbaeumer.vscode-eslint",
4
+ "esbenp.prettier-vscode",
5
+ "nrwl.angular-console"
6
+ ]
7
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "typescript.tsdk": "node_modules/typescript/lib",
3
+ "typescript.enablePromptUseWorkspaceTsdk": true,
4
+ "editor.formatOnSave": true,
5
+ "editor.codeActionsOnSave": {
6
+ "source.fixAll": "explicit"
7
+ },
8
+ "editor.tabSize": 2,
9
+ "editor.insertSpaces": true,
10
+ "editor.detectIndentation": false,
11
+ "[typescript]": {
12
+ "editor.tabSize": 2,
13
+ "editor.insertSpaces": true
14
+ },
15
+ "[typescriptreact]": {
16
+ "editor.tabSize": 2,
17
+ "editor.insertSpaces": true
18
+ },
19
+ "[javascript]": {
20
+ "editor.tabSize": 2,
21
+ "editor.insertSpaces": true
22
+ },
23
+ "[javascriptreact]": {
24
+ "editor.tabSize": 2,
25
+ "editor.insertSpaces": true
26
+ },
27
+ "[json]": {
28
+ "editor.tabSize": 2,
29
+ "editor.insertSpaces": true
30
+ },
31
+ "[jsonc]": {
32
+ "editor.tabSize": 2,
33
+ "editor.insertSpaces": true
34
+ },
35
+ "[html]": {
36
+ "editor.tabSize": 2,
37
+ "editor.insertSpaces": true
38
+ },
39
+ "[css]": {
40
+ "editor.tabSize": 2,
41
+ "editor.insertSpaces": true
42
+ },
43
+ "[scss]": {
44
+ "editor.tabSize": 2,
45
+ "editor.insertSpaces": true
46
+ },
47
+ "[yaml]": {
48
+ "editor.tabSize": 2,
49
+ "editor.insertSpaces": true
50
+ },
51
+ "[markdown]": {
52
+ "editor.tabSize": 2,
53
+ "editor.insertSpaces": true
54
+ },
55
+ "files.exclude": {
56
+ "**/.git": true,
57
+ "**/.DS_Store": true,
58
+ "**/node_modules": true,
59
+ "**/dist": true,
60
+ "**/.nx": true
61
+ },
62
+ "search.exclude": {
63
+ "**/node_modules": true,
64
+ "**/dist": true,
65
+ "**/.nx": true
66
+ }
67
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": ["../../.eslintrc.js"],
3
+ "ignorePatterns": ["!**/*"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
+ "rules": {}
8
+ },
9
+ {
10
+ "files": ["*.ts", "*.tsx"],
11
+ "rules": {}
12
+ },
13
+ {
14
+ "files": ["*.js", "*.jsx"],
15
+ "rules": {}
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ import 'source-map-support/register';
3
+ import * as cdk from 'aws-cdk-lib';
4
+ import { StaticStack } from './static-stack';
5
+ import { UserStack } from './user-stack';
6
+ import { DeploymentUserStack } from './deployment-user-stack';
7
+ // {{#if AUTH_COGNITO}}
8
+ import { CognitoStack } from './auth/cognito-stack';
9
+ // {{/if AUTH_COGNITO}}
10
+
11
+ const appName = '{{PROJECT_NAME_PASCAL}}';
12
+
13
+ // {{#if ORG_ENABLED}}
14
+ // Multi-account configuration from AWS Organizations
15
+ const accountIds: Record<string, string> = {
16
+ dev: '{{DEV_ACCOUNT_ID}}',
17
+ stage: '{{STAGE_ACCOUNT_ID}}',
18
+ prod: '{{PROD_ACCOUNT_ID}}',
19
+ };
20
+ // {{/if ORG_ENABLED}}
21
+
22
+ /**
23
+ * AWS CDK App for {{PROJECT_NAME_TITLE}}
24
+ *
25
+ * This app creates the infrastructure for the project including:
26
+ * - S3 bucket for static web content
27
+ * - API Gateway for Lambda functions
28
+ * - CloudFront distribution with both S3 and API Gateway origins
29
+ * - Lambda functions with API Gateway integrations (from lambdas.yml)
30
+ * - GitHub deployment IAM users (optional, via --context deployUser=true)
31
+ *
32
+ * Deploy commands:
33
+ * npx cdk deploy --all # Deploy all stacks
34
+ * npx cdk deploy {{PROJECT_NAME_PASCAL}}-DeployUser-dev # Deploy just the deploy user
35
+ */
36
+ const app = new cdk.App();
37
+
38
+ // Get environment from context or default to 'dev'
39
+ const environmentName = app.node.tryGetContext('environment') || 'dev';
40
+
41
+ // Get AWS account and region from environment or use defaults
42
+ const env = {
43
+ // {{#if ORG_ENABLED}}
44
+ account: accountIds[environmentName] || process.env.CDK_DEFAULT_ACCOUNT,
45
+ // {{/if ORG_ENABLED}}
46
+ // {{#unless ORG_ENABLED}}
47
+ account: process.env.CDK_DEFAULT_ACCOUNT || process.env.AWS_ACCOUNT_ID,
48
+ // {{/unless ORG_ENABLED}}
49
+ region: process.env.CDK_DEFAULT_REGION || process.env.AWS_REGION || '{{AWS_REGION}}',
50
+ };
51
+
52
+ // Common tags for all resources
53
+ const tags = {
54
+ Project: '{{PROJECT_NAME_TITLE}}',
55
+ Environment: environmentName,
56
+ ManagedBy: 'CDK',
57
+ };
58
+
59
+ // Create the static stack (CloudFront, S3, API Gateway)
60
+ const staticStack = new StaticStack(app, `${appName}-Static-${environmentName}`, {
61
+ env,
62
+ environmentName,
63
+ description: `{{PROJECT_NAME_TITLE}} static infrastructure for ${environmentName} environment`,
64
+ tags,
65
+ });
66
+
67
+ // Create the user stack (Lambda functions from lambdas.yml)
68
+ // Note: UserStack automatically depends on StaticStack because it uses staticStack.api
69
+ new UserStack(app, `${appName}-Users-${environmentName}`, {
70
+ env,
71
+ environmentName,
72
+ api: staticStack.api,
73
+ description: `{{PROJECT_NAME_TITLE}} user Lambda functions for ${environmentName} environment`,
74
+ tags,
75
+ });
76
+
77
+ // Create the deployment user stack (GitHub Actions IAM user)
78
+ new DeploymentUserStack(app, `${appName}-DeployUser-${environmentName}`, {
79
+ env,
80
+ environmentName,
81
+ description: `GitHub Actions deployment user for ${environmentName} environment`,
82
+ tags,
83
+ });
84
+
85
+ // {{#if AUTH_COGNITO}}
86
+ // Create the Cognito authentication stack
87
+ new CognitoStack(app, `${appName}-Cognito-${environmentName}`, {
88
+ stage: environmentName,
89
+ env,
90
+ description: `{{PROJECT_NAME_TITLE}} Cognito authentication for ${environmentName} environment`,
91
+ tags,
92
+ });
93
+ // {{/if AUTH_COGNITO}}
@@ -0,0 +1,164 @@
1
+ import * as cdk from 'aws-cdk-lib';
2
+ import * as cognito from 'aws-cdk-lib/aws-cognito';
3
+ import * as iam from 'aws-cdk-lib/aws-iam';
4
+ import { Construct } from 'constructs';
5
+
6
+ export interface CognitoStackProps extends cdk.StackProps {
7
+ stage: string;
8
+ }
9
+
10
+ /**
11
+ * Cognito Stack for {{PROJECT_NAME_TITLE}}
12
+ *
13
+ * Creates Cognito User Pool infrastructure for authentication including:
14
+ * - User Pool with email sign-in and self-registration
15
+ * - User Pool Client for SPA/mobile applications
16
+ * - Optional Identity Pool for federated identities (when social login enabled)
17
+ * - Optional MFA configuration
18
+ */
19
+ export class CognitoStack extends cdk.Stack {
20
+ public readonly userPool: cognito.UserPool;
21
+ public readonly userPoolClient: cognito.UserPoolClient;
22
+ public readonly identityPool?: cognito.CfnIdentityPool;
23
+
24
+ constructor(scope: Construct, id: string, props: CognitoStackProps) {
25
+ super(scope, id, props);
26
+
27
+ const { stage } = props;
28
+
29
+ // Create User Pool
30
+ this.userPool = new cognito.UserPool(this, 'UserPool', {
31
+ userPoolName: `{{PROJECT_NAME}}-${stage}-user-pool`,
32
+ signInCaseSensitive: false,
33
+ selfSignUpEnabled: true,
34
+ signInAliases: {
35
+ email: true,
36
+ },
37
+ autoVerify: {
38
+ email: true,
39
+ },
40
+ standardAttributes: {
41
+ email: {
42
+ required: true,
43
+ mutable: true,
44
+ },
45
+ },
46
+ passwordPolicy: {
47
+ minLength: 8,
48
+ requireLowercase: true,
49
+ requireUppercase: true,
50
+ requireDigits: true,
51
+ requireSymbols: false,
52
+ },
53
+ accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
54
+ removalPolicy: stage === 'prod' ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
55
+ // {{#if AUTH_MFA}}
56
+ mfa: cognito.Mfa.OPTIONAL,
57
+ mfaSecondFactor: {
58
+ sms: false,
59
+ otp: true,
60
+ },
61
+ // {{/if AUTH_MFA}}
62
+ });
63
+
64
+ // Create User Pool Client (for SPA/mobile)
65
+ this.userPoolClient = new cognito.UserPoolClient(this, 'UserPoolClient', {
66
+ userPool: this.userPool,
67
+ userPoolClientName: `{{PROJECT_NAME}}-${stage}-client`,
68
+ authFlows: {
69
+ userPassword: true,
70
+ userSrp: true,
71
+ },
72
+ generateSecret: false,
73
+ preventUserExistenceErrors: true,
74
+ accessTokenValidity: cdk.Duration.hours(1),
75
+ idTokenValidity: cdk.Duration.hours(1),
76
+ refreshTokenValidity: cdk.Duration.days(30),
77
+ });
78
+
79
+ // {{#if AUTH_SOCIAL_LOGIN}}
80
+ // Create Identity Pool for federated identities
81
+ this.identityPool = new cognito.CfnIdentityPool(this, 'IdentityPool', {
82
+ identityPoolName: `{{PROJECT_NAME}}_${stage}_identity_pool`,
83
+ allowUnauthenticatedIdentities: false,
84
+ cognitoIdentityProviders: [
85
+ {
86
+ clientId: this.userPoolClient.userPoolClientId,
87
+ providerName: this.userPool.userPoolProviderName,
88
+ },
89
+ ],
90
+ });
91
+
92
+ // Create authenticated role for Identity Pool
93
+ const authenticatedRole = new iam.Role(this, 'AuthenticatedRole', {
94
+ assumedBy: new iam.FederatedPrincipal(
95
+ 'cognito-identity.amazonaws.com',
96
+ {
97
+ StringEquals: {
98
+ 'cognito-identity.amazonaws.com:aud': this.identityPool.ref,
99
+ },
100
+ 'ForAnyValue:StringLike': {
101
+ 'cognito-identity.amazonaws.com:amr': 'authenticated',
102
+ },
103
+ },
104
+ 'sts:AssumeRoleWithWebIdentity'
105
+ ),
106
+ description: 'Role for authenticated users',
107
+ });
108
+
109
+ // Create unauthenticated role for Identity Pool
110
+ const unauthenticatedRole = new iam.Role(this, 'UnauthenticatedRole', {
111
+ assumedBy: new iam.FederatedPrincipal(
112
+ 'cognito-identity.amazonaws.com',
113
+ {
114
+ StringEquals: {
115
+ 'cognito-identity.amazonaws.com:aud': this.identityPool.ref,
116
+ },
117
+ 'ForAnyValue:StringLike': {
118
+ 'cognito-identity.amazonaws.com:amr': 'unauthenticated',
119
+ },
120
+ },
121
+ 'sts:AssumeRoleWithWebIdentity'
122
+ ),
123
+ description: 'Role for unauthenticated users',
124
+ });
125
+
126
+ // Attach roles to Identity Pool
127
+ new cognito.CfnIdentityPoolRoleAttachment(this, 'IdentityPoolRoles', {
128
+ identityPoolId: this.identityPool.ref,
129
+ roles: {
130
+ authenticated: authenticatedRole.roleArn,
131
+ unauthenticated: unauthenticatedRole.roleArn,
132
+ },
133
+ });
134
+
135
+ // Output Identity Pool ID
136
+ new cdk.CfnOutput(this, 'IdentityPoolId', {
137
+ value: this.identityPool.ref,
138
+ description: 'Cognito Identity Pool ID',
139
+ exportName: `${stage}-identity-pool-id`,
140
+ });
141
+ // {{/if AUTH_SOCIAL_LOGIN}}
142
+
143
+ // Output User Pool ID
144
+ new cdk.CfnOutput(this, 'UserPoolId', {
145
+ value: this.userPool.userPoolId,
146
+ description: 'Cognito User Pool ID',
147
+ exportName: `${stage}-user-pool-id`,
148
+ });
149
+
150
+ // Output User Pool Client ID
151
+ new cdk.CfnOutput(this, 'UserPoolClientId', {
152
+ value: this.userPoolClient.userPoolClientId,
153
+ description: 'Cognito User Pool Client ID',
154
+ exportName: `${stage}-user-pool-client-id`,
155
+ });
156
+
157
+ // Output Cognito Region
158
+ new cdk.CfnOutput(this, 'CognitoRegion', {
159
+ value: this.region,
160
+ description: 'AWS Region for Cognito',
161
+ exportName: `${stage}-cognito-region`,
162
+ });
163
+ }
164
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "app": "npx ts-node --prefer-ts-exts app.ts",
3
+ "watch": {
4
+ "include": [
5
+ "**"
6
+ ],
7
+ "exclude": [
8
+ "README.md",
9
+ "cdk*.json",
10
+ "**/*.d.ts",
11
+ "**/*.js",
12
+ "tsconfig.json",
13
+ "package*.json",
14
+ "yarn.lock",
15
+ "node_modules",
16
+ "test"
17
+ ]
18
+ },
19
+ "context": {
20
+ "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
21
+ "@aws-cdk/core:checkSecretUsage": true,
22
+ "@aws-cdk/core:target-partitions": [
23
+ "aws",
24
+ "aws-cn"
25
+ ],
26
+ "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
27
+ "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
28
+ "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
29
+ "@aws-cdk/aws-iam:minimizePolicies": true,
30
+ "@aws-cdk/core:validateSnapshotRemovalPolicy": true,
31
+ "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
32
+ "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
33
+ "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
34
+ "@aws-cdk/aws-apigateway:disableCloudWatchRole": false,
35
+ "@aws-cdk/core:enablePartitionLiterals": true,
36
+ "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
37
+ "@aws-cdk/aws-iam:standardizedServicePrincipals": true,
38
+ "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
39
+ "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
40
+ "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
41
+ "@aws-cdk/aws-route53-patters:useCertificate": true,
42
+ "@aws-cdk/customresources:installLatestAwsSdkDefault": false,
43
+ "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
44
+ "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
45
+ "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
46
+ "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
47
+ "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
48
+ "@aws-cdk/aws-redshift:columnId": true,
49
+ "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
50
+ "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
51
+ "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
52
+ "@aws-cdk/aws-kms:aliasNameRef": true,
53
+ "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
54
+ "@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
55
+ "@aws-cdk/aws-efs:denyAnonymousAccess": true,
56
+ "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
57
+ "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
58
+ "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
59
+ "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
60
+ "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
61
+ "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
62
+ "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
63
+ "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
64
+ "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
65
+ "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
66
+ "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
67
+ "@aws-cdk/aws-eks:nodegroupNameAttribute": true,
68
+ "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
69
+ "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
70
+ "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
71
+ "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false
72
+ }
73
+ }
@@ -0,0 +1,187 @@
1
+ import { Stack, StackProps, CfnOutput, SecretValue } from 'aws-cdk-lib';
2
+ import * as iam from 'aws-cdk-lib/aws-iam';
3
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
4
+ import { Construct } from 'constructs';
5
+
6
+ export interface DeploymentUserStackProps extends StackProps {
7
+ environmentName: string;
8
+ }
9
+
10
+ /**
11
+ * Stack that creates IAM users for GitHub Actions deployments.
12
+ *
13
+ * This stack should be deployed to each target account (dev, stage, prod)
14
+ * to create the necessary IAM user with permissions for CDK deployments.
15
+ *
16
+ * The access keys are stored in AWS Secrets Manager for secure retrieval.
17
+ *
18
+ * This uses least-privilege permissions based on what CDK actually needs.
19
+ */
20
+ export class DeploymentUserStack extends Stack {
21
+ constructor(scope: Construct, id: string, props: DeploymentUserStackProps) {
22
+ super(scope, id, props);
23
+
24
+ const { environmentName } = props;
25
+ const accountId = this.account;
26
+ const region = this.region;
27
+
28
+ // Create IAM user for GitHub Actions deployments
29
+ const deployUser = new iam.User(this, 'GitHubDeployUser', {
30
+ userName: `github-deploy-${environmentName}`,
31
+ });
32
+
33
+ // Policy for CDK deployments - least privilege permissions
34
+ const cdkDeployPolicy = new iam.ManagedPolicy(this, 'CdkDeployPolicy', {
35
+ managedPolicyName: `github-deploy-policy-${environmentName}`,
36
+ description: `Policy for GitHub Actions CDK deployments in ${environmentName}`,
37
+ statements: [
38
+ // CloudFormation - specific actions only
39
+ new iam.PolicyStatement({
40
+ sid: 'CloudFormationPermissions',
41
+ effect: iam.Effect.ALLOW,
42
+ actions: [
43
+ 'cloudformation:CreateChangeSet',
44
+ 'cloudformation:DeleteChangeSet',
45
+ 'cloudformation:DescribeChangeSet',
46
+ 'cloudformation:DescribeStacks',
47
+ 'cloudformation:ExecuteChangeSet',
48
+ 'cloudformation:CreateStack',
49
+ 'cloudformation:UpdateStack',
50
+ 'cloudformation:RollbackStack',
51
+ 'cloudformation:ContinueUpdateRollback',
52
+ 'cloudformation:DescribeStackEvents',
53
+ 'cloudformation:GetTemplate',
54
+ 'cloudformation:DeleteStack',
55
+ 'cloudformation:UpdateTerminationProtection',
56
+ 'cloudformation:GetTemplateSummary',
57
+ 'cloudformation:CreateStackRefactor',
58
+ 'cloudformation:DescribeStackRefactor',
59
+ 'cloudformation:ExecuteStackRefactor',
60
+ 'cloudformation:ListStackRefactorActions',
61
+ 'cloudformation:ListStackRefactors',
62
+ 'cloudformation:ListStacks',
63
+ ],
64
+ resources: ['*'],
65
+ }),
66
+ // CloudFront - limited to listing and invalidation
67
+ new iam.PolicyStatement({
68
+ sid: 'CloudFrontPermissions',
69
+ effect: iam.Effect.ALLOW,
70
+ actions: [
71
+ 'cloudfront:ListDistributions',
72
+ 'cloudfront:CreateInvalidation',
73
+ ],
74
+ resources: ['*'],
75
+ }),
76
+ // SSM - read CDK bootstrap version parameter
77
+ new iam.PolicyStatement({
78
+ sid: 'ReadVersion',
79
+ effect: iam.Effect.ALLOW,
80
+ actions: [
81
+ 'ssm:GetParameter',
82
+ 'ssm:GetParameters',
83
+ ],
84
+ resources: [
85
+ `arn:aws:ssm:${region}:${accountId}:parameter/cdk-bootstrap/*`,
86
+ ],
87
+ }),
88
+ // S3 - CDK assets and web deployment (scoped to account)
89
+ new iam.PolicyStatement({
90
+ sid: 'S3Permissions',
91
+ effect: iam.Effect.ALLOW,
92
+ actions: [
93
+ 's3:GetObject*',
94
+ 's3:GetBucket*',
95
+ 's3:List*',
96
+ 's3:Abort*',
97
+ 's3:DeleteObject*',
98
+ 's3:PutObject*',
99
+ 's3:ListAllMyBuckets',
100
+ ],
101
+ resources: ['*'],
102
+ conditions: {
103
+ StringEquals: {
104
+ 's3:ResourceAccount': accountId,
105
+ },
106
+ },
107
+ }),
108
+ // KMS - for S3 encryption
109
+ new iam.PolicyStatement({
110
+ sid: 'KMSPermissions',
111
+ effect: iam.Effect.ALLOW,
112
+ actions: [
113
+ 'kms:Decrypt',
114
+ 'kms:DescribeKey',
115
+ 'kms:Encrypt',
116
+ 'kms:ReEncrypt*',
117
+ 'kms:GenerateDataKey*',
118
+ ],
119
+ resources: ['*'],
120
+ conditions: {
121
+ StringEquals: {
122
+ 'kms:ViaService': `s3.${region}.amazonaws.com`,
123
+ },
124
+ },
125
+ }),
126
+ // IAM PassRole - for CloudFormation execution role
127
+ new iam.PolicyStatement({
128
+ sid: 'IAMPassRole',
129
+ effect: iam.Effect.ALLOW,
130
+ actions: ['iam:PassRole'],
131
+ resources: [
132
+ `arn:aws:iam::${accountId}:role/cdk-*-cfn-exec-role-${accountId}-${region}`,
133
+ ],
134
+ }),
135
+ // STS - for CDK operations
136
+ new iam.PolicyStatement({
137
+ sid: 'STSPermissions',
138
+ effect: iam.Effect.ALLOW,
139
+ actions: ['sts:GetCallerIdentity'],
140
+ resources: ['*'],
141
+ }),
142
+ ],
143
+ });
144
+
145
+ deployUser.addManagedPolicy(cdkDeployPolicy);
146
+
147
+ // Create access key for the user
148
+ const accessKey = new iam.AccessKey(this, 'GitHubDeployAccessKey', {
149
+ user: deployUser,
150
+ });
151
+
152
+ // Store access keys in Secrets Manager
153
+ const accessKeySecret = new secretsmanager.Secret(this, 'GitHubDeployCredentials', {
154
+ secretName: `github-deploy-credentials-${environmentName}`,
155
+ description: `GitHub Actions deployment credentials for ${environmentName}`,
156
+ secretObjectValue: {
157
+ accessKeyId: SecretValue.unsafePlainText(accessKey.accessKeyId),
158
+ secretAccessKey: accessKey.secretAccessKey,
159
+ },
160
+ });
161
+
162
+ // Outputs
163
+ new CfnOutput(this, 'DeployUserName', {
164
+ value: deployUser.userName,
165
+ description: `GitHub deploy user name for ${environmentName}`,
166
+ exportName: `GitHubDeployUserName-${environmentName}`,
167
+ });
168
+
169
+ new CfnOutput(this, 'DeployUserArn', {
170
+ value: deployUser.userArn,
171
+ description: `GitHub deploy user ARN for ${environmentName}`,
172
+ exportName: `GitHubDeployUserArn-${environmentName}`,
173
+ });
174
+
175
+ new CfnOutput(this, 'CredentialsSecretArn', {
176
+ value: accessKeySecret.secretArn,
177
+ description: `Secret ARN containing deploy credentials for ${environmentName}`,
178
+ exportName: `GitHubDeployCredentialsArn-${environmentName}`,
179
+ });
180
+
181
+ new CfnOutput(this, 'AccessKeyId', {
182
+ value: accessKey.accessKeyId,
183
+ description: `Access Key ID for ${environmentName} (use Secrets Manager for secret key)`,
184
+ exportName: `GitHubDeployAccessKeyId-${environmentName}`,
185
+ });
186
+ }
187
+ }
@@ -0,0 +1,67 @@
1
+ import { Stack, StackProps, CfnOutput } from "aws-cdk-lib";
2
+ import { Construct } from "constructs";
3
+ import {
4
+ Organization,
5
+ OrganizationalUnit,
6
+ Account,
7
+ } from "@pepperize/cdk-organizations";
8
+
9
+ export interface OrgStackProps extends StackProps {
10
+ devEmail: string;
11
+ stageEmail: string;
12
+ prodEmail: string;
13
+ }
14
+
15
+ export class OrgStack extends Stack {
16
+ constructor(scope: Construct, id: string, props: OrgStackProps) {
17
+ super(scope, id, props);
18
+
19
+ const org = new Organization(this, "Org");
20
+
21
+ const nonProd = new OrganizationalUnit(this, "NonProdOu", {
22
+ organizationalUnitName: "nonprod",
23
+ parent: org.root,
24
+ });
25
+
26
+ const prod = new OrganizationalUnit(this, "ProdOu", {
27
+ organizationalUnitName: "prod",
28
+ parent: org.root,
29
+ });
30
+
31
+ const dev = new Account(this, "DevAccount", {
32
+ accountName: "dev",
33
+ email: props.devEmail,
34
+ parent: nonProd,
35
+ });
36
+
37
+ const stage = new Account(this, "StageAccount", {
38
+ accountName: "stage",
39
+ email: props.stageEmail,
40
+ parent: nonProd,
41
+ });
42
+
43
+ const prodAccount = new Account(this, "ProdAccount", {
44
+ accountName: "prod",
45
+ email: props.prodEmail,
46
+ parent: prod,
47
+ });
48
+
49
+ new CfnOutput(this, "DevAccountId", {
50
+ value: dev.accountId,
51
+ description: "Dev Account ID",
52
+ exportName: "DevAccountId",
53
+ });
54
+
55
+ new CfnOutput(this, "StageAccountId", {
56
+ value: stage.accountId,
57
+ description: "Stage Account ID",
58
+ exportName: "StageAccountId",
59
+ });
60
+
61
+ new CfnOutput(this, "ProdAccountId", {
62
+ value: prodAccount.accountId,
63
+ description: "Prod Account ID",
64
+ exportName: "ProdAccountId",
65
+ });
66
+ }
67
+ }