Author:
Fluent Commerce
Changed on:
31 Jan 2024
commercetools CloudFormation Template
1AWSTemplateFormatVersion: "2010-09-09"
2Description: "Fluentcommerce Connectors - Regional Deployment"
3
4Parameters:
5ConnectorName:
6Description: "Connector name"
7Type: String
8
9FluentAccount:
10Description: "Fluent Account name"
11Type: String
12
13RetailerId:
14Description: "Retailer Id"
15Type: Number
16
17Environment:
18Description: "Environment name"
19Type: String
20Default: test
21
22ImageURI:
23Description: "Fully qualified docker image URI to run"
24Type: String
25
26CPU:
27Description: Number of CPUs to assign to the task
28Type: Number
29Default: 512
30AllowedValues:
31- 256
32- 512
33- 1024
34
35Memory:
36Description: Amount of memory to assign to the task in GB
37Type: Number
38Default: 1024
39AllowedValues:
40- 512
41- 1024
42- 2048
43- 3072
44
45ContainerPort:
46Description: Container port to expose to load balancer
47Type: Number
48Default: 8080
49
50MinCapacity:
51Type: Number
52Default: 1
53
54MaxCapacity:
55Type: Number
56Default: 6
57
58VpcId:
59Description: VPC Id
60Type: AWS::EC2::VPC::Id
61
62VpcCidr:
63Description: VPC CIDR Block (eg 10.0.0.0/16)
64Type: String
65AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$'
66
67PublicHostedZoneName:
68Description: Public hosted zone (domain name)
69Type: String
70
71PublicHostedZoneId:
72Description: Public hosted zone id
73Type: String
74
75PublicSubnets:
76Description: Public Subnet IDs
77Type: List<AWS::EC2::Subnet::Id>
78
79PrivateSubnets:
80Description: Private Subnet IDs
81Type: List<AWS::EC2::Subnet::Id>
82
83BatchSQSQueueName:
84Description: Batch Queue Name
85Type: String
86Default: batch
87
88EventsSQSQueueName:
89Description: Events Queue Name
90Type: String
91Default: events
92
93ConnectorSQSQueueName:
94Description: Connector Queue Name
95Type: String
96Default: commercetools
97
98Mappings:
99RegionMap:
100us-east-1:
101RegionCode: use1
102us-east-2:
103RegionCode: use2
104us-west-1:
105RegionCode: usw1
106us-west-2:
107RegionCode: usw2
108af-south-1:
109RegionCode: afs1
110ap-east-1:
111RegionCode: ape1
112ap-south-1:
113RegionCode: aps1
114ap-northeast-1:
115RegionCode: apne1
116ap-northeast-2:
117RegionCode: apne2
118ap-northeast-3:
119RegionCode: apne3
120ap-southeast-1:
121RegionCode: apse1
122ap-southeast-2:
123RegionCode: apse2
124ap-southeast-3:
125RegionCode: apse3
126ca-central-1:
127RegionCode: cac1
128eu-central-1:
129RegionCode: euc1
130eu-west-1:
131RegionCode: euw1
132eu-west-2:
133RegionCode: euw2
134eu-west-3:
135RegionCode: euw3
136eu-south-1:
137RegionCode: eus1
138eu-north-1:
139RegionCode: eun1
140me-south-1:
141RegionCode: mes1
142me-central-1:
143RegionCode: mec1
144sa-east-1:
145RegionCode: sae1
146
147Resources:
148ALBSecurityGroup:
149Type: AWS::EC2::SecurityGroup
150Properties:
151GroupName: !Sub "${ConnectorName}-${Environment}-connectors-alb-sg"
152GroupDescription: !Sub "${Environment} ${AWS::Region} ${ConnectorName} connector load balancer Security Group"
153VpcId: !Ref VpcId
154SecurityGroupIngress:
155- IpProtocol: tcp
156FromPort: 443
157ToPort: 443
158CidrIp: 0.0.0.0/0
159Tags:
160- Key: Name
161Value: !Sub "${ConnectorName}-${Environment}-connectors-alb-sg"
162- Key: Region
163Value: !Ref AWS::Region
164
165ConnectorSecurityGroup:
166Type: AWS::EC2::SecurityGroup
167Properties:
168GroupName: !Sub ${Environment}-{ConnectorName}-connectors-sg
169GroupDescription: !Sub "${Environment} ${AWS::Region} ${ConnectorName} connector Security Group"
170VpcId: !Ref VpcId
171SecurityGroupIngress:
172- CidrIp: !Ref VpcCidr
173Description: "oms connectors access"
174FromPort: 443
175ToPort: 443
176IpProtocol: -1
177Tags:
178- Key: Name
179Value: !Sub "${ConnectorName}-${Environment}-connectors-sg"
180- Key: Region
181Value: !Ref AWS::Region
182
183LogGroup:
184Type: AWS::Logs::LogGroup
185Properties:
186RetentionInDays: 7
187LogGroupName: !Sub "/${ConnectorName}/${Environment}/connectors"
188
189LoadBalancer:
190Type: AWS::ElasticLoadBalancingV2::LoadBalancer
191Properties:
192Name: !Sub "${ConnectorName}-${Environment}-connect-alb"
193Scheme: internet-facing
194SecurityGroups:
195- !Ref ALBSecurityGroup
196Subnets: !Ref PublicSubnets
197
198Certificate:
199Type: "AWS::CertificateManager::Certificate"
200Properties:
201DomainName:
202Fn::Sub:
203- "${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}"
204- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
205ValidationMethod: DNS
206DomainValidationOptions:
207- DomainName:
208Fn::Sub:
209- "${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}"
210- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
211HostedZoneId: !Ref PublicHostedZoneId
212
213RecordSet:
214Type: AWS::Route53::RecordSet
215Properties:
216Name:
217Fn::Sub:
218- "${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}"
219- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
220Comment: !Sub "Alias for connectors endpoint of ${ConnectorName} in ${AWS::Region}"
221Type: CNAME
222HostedZoneId: !Ref PublicHostedZoneId
223TTL: 60
224ResourceRecords:
225- !GetAtt LoadBalancer.DNSName
226
227HttpsTargetGroup:
228Type: AWS::ElasticLoadBalancingV2::TargetGroup
229Properties:
230Name: !Sub ${ConnectorName}-${Environment}-https-tg
231HealthCheckPath: /actuator/health
232HealthCheckPort: !Ref ContainerPort
233HealthCheckIntervalSeconds: 60
234HealthCheckTimeoutSeconds: 2
235HealthyThresholdCount: 2
236UnhealthyThresholdCount: 5
237Port: 443
238Protocol: HTTP
239TargetType: ip
240TargetGroupAttributes:
241- Key: deregistration_delay.timeout_seconds
242Value: "60"
243VpcId: !Ref VpcId
244
245HttpsListener:
246Type: "AWS::ElasticLoadBalancingV2::Listener"
247Properties:
248LoadBalancerArn: !Ref LoadBalancer
249Protocol: HTTPS
250SslPolicy: ELBSecurityPolicy-TLS-1-2-2017-01
251Port: 443
252Certificates:
253- CertificateArn: !Ref Certificate
254DefaultActions:
255- Type: "forward"
256TargetGroupArn: !Ref HttpsTargetGroup
257
258ECSCluster:
259Type: AWS::ECS::Cluster
260Properties:
261ClusterName: !Sub "${ConnectorName}-${Environment}-connectors-cluster"
262
263TaskExecutionRole:
264Type: AWS::IAM::Role
265Properties:
266RoleName: !Sub "${ConnectorName}-${Environment}-connectors-task-execution-role"
267AssumeRolePolicyDocument:
268Version: 2012-10-17
269Statement:
270- Effect: Allow
271Principal:
272Service:
273- ecs-tasks.amazonaws.com
274Action:
275- "sts:AssumeRole"
276ManagedPolicyArns:
277- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
278
279TaskRole:
280Type: AWS::IAM::Role
281Properties:
282RoleName: !Sub "${ConnectorName}-${Environment}-connectors-task-role"
283AssumeRolePolicyDocument:
284Version: 2012-10-17
285Statement:
286- Effect: Allow
287Principal:
288Service:
289- ecs-tasks.amazonaws.com
290Action:
291- "sts:AssumeRole"
292Policies:
293- PolicyName: !Sub "${ConnectorName}-${Environment}-connector-logs"
294PolicyDocument:
295Version: 2012-10-17
296Statement:
297- Effect: Allow
298Action:
299- "logs:CreateLogGroup"
300- "logs:CreateLogStream"
301- "logs:PutLogEvents"
302- "cloudwatch:PutMetricData"
303Resource:
304- "*"
305- PolicyName: !Sub "${ConnectorName}-${Environment}-connector-cfn"
306PolicyDocument:
307Version: 2012-10-17
308Statement:
309- Effect: Allow
310Action:
311- "cloudformation:DescribeStacks"
312Resource:
313- "*"
314- PolicyName: !Sub "${ConnectorName}-${Environment}-connector-secrets"
315PolicyDocument:
316Version: 2012-10-17
317Statement:
318- Effect: Allow
319Action:
320- "secretsmanager:GetResourcePolicy"
321- "secretsmanager:GetSecretValue"
322- "secretsmanager:DescribeSecret"
323- "secretsmanager:ListSecretVersionIds"
324- "secretsmanager:CreateSecret"
325- "secretsmanager:DeleteSecret"
326Resource:
327- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*"
328
329 - PolicyName: !Sub "${ConnectorName}-${Environment}-connector-sqs"
330 PolicyDocument:
331 Version: 2012-10-17
332 Statement:
333 - Effect: Allow
334 Action:
335 - "sqs:*"
336 Resource:
337 - !Sub "arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:*"
338
339TaskDefinition:
340Type: AWS::ECS::TaskDefinition
341Properties:
342Family: !Sub "${ConnectorName}-${Environment}-connector"
343Cpu: !Ref CPU
344Memory: !Ref Memory
345NetworkMode: awsvpc
346ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
347TaskRoleArn: !GetAtt TaskRole.Arn
348RequiresCompatibilities:
349- FARGATE
350ContainerDefinitions:
351- Name: connector
352Image: !Ref ImageURI
353Essential: true
354PortMappings:
355- ContainerPort: !Ref ContainerPort
356Environment:
357- Name: CONNECTOR_NAME
358Value: !Ref ConnectorName
359- Name: ENVIRONMENT
360Value: !Ref Environment
361- Name: SQS_BATCH
362Value: !GetAtt BatchSQSQueue.QueueName
363- Name: SQS_EVENTS
364Value: !GetAtt EventsSQSQueue.QueueName
365- Name: SQS_CONNECTOR
366Value: !GetAtt ConnectorSQSQueue.QueueName
367- Name: REGION
368Value: !Ref AWS::Region
369LogConfiguration:
370LogDriver: awslogs
371Options:
372awslogs-group: !Ref LogGroup
373awslogs-region: !Ref AWS::Region
374awslogs-stream-prefix: connectors
375
376ECSService:
377DependsOn:
378- HttpsListener
379Type: AWS::ECS::Service
380Properties:
381ServiceName: !Sub "${ConnectorName}-${Environment}-connectors"
382Cluster: !Ref ECSCluster
383TaskDefinition: !Ref TaskDefinition
384DesiredCount: 1
385DeploymentConfiguration:
386MinimumHealthyPercent: 100
387MaximumPercent: 200
388EnableECSManagedTags: true
389LaunchType: FARGATE
390HealthCheckGracePeriodSeconds: 120
391LoadBalancers:
392- TargetGroupArn: !Ref HttpsTargetGroup
393ContainerPort: !Ref ContainerPort
394ContainerName: connector
395NetworkConfiguration:
396AwsvpcConfiguration:
397AssignPublicIp: DISABLED
398SecurityGroups:
399- !Ref ConnectorSecurityGroup
400Subnets: !Ref PrivateSubnets
401PropagateTags: SERVICE
402
403ScalingPolicy:
404Type: AWS::ApplicationAutoScaling::ScalingPolicy
405Properties:
406PolicyName: "connectors autoscaling policy"
407PolicyType: TargetTrackingScaling
408ScalingTargetId: !Ref AutoScalingTarget
409TargetTrackingScalingPolicyConfiguration:
410DisableScaleIn: False
411PredefinedMetricSpecification:
412PredefinedMetricType: ECSServiceAverageMemoryUtilization
413ScaleInCooldown: 300
414ScaleOutCooldown: 120
415TargetValue: 60.0
416
417AutoScalingTarget:
418Type: AWS::ApplicationAutoScaling::ScalableTarget
419Properties:
420MinCapacity: !Ref MinCapacity
421MaxCapacity: !Ref MaxCapacity
422ResourceId: !Sub
423- service/${ClusterName}/${ServiceName}
424- ServiceName: !GetAtt ECSService.Name
425ClusterName: !Ref ECSCluster
426ScalableDimension: ecs:service:DesiredCount
427ServiceNamespace: ecs
428RoleARN: !Sub "arn:aws:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_ECSService"
429
430
431BatchSQSQueue:
432Type: AWS::SQS::Queue
433Properties:
434QueueName: !Sub "${Environment}-${BatchSQSQueueName}"
435ReceiveMessageWaitTimeSeconds: 5
436RedrivePolicy:
437deadLetterTargetArn: !GetAtt BatchDeadLetterQueue.Arn
438maxReceiveCount: 5
439Tags:
440- Key: Name
441Value:
442Fn::Sub:
443- "${ConnectorName}-${RegionCode}-${Environment}-${BatchSQSQueueName}-sqs"
444- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
445VisibilityTimeout: 120
446
447BatchDeadLetterQueue:
448Type: AWS::SQS::Queue
449Properties:
450QueueName: !Sub "${Environment}-${BatchSQSQueueName}-dlq"
451
452EventsSQSQueue:
453Type: AWS::SQS::Queue
454Properties:
455QueueName: !Sub "${Environment}-${EventsSQSQueueName}"
456ReceiveMessageWaitTimeSeconds: 20
457RedrivePolicy:
458deadLetterTargetArn: !GetAtt EventsDeadLetterQueue.Arn
459maxReceiveCount: 5
460Tags:
461- Key: Name
462Value:
463Fn::Sub:
464- "${ConnectorName}-${RegionCode}-${Environment}-${EventsSQSQueueName}-sqs"
465- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
466VisibilityTimeout: 120
467
468EventsDeadLetterQueue:
469Type: AWS::SQS::Queue
470Properties:
471QueueName: !Sub "${Environment}-${EventsSQSQueueName}-dlq"
472
473ConnectorSQSQueue:
474Type: AWS::SQS::Queue
475Properties:
476QueueName: !Sub "${Environment}-${ConnectorSQSQueueName}"
477ReceiveMessageWaitTimeSeconds: 20
478RedrivePolicy:
479deadLetterTargetArn: !GetAtt ConnectorDeadLetterQueue.Arn
480maxReceiveCount: 5
481Tags:
482- Key: Name
483Value:
484Fn::Sub:
485- "${ConnectorName}-${RegionCode}-${Environment}-${ConnectorSQSQueueName}-sqs"
486- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
487VisibilityTimeout: 120
488
489ConnectorDeadLetterQueue:
490Type: AWS::SQS::Queue
491Properties:
492QueueName: !Sub "${Environment}-${ConnectorSQSQueueName}-dlq"
493
494ApiConnection:
495Type: AWS::Events::Connection
496Properties:
497AuthorizationType: BASIC
498Description: !Sub "Connection to ${ConnectorName} ${Environment} connector endpoint"
499AuthParameters:
500BasicAuthParameters:
501Username: admin
502Password: "pass"
503InvocationHttpParameters: {}
504
505ProductApiDestination:
506DependsOn:
507- ECSService
508Type: AWS::Events::ApiDestination
509Properties:
510ConnectionArn: !GetAtt ApiConnection.Arn
511Description: !Sub "Product Api Destination for ${ConnectorName} Scheduled Events Rule"
512HttpMethod: PUT
513InvocationEndpoint:
514Fn::Sub:
515- "https://${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}/api/v1/fluent-connect/scheduler/add/${FluentAccount}/${RetailerId}/batch-product-catalogue-sync"
516- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
517InvocationRateLimitPerSecond: 300
518Name: !Sub "${ConnectorName}.${Environment}-product-api-destination"
519
520LocationApiDestination:
521DependsOn:
522- ECSService
523Type: AWS::Events::ApiDestination
524Properties:
525ConnectionArn: !GetAtt ApiConnection.Arn
526Description: !Sub "Location Api Destination for ${ConnectorName} Scheduled Events Rule"
527HttpMethod: PUT
528InvocationEndpoint:
529Fn::Sub:
530- "https://${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}/api/v1/fluent-connect/scheduler/add/${FluentAccount}/${RetailerId}/batch-location-sync"
531- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
532InvocationRateLimitPerSecond: 300
533Name: !Sub "${ConnectorName}.${Environment}-location-api-destination"
534
535InventoryApiDestination:
536DependsOn:
537- ECSService
538Type: AWS::Events::ApiDestination
539Properties:
540ConnectionArn: !GetAtt ApiConnection.Arn
541Description: !Sub "Inventory Api Destination for ${ConnectorName} Scheduled Events Rule"
542HttpMethod: PUT
543InvocationEndpoint:
544Fn::Sub:
545- "https://${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}/api/v1/fluent-connect/scheduler/add/${FluentAccount}/${RetailerId}/batch-inventory-sync"
546- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
547InvocationRateLimitPerSecond: 300
548Name: !Sub "${ConnectorName}.${Environment}-inventory-api-destination"
549
550RuleTargetRole:
551Type: AWS::IAM::Role
552Properties:
553RoleName: !Sub "${ConnectorName}-${Environment}-invoke-api-destination"
554AssumeRolePolicyDocument:
555Version: 2012-10-17
556Statement:
557- Effect: Allow
558Principal:
559Service:
560- events.amazonaws.com
561Action:
562- "sts:AssumeRole"
563Policies:
564- PolicyName: !Sub "${ConnectorName}-${Environment}-invoke-api-destination"
565PolicyDocument:
566Version: 2012-10-17
567Statement:
568- Effect: Allow
569Action:
570- events:InvokeApiDestination
571Resource:
572- !Sub "arn:aws:events:${AWS::Region}:${AWS::AccountId}:api-destination/*"
573
574ProductScheduledEventsRule:
575Type: AWS::Events::Rule
576Properties:
577Description: Hits the connector endpoint on a schedule
578EventBusName: default
579Name: !Sub "${ConnectorName}-${Environment}-scheduled-product-catalogue-rule"
580ScheduleExpression: "rate(15 minutes)"
581State: ENABLED
582Targets:
583- Arn: !GetAtt ProductApiDestination.Arn
584Id: !Sub "${ConnectorName}-${Environment}-product-catalogue-target-id"
585RoleArn: !GetAtt RuleTargetRole.Arn
586RetryPolicy:
587MaximumRetryAttempts: 4
588MaximumEventAgeInSeconds: 400
589DeadLetterConfig:
590Arn: !GetAtt BatchDeadLetterQueue.Arn
591
592LocationScheduledEventsRule:
593Type: AWS::Events::Rule
594Properties:
595Description: Hits the connector endpoint on a schedule
596EventBusName: default
597Name: !Sub "${ConnectorName}-${Environment}-scheduled-batch-location-rule"
598ScheduleExpression: "rate(30 minutes)"
599State: ENABLED
600Targets:
601- Arn: !GetAtt LocationApiDestination.Arn
602Id: !Sub "${ConnectorName}-${Environment}-batch-location-target-id"
603RoleArn: !GetAtt RuleTargetRole.Arn
604RetryPolicy:
605MaximumRetryAttempts: 4
606MaximumEventAgeInSeconds: 400
607DeadLetterConfig:
608Arn: !GetAtt BatchDeadLetterQueue.Arn
609InventoryScheduledEventsRule:
610Type: AWS::Events::Rule
611Properties:
612Description: Hits the connector endpoint on a schedule
613EventBusName: default
614Name: !Sub "${ConnectorName}-${Environment}-scheduled-batch-inventory-rule"
615ScheduleExpression: "rate(5 minutes)"
616State: ENABLED
617Targets:
618- Arn: !GetAtt InventoryApiDestination.Arn
619Id: !Sub "${ConnectorName}-${Environment}-batch-inventory-target-id"
620RoleArn: !GetAtt RuleTargetRole.Arn
621RetryPolicy:
622MaximumRetryAttempts: 4
623MaximumEventAgeInSeconds: 400
624DeadLetterConfig:
625Arn: !GetAtt BatchDeadLetterQueue.Arn
626
627Outputs:
628BatchQueueURL:
629Description: URL of batch SQS queue
630Value: !Ref BatchSQSQueue
631
632EventsQueueURL:
633Description: URL of events SQS queue
634Value: !Ref EventsSQSQueue
635
636ConnectorQueueURL:
637Description: URL of connector SQS queue
638Value: !Ref ConnectorSQSQueue
639
640ConnectorsPublicURL:
641Description: "The public URL of the connectors endpoint"
642Value:
643Fn::Sub:
644- "https://${ConnectorName}.${RegionCode}.${Environment}.${PublicHostedZoneName}"
645- RegionCode: !FindInMap [RegionMap, !Ref AWS::Region, RegionCode]
Language: text
Name: CloudFormation Template
Description:
[Warning: empty required content area]Copyright © 2024 Fluent Retail Pty Ltd (trading as Fluent Commerce). All rights reserved. No materials on this docs.fluentcommerce.com site may be used in any way and/or for any purpose without prior written authorisation from Fluent Commerce. Current customers and partners shall use these materials strictly in accordance with the terms and conditions of their written agreements with Fluent Commerce or its affiliates.