AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: hexagonal-arch-sample Globals: Function: Timeout: 20 Resources: #DynamoDB table StocksTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: STOCK_ID AttributeType: S KeySchema: - AttributeName: STOCK_ID KeyType: HASH BillingMode: PAY_PER_REQUEST #Network configuration Vpc: Type: AWS::EC2::VPC Properties: CidrBlock: '10.0.0.0/16' SecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: 'Lambda Security Group' VpcId: !Ref Vpc SecurityGroupEgress: - CidrIp: '0.0.0.0/0' FromPort: 0 ToPort: 65535 IpProtocol: tcp SecurityGroupIngress: - CidrIp: '0.0.0.0/0' FromPort: 0 ToPort: 65535 IpProtocol: tcp SubnetA: Type: AWS::EC2::Subnet Properties: VpcId: !Ref Vpc AvailabilityZone: !Select [ 0, !GetAZs '' ] MapPublicIpOnLaunch: false CidrBlock: '10.0.0.0/24' SubnetB: Type: AWS::EC2::Subnet Properties: VpcId: !Ref Vpc AvailabilityZone: !Select [ 1, !GetAZs '' ] MapPublicIpOnLaunch: false CidrBlock: '10.0.1.0/24' SubnetC: Type: AWS::EC2::Subnet Properties: VpcId: !Ref Vpc AvailabilityZone: !Select [ 2, !GetAZs '' ] MapPublicIpOnLaunch: false CidrBlock: '10.0.2.0/24' RouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc PublicSubnetCRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref SubnetC RouteTableId: !Ref RouteTable InternetGateway: Type: 'AWS::EC2::InternetGateway' VPCGatewayAttachment: Type: 'AWS::EC2::VPCGatewayAttachment' Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref InternetGateway InternetRoute: Type: 'AWS::EC2::Route' Properties: DestinationCidrBlock: '0.0.0.0/0' GatewayId: !Ref InternetGateway RouteTableId: !Ref RouteTable EIP: Type: 'AWS::EC2::EIP' Properties: Domain: 'vpc' Nat: Type: 'AWS::EC2::NatGateway' Properties: AllocationId: !GetAtt EIP.AllocationId SubnetId: !Ref SubnetC NatRouteTable: Type: 'AWS::EC2::RouteTable' Properties: VpcId: !Ref Vpc NatRoute: Type: 'AWS::EC2::Route' Properties: DestinationCidrBlock: '0.0.0.0/0' NatGatewayId: !Ref Nat RouteTableId: !Ref NatRouteTable SubnetARouteTableAssociation: Type: 'AWS::EC2::SubnetRouteTableAssociation' Properties: RouteTableId: !Ref NatRouteTable SubnetId: !Ref SubnetA SubnetBRouteTableAssociation: Type: 'AWS::EC2::SubnetRouteTableAssociation' Properties: RouteTableId: !Ref NatRouteTable SubnetId: !Ref SubnetB DDBEndpoint: Type: 'AWS::EC2::VPCEndpoint' Properties: PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: '*' Action: - 'dynamodb:GetItem' - 'dynamodb:DescribeTable' Resource: - '*' ServiceName: !Sub 'com.amazonaws.${AWS::Region}.dynamodb' RouteTableIds: - !Ref NatRouteTable VpcId: !Ref Vpc InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: 'Security Group for outbound traffic' GroupDescription: 'Lambda Traffic' VpcId: !Ref Vpc SecurityGroupEgress: - IpProtocol: '-1' CidrIp: '0.0.0.0/0' InstanceSecurityGroupIngress: Type: 'AWS::EC2::SecurityGroupIngress' DependsOn: 'InstanceSecurityGroup' Properties: GroupId: !Ref InstanceSecurityGroup IpProtocol: 'tcp' FromPort: 0 ToPort: 65535 SourceSecurityGroupId: !Ref InstanceSecurityGroup #ElastiCache Redis ElastiCacheSubnetGroup: Type: 'AWS::ElastiCache::SubnetGroup' Properties: Description: Cache Subnet Group SubnetIds: - !Ref SubnetA - !Ref SubnetB ElastiCacheRedisCluster: Type: AWS::ElastiCache::ReplicationGroup Properties: AutoMinorVersionUpgrade: true AtRestEncryptionEnabled: true CacheNodeType: cache.t2.micro CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup Engine: redis NumNodeGroups: 1 Port: 6379 ReplicasPerNodeGroup: 1 ReplicationGroupDescription: cache-aside pattern with Redis SecurityGroupIds: - !Ref InstanceSecurityGroup #Lambda function StocksConverterFunction: Type: AWS::Serverless::Function DependsOn: ElastiCacheRedisCluster Properties: CodeUri: hexagonal-architecture/ Handler: app.lambdaHandler Runtime: nodejs18.x MemorySize: 256 Policies: - DynamoDBReadPolicy: TableName: !Ref StocksTable - Version: '2012-10-17' Statement: - Effect: Allow Action: - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'ec2:CreateNetworkInterface' - 'ec2:DescribeNetworkInterfaces' - 'ec2:DeleteNetworkInterface' - 'ec2:AssignPrivateIpAddresses' - 'ec2:UnassignPrivateIpAddresses' Resource: '*' Environment: Variables: DB_TABLE: !Ref StocksTable CACHE_URL: !GetAtt ElastiCacheRedisCluster.PrimaryEndPoint.Address CACHE_PORT: !GetAtt ElastiCacheRedisCluster.PrimaryEndPoint.Port # change with a mock API URL or an 3rd Party API # CURRENCIES_PATH: 'https://www.myAPI.com' VpcConfig: SecurityGroupIds: - !Ref InstanceSecurityGroup SubnetIds: - !Ref SubnetA - !Ref SubnetB Events: StocksConverter: Type: HttpApi Properties: ApiId: !Ref StocksGateway Path: /stock/{StockID} Method: get #HTTP API Gateway StocksGateway: Type: AWS::Serverless::HttpApi Properties: CorsConfiguration: AllowMethods: - GET - POST AllowOrigins: - '*' Outputs: HttpApiUrl: Description: URL of your API endpoint, add stock ID like AMZN Value: Fn::Sub: 'https://${StocksGateway}.execute-api.${AWS::Region}.${AWS::URLSuffix}/stock/'