-
Notifications
You must be signed in to change notification settings - Fork 0
/
cloudformation.yaml
652 lines (652 loc) · 23.9 KB
/
cloudformation.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
AWSTemplateFormatVersion: "2010-09-09"
Description: >-
A stack to host the frontend of the AWS Shopping App
#Metadata
Parameters:
Email:
Type: String
Description: The email address that will receive SNS notifications for the health check alarm
# Simple regex from https://stackoverflow.com/a/201378
AllowedPattern: "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"
AuthDomain:
Type: String
Description: The domain name for Cognito's hosted UI
# aws, amazon, and cognito are reserved words
AllowedPattern: "^(?![\\s\\S]*?(?:aws|amazon|cognito))^[a-z0-9](?:[a-z0-9\\-]{0,61}[a-z0-9])?$"
#Rules
Mappings:
CloudFront:
# Managed cache policies: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-cache-policies.html
CachePolicies:
Amplify: 2e54312d-136d-493c-8eb9-b001f22f67d2
CachingDisabled: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
CachingOptimized: 658327ea-f89d-4fab-a63d-7e88639e58f6
CachingOptimizedForUncompressedObjects: b2884449-e4de-46a7-ac36-70bc7f1ddd6d
ElementalMediaPackage: 08627262-05a9-4f76-9ded-b50ca2e3a84f
#Conditions
#Transform
Resources:
# IAM users/roles
# Frontend Developer
# Backend Developer
# Database Administrator
AdminGroup:
Type: AWS::IAM::Group
Properties:
GroupName: Admins
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
DeveloperGroup:
Type: AWS::IAM::Group
Properties:
GroupName: Developers
ManagedPolicyArns:
- arn:aws:iam::aws:policy/job-function/SystemAdministrator
DatabaseAdminGroup:
Type: AWS::IAM::Group
Properties:
GroupName: DatabaseAdmins
ManagedPolicyArns:
- arn:aws:iam::aws:policy/job-function/DatabaseAdministrator
# Create an IdP for GitHub to use in workflows
GitHubRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: sts:AssumeRoleWithWebIdentity
Principal:
Federated: !Ref GitHubOIDC
Condition:
# aud = audience, sub = subject
StringEquals:
token.actions.githubusercontent.com:aud: sts.amazonaws.com
StringLike:
token.actions.githubusercontent.com:sub: repo:Abhiek187/aws-shop:*
ManagedPolicyArns:
# A list of policies required for every workflow (max 10)
# The shortest managed policies will move to a consolidated customer policy
- !Ref GitHubRemainingPolicy
# CloudFormation (need read access to detect drift & write access to update resources)
- "arn:aws:iam::aws:policy/CloudFrontFullAccess" # 31
- "arn:aws:iam::aws:policy/IAMFullAccess" # 22
- "arn:aws:iam::aws:policy/AmazonRoute53FullAccess" # 31
- "arn:aws:iam::aws:policy/AmazonCognitoPowerUser" # 52
# React
- "arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess" # 133
# Microservices
- "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess" # 108
# SAM
- "arn:aws:iam::aws:policy/AWSLambda_FullAccess" # 52
- "arn:aws:iam::aws:policy/AmazonEventBridgeFullAccess" # 83
- "arn:aws:iam::aws:policy/CloudWatchFullAccessV2" # 45
GitHubRemainingPolicy:
Type: AWS::IAM::ManagedPolicy
Metadata:
guard:
SuppressedRules:
- IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS # need write access for CloudFormation
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AWSCloudFormationFullAccess
Effect: Allow
Action:
- "cloudformation:*"
Resource: "*"
- Sid: AmazonSNSFullAccess
Effect: Allow
Action:
- "sns:*"
Resource: "*"
- Sid: AmazonS3FullAccess
Effect: Allow
Action:
- "s3:*"
- "s3-object-lambda:*"
Resource: "*"
- Sid: AmazonAPIGatewayAdministrator
Effect: Allow
Action:
- "apigateway:*"
Resource: "arn:aws:apigateway:*::/*"
- Sid: AmazonSQSFullAccess
Effect: Allow
Action:
- "sqs:*"
Resource: "*"
GitHubOIDC:
Type: AWS::IAM::OIDCProvider
Properties:
Url: https://token.actions.githubusercontent.com
ClientIdList:
- sts.amazonaws.com
ThumbprintList:
- 6938fd4d98bab03faadb97b34396831e3780aea1
- 1c58a3a8518e8759bf075b76b750d4f2df264fcd
# Allow CodeBuild to upload objects to S3
# https://docs.aws.amazon.com/codebuild/latest/userguide/setting-up.html#setting-up-service-role
CodeBuildPolicy:
Type: AWS::IAM::ManagedPolicy
Metadata:
guard:
SuppressedRules:
# The same suppressed rule must be defined here due to a bug with cfn-guard:
# https://github.com/aws-cloudformation/aws-guard-rules-registry/issues/246
- IAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESS
Properties:
ManagedPolicyName: CodeBuildAccess
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: CloudWatchLogsPolicy
Effect: Allow
Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
- Sid: S3ObjectPolicy
Effect: Allow
Action:
- "s3:DeleteObject"
- "s3:GetObject"
- "s3:ListBucket"
- "s3:PutObject"
Resource:
- !GetAtt ReactAppBucket.Arn
- !Sub "${ReactAppBucket.Arn}/*"
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: codebuild.amazonaws.com
ManagedPolicyArns:
- !Ref CodeBuildPolicy
# CodePipeline (or just CodeBuild for now)
CodeBuildProject:
Type: AWS::CodeBuild::Project
# DependsOn: CodeBuildSourceCredential
Properties:
Name: AWSShopBuild
Description: A project that builds and deploys the AWS Shopping App
Source:
Auth:
Type: OAUTH
# Directions to let GitHub authorize CodeBuild:
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codebuild-project-source.html#cfn-codebuild-project-source-location
Location: https://github.com/Abhiek187/aws-shop.git # not required if using CodePipeline
Type: GITHUB
ReportBuildStatus: true
ServiceRole: !GetAtt CodeBuildRole.Arn
# Upload the build to the S3 bucket
Artifacts:
Location: !Ref ReactAppBucket
Name: "/" # store the build in the root directory of the bucket (not zipped)
Type: S3
# Don't override S3's default SSE-S3 encryption with a KMS key
# (will prevent the website from being accessible)
EncryptionDisabled: true
Environment:
# Make sure the image supports the runtime in the buildspec:
# https://docs.aws.amazon.com/codebuild/latest/userguide/available-runtimes.html
Type: ARM_CONTAINER
Image: aws/codebuild/amazonlinux2-aarch64-standard:3.0 # Amazon Linux 2023 ARM image
ComputeType: BUILD_GENERAL1_SMALL # free tier eligible
EnvironmentVariables:
- Name: CI
Value: "true"
- Name: ARTIFACT_BUCKET
Value: !Ref ReactAppBucket
BadgeEnabled: true # show a badge of the build status on GitHub, remove if using CodePipeline
TimeoutInMinutes: 5 # important to limit build minutes/month (default: 60)
# Get the PAT from Parameter Store to let CodeBuild authenticate with GitHub
# ssm-secure only works with a limited number of resources:
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#template-parameters-dynamic-patterns-resources
# CodeBuildSourceCredential:
# Type: AWS::CodeBuild::SourceCredential
# Properties:
# Token: "{{resolve:ssm-secure:GitHubToken}}" # must be created outside CloudFormation
# ServerType: GITHUB
# AuthType: PERSONAL_ACCESS_TOKEN
### Frontend ###
# S3 bucket
ReactAppBucket:
Type: AWS::S3::Bucket
Metadata:
guard:
SuppressedRules:
# Reliability suppressions
- S3_BUCKET_REPLICATION_ENABLED # stay within 435 MB
- S3_BUCKET_DEFAULT_LOCK_ENABLED # objects can be overwritten regularly
# Can't delete an S3 bucket until all its objects are deleted
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
# Buckets and objects are encrypted by default
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
LoggingConfiguration:
DestinationBucketName: !Ref S3LoggingBucket
# Disable ACLs
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
# Some access settings need to be enabled to add bucket policies
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true # set to false when creating a bucket policy
IgnorePublicAcls: true
RestrictPublicBuckets: true # set to false when creating a bucket policy
VersioningConfiguration:
Status: Enabled
# Save money by deleting older copies of objects
LifecycleConfiguration:
Rules:
- Id: DeleteOldVersionsRule
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 1
ExpiredObjectDeleteMarker: true
NoncurrentVersionExpiration:
NoncurrentDays: 1
ReactAppBucketPolicy:
Type: AWS::S3::BucketPolicy
Metadata:
guard:
SuppressedRules:
- S3_BUCKET_SSL_REQUESTS_ONLY # Resource: "*" isn't valid
Properties:
Bucket: !Ref ReactAppBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
# Only allow CloudFront to access the S3 bucket
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#create-oac-overview-s3
- Sid: AllowCloudFrontServicePrincipalReadOnly
Effect: Allow
Principal:
Service: cloudfront.amazonaws.com
Action: s3:GetObject
Resource: !Sub "${ReactAppBucket.Arn}/*"
Condition:
ArnEquals:
"aws:SourceArn": !Sub "arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution.Id}"
- Sid: AllowSSLRequestsOnly
Effect: Deny
Principal: "*"
Action: "s3:*"
Resource:
- !GetAtt ReactAppBucket.Arn
- !Sub "${ReactAppBucket.Arn}/*"
Condition:
Bool:
"aws:SecureTransport": false
S3LoggingBucket:
Type: AWS::S3::Bucket
Metadata:
guard:
SuppressedRules:
# Reliability suppressions
- S3_BUCKET_REPLICATION_ENABLED # stay within 435 MB
# Security suppressions
- S3_BUCKET_LOGGING_ENABLED # this is a logging bucket
# Can't delete an S3 bucket until all its objects are deleted
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
# Disable ACLs
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
# Temporarily store logs for 1 day
LifecycleConfiguration:
Rules:
- Id: DeleteRule
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 1
ExpirationInDays: 1
NoncurrentVersionExpiration:
NoncurrentDays: 1
# Enable WORM (write once, read many)
ObjectLockEnabled: true
ObjectLockConfiguration:
ObjectLockEnabled: Enabled
Rule:
DefaultRetention:
Mode: GOVERNANCE # laxer than compliance mode
Days: 1
LoggingBucketPolicy:
Type: AWS::S3::BucketPolicy
Metadata:
guard:
SuppressedRules:
- S3_BUCKET_SSL_REQUESTS_ONLY # Resource: "*" isn't valid
Properties:
Bucket: !Ref S3LoggingBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
# Allow the frontend bucket to store server access logs in this bucket
- Sid: AllowS3Logs
Effect: Allow
Principal:
Service: logging.s3.amazonaws.com
Action: s3:PutObject
Resource: !Sub "${S3LoggingBucket.Arn}/*"
Condition:
ArnLike:
"aws:SourceArn": !GetAtt ReactAppBucket.Arn
StringEquals:
"aws:SourceAccount": !Ref AWS::AccountId
- Sid: AllowSSLRequestsOnly
Effect: Deny
Principal: "*"
Action: "s3:*"
Resource:
- !GetAtt S3LoggingBucket.Arn
- !Sub "${S3LoggingBucket.Arn}/*"
Condition:
Bool:
"aws:SecureTransport": false
CloudFrontLoggingBucket:
Type: AWS::S3::Bucket
Metadata:
guard:
SuppressedRules:
# Reliability suppressions
- S3_BUCKET_REPLICATION_ENABLED # stay within 435 MB
# Security suppressions
- S3_BUCKET_LOGGING_ENABLED # this is a logging bucket
# Can't delete an S3 bucket until all its objects are deleted
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
# ACLs must be enabled for CloudFront logs
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerPreferred
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
# Temporarily store logs for 1 day
LifecycleConfiguration:
Rules:
- Id: DeleteRule
Status: Enabled
AbortIncompleteMultipartUpload:
DaysAfterInitiation: 1
ExpirationInDays: 1
NoncurrentVersionExpiration:
NoncurrentDays: 1
# Enable WORM (write once, read many)
ObjectLockEnabled: true
ObjectLockConfiguration:
ObjectLockEnabled: Enabled
Rule:
DefaultRetention:
Mode: GOVERNANCE # laxer than compliance mode
Days: 1
# CloudFront
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Origins:
- DomainName: !GetAtt ReactAppBucket.DomainName
Id: ReactAppS3Origin
S3OriginConfig:
OriginAccessIdentity: ""
OriginAccessControlId: !GetAtt CloudFrontOriginAccessControl.Id
Enabled: true # required
ViewerCertificate:
CloudFrontDefaultCertificate: true
DefaultRootObject: index.html
DefaultCacheBehavior: # required
AllowedMethods:
- GET
- HEAD
- OPTIONS
TargetOriginId: ReactAppS3Origin
Compress: true
# TTL: min = 1s, default = 1d, max = 1y; brotli+gzip compression
# No cookies, headers (except Accept-Encoding), or queries in the cache key
CachePolicyId:
!FindInMap [CloudFront, CachePolicies, CachingOptimized]
ViewerProtocolPolicy: redirect-to-https
ResponseHeadersPolicyId: !Ref CloudFrontResponseHeadersPolicy
PriceClass: PriceClass_100 # only use edge locations in North America, Europe, and Israel
CustomErrorResponses:
- ErrorCachingMinTTL: 300 # 5 minutes
# Redirect 403 errors (forbidden) to a 404 page (not found)
ErrorCode: 403
ResponseCode: 404
ResponsePagePath: /index.html
# Logging:
# Bucket: !GetAtt CloudFrontLoggingBucket.DomainName
# IncludeCookies: false
CloudFrontOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Description: AWS Shopping App Origin Access Control
Name: ReactAppOAC
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
CloudFrontResponseHeadersPolicy:
Type: AWS::CloudFront::ResponseHeadersPolicy
Properties:
ResponseHeadersPolicyConfig:
Name: ReactAppHeaders
# CustomHeadersConfig:
# Items:
# - Header: Content-Security-Policy-Report-Only
# Override: true
# Value: >-
SecurityHeadersConfig:
# Add the necessary security headers to pass Mozilla Observatory
ContentSecurityPolicy:
# Test using Content-Security-Policy-Report-Only
ContentSecurityPolicy: !Sub >-
default-src 'none';
img-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
object-src 'none';
font-src 'self' https://fonts.gstatic.com;
manifest-src 'self';
connect-src 'self' https://dovshfcety3as.cloudfront.net https://${AuthDomain}.auth.${AWS::Region}.amazoncognito.com https://cognito-idp.${AWS::Region}.amazonaws.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self'
Override: true
# X-Content-Type-Options
ContentTypeOptions:
Override: true # set to nosniff
# X-Frame-Options
FrameOptions:
FrameOption: SAMEORIGIN
Override: true
ReferrerPolicy:
Override: true
ReferrerPolicy: strict-origin-when-cross-origin
# Strict-Transport-Security: max-age=31536000; includeSubDomains
StrictTransportSecurity:
AccessControlMaxAgeSec: 31536000 # 1 year
IncludeSubdomains: true
Override: true
Preload: false # not standard
# X-XSS-Protection is non-standard and can cause vulnerabilities
# Route 53
HealthCheck:
Type: AWS::Route53::HealthCheck
Properties:
HealthCheckConfig:
# 3 health checks every 30s by default
# Optional features: HTTPS, STR_MATCH, RequestInterval < 30, MeasureLatency
Type: HTTP
FullyQualifiedDomainName: !GetAtt CloudFrontDistribution.DomainName
Port: 80
ResourcePath: /
# CloudWatch
HealthCheckAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Check the health of the AWS Shop site and send an email whenever it's unhealthy
# In Route 53, monitor the health check status in the past 5 minutes (either 0 or 1).
# If this unitless value is < 1 at any point, transition to an ALARM state.
# https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html
Namespace: AWS/Route53
MetricName: HealthCheckStatus
Dimensions:
- Name: HealthCheckId
Value: !Ref HealthCheck
Period: 300 # 5 minutes
EvaluationPeriods: 1
Statistic: Minimum
ComparisonOperator: LessThanThreshold
Threshold: 1
Unit: None
AlarmActions:
- !Ref SNSTopic
# SNS
SNSTopic:
Type: AWS::SNS::Topic
Properties:
# Encrypt using the default SNS SSE key
# Key aliases: aws kms list-aliases
KmsMasterKeyId: alias/aws/sns
Subscription:
- Endpoint: !Ref Email
Protocol: email
SNSTopicPolicy:
Type: AWS::SNS::TopicPolicy
Properties:
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowSSLRequestsOnly
Effect: Deny
Principal: "*"
Action: sns:Publish
Resource: !Ref SNSTopic
Condition:
Bool:
"aws:SecureTransport": false
Topics:
- !Ref SNSTopic
# X-Ray
### Backend ###
# API Gateway
# SQS
# Lambda
# DynamoDB
# Parameter Store
# CloudFormation doesn't support creating a SecureString parameter type
# Cognito
CognitoUserPool:
Type: AWS::Cognito::UserPool
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
AccountRecoverySetting:
RecoveryMechanisms:
- Name: verified_email
Priority: 1
AdminCreateUserConfig:
AllowAdminCreateUserOnly: false # change to true if testing
AutoVerifiedAttributes:
- email
DeletionProtection: ACTIVE
EmailConfiguration:
# Default email: [email protected]
EmailSendingAccount: COGNITO_DEFAULT # max 50 emails per day
# Default password policy: 8 chars w/ 1 num, special char, uppercase & lowercase letter
MfaConfiguration: "OFF" # turn on or make optional for more sensitive apps
UserAttributeUpdateSettings:
# Don't update email addresses until they're verified
AttributesRequireVerificationBeforeUpdate:
- email
# Allow users to sign in using either their username or email
UsernameAttributes:
- email
UsernameConfiguration:
CaseSensitive: false
# Use the Cognito Hosted UI for authentication
CognitoUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Ref AuthDomain # .auth.[region].amazoncognito.com
UserPoolId: !Ref CognitoUserPool
CognitoUserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
AccessTokenValidity: 1 # TokenValidityUnits = hours
AllowedOAuthFlows:
- code
AllowedOAuthFlowsUserPoolClient: true
AllowedOAuthScopes:
- openid
- phone
- email
- profile
- aws.cognito.signin.user.admin
AuthSessionValidity: 3 # session token validity in minutes
CallbackURLs:
- "http://localhost:5173" # dev
- "http://localhost:4173" # QA
- !Sub "https://${CloudFrontDistribution.DomainName}" # prod
EnableTokenRevocation: true
ExplicitAuthFlows:
- ALLOW_REFRESH_TOKEN_AUTH
- ALLOW_USER_SRP_AUTH # SRP = Secure Remote Password
GenerateSecret: false
IdTokenValidity: 1 # hour
PreventUserExistenceErrors: ENABLED # shh, don't tell hackers a user doesn't exist
RefreshTokenValidity: 30 # days
SupportedIdentityProviders:
- COGNITO
UserPoolId: !Ref CognitoUserPool
Outputs:
WebsiteURL:
Description: URL for the website hosted on S3
Value: !GetAtt ReactAppBucket.WebsiteURL
CloudFrontURL:
Description: The URL of the React app hosted over HTTPS using CloudFront
Value: !Sub "https://${CloudFrontDistribution.DomainName}"
GitHubRoleArn:
Value: !GetAtt GitHubRole.Arn
UserPoolProviderURL:
Description: The provider URL for the Cognito user pool
Value: !GetAtt CognitoUserPool.ProviderURL