AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: Serverlesspresso core stack. Parameters: AppName: Type: String Description: Application name (eg. serverlesspresso) Default: 'Serverlesspresso' Service: Type: String Description: Service name (eg. core) Default: 'core' LogRetentionInDays: Type: Number Default: 14 Description: CloudWatch Logs retention period TimeInterval: Type: Number Description: Time interval of buckets (mins) Default: 5 CodeLength: Type: Number Description: Code length in characters Default: 10 TokensPerBucket: Type: Number Description: Tokens in each bucket Default: 10 Source: Type: String Description: Event bus source by application Default: 'awsserverlessda.serverlesspresso' Globals: Api: Cors: AllowOrigin: "'*'" AllowHeaders: "'Content-Type'" AllowMethods: "'GET,OPTIONS'" Resources: ########################################## # Custom event bus # ########################################## ServerlesspressoEventBus: Type: AWS::Events::EventBus Properties: Name: "Serverlesspresso" CoreEventBusNameParameter: Type: "AWS::SSM::Parameter" Properties: Name: !Sub /${AppName}/${Service}/eventbusname Description: EventBus Name Type: String Value: !Ref ServerlesspressoEventBus CoreEventBusARNParameter: Type: "AWS::SSM::Parameter" Properties: Name: !Sub /${AppName}/${Service}/eventbusarn Description: EventBus ARN Type: String Value: !Sub ${ServerlesspressoEventBus.Arn} ############################################# # Resources for IoT Core realtime messaging # ############################################# ServerlesspressoRealtime: Type: "AWS::IoT::Thing" Properties: ThingName: "serverlesspresso-realtime-workshop" AttributePayload: Attributes: {} IoTRealtimeParameter: Type: "AWS::SSM::Parameter" Properties: Name: !Sub /${AppName}/${Service}/realtime Description: IoTRealtime URL Type: String Value: !GetAtt IotEndpoint.IotEndpointAddress GetIoTEndpointFunction: Type: 'AWS::Serverless::Function' Properties: Handler: GetIoTEndpoint.handler Runtime: nodejs14.x CodeUri: ../backends/0-core/code/GetIoTEndpoint MemorySize: 128 Timeout: 3 Policies: - Version: '2012-10-17' Statement: - Effect: Allow Action: - iot:DescribeEndpoint Resource: - '*' GetIoTEndpointLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub "/aws/lambda/${GetIoTEndpointFunction}" RetentionInDays: !Ref LogRetentionInDays IotEndpoint: Type: 'Custom::IotEndpoint' Properties: ServiceToken: !GetAtt GetIoTEndpointFunction.Arn ########################################## # Cognito Resources # ########################################## UserPool: Type: "AWS::Cognito::UserPool" Properties: UserPoolName: ServerlesspressoUserPool AutoVerifiedAttributes: - email EmailVerificationMessage: "Here is your Serverlesspresso workshop verification code: {####}." EmailVerificationSubject: "Serverlesspresso workshop code" MfaConfiguration: "OFF" Schema: - Name: email AttributeDataType: String Mutable: false Required: true Policies: PasswordPolicy: MinimumLength: 6 RequireLowercase: false RequireNumbers: false RequireSymbols: false RequireUppercase: false # UsernameAttributes: # - phone_number UserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: GenerateSecret: false UserPoolId: !Ref UserPool UserPoolParameter: Type: "AWS::SSM::Parameter" Properties: Name: !Sub /${AppName}/${Service}/userpool Description: UserPool ID Type: String Value: !Ref UserPool UserPoolClientParameter: Type: "AWS::SSM::Parameter" Properties: Name: !Sub /${AppName}/${Service}/userpoolclient Description: UserPool Client Type: String Value: !Ref UserPoolClient # Creates a federated Identity pool IdentityPool: Type: "AWS::Cognito::IdentityPool" Properties: IdentityPoolName: ServerlesspressoIdentityPool AllowUnauthenticatedIdentities: true CognitoIdentityProviders: - ClientId: !Ref UserPoolClient ProviderName: !GetAtt UserPool.ProviderName # Create a role for unauthorized access to AWS resources. CognitoUnAuthorizedRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Federated: "cognito-identity.amazonaws.com" Action: - "sts:AssumeRoleWithWebIdentity" Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref IdentityPool "ForAnyValue:StringLike": "cognito-identity.amazonaws.com:amr": unauthenticated Policies: - PolicyName: "CognitoUnauthorizedPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "cognito-sync:*" Resource: !Join [ "", [ "arn:aws:cognito-sync:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":identitypool/", !Ref IdentityPool] ] - Effect: Allow Action: - iot:Connect Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":client/serverlesspresso-*" ] ] - Effect: Allow Action: - iot:Subscribe Resource: "*" - Effect: Allow Action: - iot:Receive Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":topic/*" ] ] # Create a role for authorized acces to AWS resources. CognitoAuthorizedRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Federated: "cognito-identity.amazonaws.com" Action: - "sts:AssumeRoleWithWebIdentity" Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref IdentityPool "ForAnyValue:StringLike": "cognito-identity.amazonaws.com:amr": authenticated Policies: - PolicyName: "CognitoAuthorizedPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "cognito-sync:*" Resource: !Join [ "", [ "arn:aws:cognito-sync:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":identitypool/", !Ref IdentityPool] ] - Effect: Allow Action: - iot:Connect Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":client/serverlesspresso-*" ] ] - Effect: Allow Action: - iot:Subscribe Resource: "*" - Effect: Allow Action: - iot:Receive Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":topic/*" ] ] # Assigns the roles to the Identity Pool IdentityPoolRoleMapping: Type: "AWS::Cognito::IdentityPoolRoleAttachment" Properties: IdentityPoolId: !Ref IdentityPool Roles: authenticated: !GetAtt CognitoAuthorizedRole.Arn unauthenticated: !GetAtt CognitoUnAuthorizedRole.Arn CountingTable: Type: AWS::DynamoDB::Table Properties: TableName: serverlesspresso-counting-table AttributeDefinitions: - AttributeName: PK AttributeType: S KeySchema: - AttributeName: PK KeyType: HASH BillingMode: PAY_PER_REQUEST ########################################## # Publisher service # ########################################## PublisherFunctionAdmin: Type: AWS::Serverless::Function Description: Serverlesspresso Publisher. Invoked by EventBridge rule Properties: CodeUri: ../backends/6-publisher-service/code/ Handler: publishToIOT.handler Runtime: nodejs14.x Timeout: 3 Environment: Variables: IOT_DATA_ENDPOINT: !GetAtt IotEndpoint.IotEndpointAddress IOT_TOPIC: 'serverlesspresso-admin' Policies: - Statement: - Effect: Allow Resource: 'arn:aws:iot:*' Action: - iot:Connect - iot:Publish Events: Trigger: Type: EventBridgeRule Properties: EventBusName: "Serverlesspresso" Pattern: source: - !Ref Source detail-type: - prefix: 'OrderManager.' - prefix: 'OrderProcessor.' - prefix: 'Validator.' - prefix: 'ConfigService.' PublisherFunctionUser: Type: AWS::Serverless::Function Description: Serverlesspresso Publisher. Invoked by EventBridge rule Properties: CodeUri: ../backends/6-publisher-service/code/ Handler: publishToIOTuserTopic.handler Runtime: nodejs14.x Timeout: 3 Environment: Variables: IOT_DATA_ENDPOINT: !GetAtt IotEndpoint.IotEndpointAddress IOT_TOPIC: 'serverlesspresso-user-' Policies: - Statement: - Effect: Allow Resource: 'arn:aws:iot:*' Action: - iot:Connect - iot:Publish Events: OrderManagerEvents: Type: EventBridgeRule Properties: EventBusName: "Serverlesspresso" Pattern: source: - !Ref Source detail-type: - prefix: 'OrderManager.' OrderProcessorEvents: Type: EventBridgeRule Properties: EventBusName: "Serverlesspresso" Pattern: source: - !Ref Source detail-type: - prefix: 'OrderProcessor.' PublisherFunctionConfig: Type: AWS::Serverless::Function Description: Serverlesspresso Publisher. Invoked by EventBridge rule Properties: CodeUri: ../backends/6-publisher-service/code/ Handler: publishToIOT.handler Runtime: nodejs14.x Timeout: 3 Environment: Variables: IOT_DATA_ENDPOINT: !GetAtt IotEndpoint.IotEndpointAddress IOT_TOPIC: 'serverlesspresso-config' Policies: - Statement: - Effect: Allow Resource: 'arn:aws:iot:*' Action: - iot:Connect - iot:Publish Events: TriggerValidator: Type: EventBridgeRule Properties: EventBusName: "Serverlesspresso" Pattern: source: - !Ref Source detail-type: - prefix: 'ConfigService.' ########################################## # Config service # ########################################## ConfigTable: Type: AWS::DynamoDB::Table Properties: TableName: serverlesspresso-config-table AttributeDefinitions: - AttributeName: PK AttributeType: S KeySchema: - AttributeName: PK KeyType: HASH BillingMode: PAY_PER_REQUEST StreamSpecification: StreamViewType: NEW_AND_OLD_IMAGES RESTApConfigService: Type: AWS::Serverless::Api Properties: StageName: Prod DefinitionBody: 'Fn::Transform': Name: 'AWS::Include' Parameters: Location: '../backends/2-config-service/RestAPIs/api.yaml' ConfigChangedFunction: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/2-config-service/code Handler: configChanged.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 Environment: Variables: EventBusName: !Ref ServerlesspressoEventBus Source: !Ref Source Policies: - DynamoDBReadPolicy: TableName: !Ref ConfigTable - EventBridgePutEventsPolicy: EventBusName: !Ref ServerlesspressoEventBus Events: Stream: Type: DynamoDB Properties: Stream: !GetAtt ConfigTable.StreamArn BatchSize: 1 StartingPosition: TRIM_HORIZON # StoreFunction: # This allows teh store to be opened or closed, this is a direct integration from APIGW->DynamoDB # eg. PUT /Store?state=false # GetConfigFunction: # Retries all items in the config table # eg. GET /config # GetAdminConfigFunction: # Retries all items in the config table # eg. GET /admin-config ########################################################################## # Config service API Role # ########################################################################## ConfigRESTApiRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - apigateway.amazonaws.com Action: - 'sts:AssumeRole' Policies: - PolicyName: DynamoDBinteraction PolicyDocument: Version: 2012-10-17 Statement: - Effect: "Allow" Action: - dynamodb:Scan - dynamodb:GetItem - dynamodb:UpdateItem Resource: !GetAtt ConfigTable.Arn - Effect: "Allow" Action: - dynamodb:Query Resource: Fn::Sub: - ${tableArn}${index} - {tableArn: !GetAtt ConfigTable.Arn, index: "/index/*"} ########################################## # < / END Config service > # ########################################## ########################################## # Validator Service # ########################################## GetQRcodeFunction: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/9-validator/code Handler: getCode.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 Environment: Variables: AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1 TableName: !Ref ValidatorTable TimeInterval: !Ref TimeInterval CodeLength: !Ref CodeLength TokensPerBucket: !Ref TokensPerBucket BusName: !Ref ServerlesspressoEventBus Source: !Ref Source Policies: - DynamoDBCrudPolicy: TableName: !Ref ValidatorTable Events: GetQrCode: Type: Api Properties: RestApiId: !Ref RESTApiValidatorService Path: /qr-code Method: get Auth: Authorizer: MyCognitoAuthorizor AuthorizationScopes: - aws.cognito.signin.user.admin VerifyQRcodeFunction: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/9-validator/code Handler: verifyCode.handler Runtime: nodejs14.x Timeout: 10 MemorySize: 128 Environment: Variables: AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1 TableName: !Ref ValidatorTable TimeInterval: !Ref TimeInterval CodeLength: !Ref CodeLength TokensPerBucket: !Ref TokensPerBucket BusName: !Ref ServerlesspressoEventBus Source: !Ref Source Policies: - EventBridgePutEventsPolicy: EventBusName: !Ref ServerlesspressoEventBus - DynamoDBCrudPolicy: TableName: !Ref ValidatorTable Events: GetQrCode: Type: Api Properties: RestApiId: !Ref RESTApiValidatorService Path: /qr-code Method: POST Auth: Authorizer: MyCognitoAuthorizor AuthorizationScopes: - aws.cognito.signin.user.admin RESTApiValidatorService: Type: AWS::Serverless::Api Name: RESTApiValidatorService Properties: StageName: Prod Cors: AllowOrigin: "'*'" AllowHeaders: "'*'" AllowMethods: "'GET,POST,OPTIONS'" Auth: DefaultAuthorizer: MyCognitoAuthorizor # OPTIONAL AddDefaultAuthorizerToCorsPreflight: FALSE Authorizers: MyCognitoAuthorizor: # Can also accept an array UserPoolArn: !GetAtt UserPool.Arn # Validator API Role # ## DynamoDB table ValidatorTable: Type: AWS::DynamoDB::Table Properties: TableName: serverlesspresso-validator-table AttributeDefinitions: - AttributeName: PK AttributeType: N KeySchema: - AttributeName: PK KeyType: HASH BillingMode: PAY_PER_REQUEST StreamSpecification: StreamViewType: NEW_AND_OLD_IMAGES ############################################# # < /END Validator service > # ############################################# # Lambda function for initializing DDB table content FillDatabasesCustomResource: Type: AWS::Serverless::Function Properties: CodeUri: initDB Handler: app.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 Environment: Variables: configTable: !Ref ConfigTable countingTable: !Ref CountingTable Policies: - DynamoDBCrudPolicy: TableName: !Ref ConfigTable - DynamoDBCrudPolicy: TableName: !Ref CountingTable #Triggers Lambda function after stack creation DeploymentCustomResourcePrefil: Type: AWS::CloudFormation::CustomResource Properties: ServiceToken: !GetAtt FillDatabasesCustomResource.Arn ########################################################################## # STEP FUNCTION LOG GROUP # ########################################################################## StateMachineLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Join [ "/", [ "stepfunctions", StateMachineExpressSync]] ############################################# # < Order Manager service > # ############################################# # Order manager Lambda Functions ## #The function that first adds an iteom to the order DB.. Student will ad teh event to trigger this ValidatorNewOrderFunction: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/5-order-manager/functions/newOrder Handler: app.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 Environment: Variables: TableName: !Ref OrderTable Policies: - DynamoDBWritePolicy: TableName: !Ref OrderTable SanitizeOrderLambda: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/5-order-manager/functions/sanitize Handler: app.handler Runtime: nodejs14.x Timeout: 20 MemorySize: 128 GetOrderByIdFunction: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/5-order-manager/functions/getById Handler: app.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 WorkFlowStarted: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/5-order-manager/functions/workflowStarted Handler: app.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 Environment: Variables: TableName: !Ref OrderTable Policies: - DynamoDBWritePolicy: TableName: !Ref OrderTable WaitingCompletion: Type: AWS::Serverless::Function Properties: CodeUri: ../backends/5-order-manager/functions/waitingCompletion Handler: app.handler Runtime: nodejs14.x Timeout: 15 MemorySize: 128 Environment: Variables: TableName: !Ref OrderTable BusName: !Ref ServerlesspressoEventBus Source: !Ref Source Policies: - DynamoDBWritePolicy: TableName: !Ref OrderTable - EventBridgePutEventsPolicy: EventBusName: !Ref ServerlesspressoEventBus # Order Table - DynamoDB # OrderTable: Type: AWS::DynamoDB::Table Properties: TableName: serverlesspresso-order-table AttributeDefinitions: - AttributeName: PK AttributeType: S - AttributeName: SK AttributeType: S - AttributeName: TS AttributeType: N - AttributeName: ORDERSTATE AttributeType: S - AttributeName: USERID AttributeType: S KeySchema: - AttributeName: PK KeyType: HASH - AttributeName: SK KeyType: RANGE LocalSecondaryIndexes: - IndexName: LSI-timestamp KeySchema: - AttributeName: PK KeyType: HASH - AttributeName: TS KeyType: RANGE Projection: ProjectionType: ALL GlobalSecondaryIndexes: - IndexName: GSI-status KeySchema: - AttributeName: ORDERSTATE KeyType: HASH - AttributeName: SK KeyType: RANGE Projection: ProjectionType: ALL - IndexName: GSI-userId KeySchema: - AttributeName: USERID KeyType: HASH - AttributeName: SK KeyType: RANGE Projection: ProjectionType: ALL BillingMode: PAY_PER_REQUEST StreamSpecification: StreamViewType: NEW_AND_OLD_IMAGES # Order Manager REST API # RESTApiForOrderManager: Type: AWS::Serverless::Api Properties: StageName: Prod DefinitionBody: 'Fn::Transform': Name: 'AWS::Include' Parameters: Location: '../backends/5-order-manager/RestAPIs/api.yaml' # Order Manager API Role # RESTApiRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - apigateway.amazonaws.com Action: - 'sts:AssumeRole' Policies: - PolicyName: DynamoDBinteraction PolicyDocument: Version: 2012-10-17 Statement: - Effect: "Allow" Action: - states:StartExecution Resource: !GetAtt 02OrderManagerStateMachine.Arn - Effect: "Allow" Action: - dynamodb:GetItem Resource: !GetAtt OrderTable.Arn - Effect: "Allow" Action: - dynamodb:Query Resource: Fn::Sub: - ${tableArn}${index} - {tableArn: !GetAtt OrderTable.Arn, index: "/index/*"} # Order Manager SFN # 02OrderManagerStateMachine: Type: AWS::Serverless::StateMachine # More info about State Machine Resource: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-statemachine.html Properties: DefinitionUri: ../backends/5-order-manager/statemachine/om.asl.json DefinitionSubstitutions: OMTable: !Ref OrderTable ConfigTable: !Ref ConfigTable SanitizeOrderLambda: !Ref SanitizeOrderLambda Policies: # Find out more about SAM policy templates: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-policy-templates.html - EventBridgePutEventsPolicy: EventBusName: !Ref ServerlesspressoEventBus - LambdaInvokePolicy: FunctionName: !Ref SanitizeOrderLambda - DynamoDBReadPolicy: TableName: !Ref ConfigTable - DynamoDBWritePolicy: TableName: !Ref OrderTable - Statement: - Sid: SendTaskSuccess Effect: Allow Action: - states:SendTaskSuccess Resource: '*' ############################################# # < /Order Manager service > # ############################################# ########################################## # Order Processor IAM ROLE # ########################################## 01OrderProcessorRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "states.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" RolePolicies: Type: "AWS::IAM::Policy" Properties: PolicyName: "orderProcessorWFPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: "events:PutEvents" Resource: !GetAtt ServerlesspressoEventBus.Arn - Effect: "Allow" Action: "dynamodb:GetItem" Resource: !GetAtt ConfigTable.Arn - Effect: "Allow" Action: "dynamodb:UpdateItem" Resource: !GetAtt ConfigTable.Arn - Effect: "Allow" Action: "dynamodb:UpdateItem" Resource: !GetAtt CountingTable.Arn - Effect: "Allow" Action: "states:ListExecutions" Resource: !Join [ "", [ "arn:aws:states:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":stateMachine:", "*"] ] Roles: - Ref: "01OrderProcessorRole" ########################################## # Outputs # ########################################## Outputs: DisplayAppURI: Description: The URL for the Display App Value: !Sub - "https://workshop-display.serverlesscoffee.com/?region=${AWS::Region}&userPoolId=${UserPoolID}&userPoolWebClientId=${UserPoolWebClientId}&orderManagerEndpoint=${OrderManagerEndpoint}&APIGWEndpointValidatorService=${APIGWEndpointValidatorService}&APIGWEndpointConfigService=${APIGWEndpointConfigService}" - UserPoolID: !Ref UserPool UserPoolWebClientId: !Ref UserPoolClient OrderManagerEndpoint: !Sub "https://${RESTApiForOrderManager}.execute-api.${AWS::Region}.amazonaws.com/Prod/" APIGWEndpointValidatorService: !Sub "https://${RESTApiValidatorService}.execute-api.${AWS::Region}.amazonaws.com/Prod/" APIGWEndpointConfigService: !Sub "https://${RESTApConfigService}.execute-api.${AWS::Region}.amazonaws.com/Prod/" # UserPoolID: # Description: UserPool ID # Value: !Ref UserPool # UserPoolWebClientId: # Description: UserPool Web Client ID # Value: !Ref UserPoolClient # OrderManagerEndpoint: # Description: "API Prod stage endpoint" # Value: !Sub "https://${RESTApiForOrderManager}.execute-api.${AWS::Region}.amazonaws.com/Prod/" # APIGWEndpointValidatorService: # Description: "API Prod stage endpoint" # Value: !Sub "https://${RESTApiValidatorService}.execute-api.${AWS::Region}.amazonaws.com/Prod/" # APIGWEndpointConfigService: # Description: "API Prod stage endpoint" # Value: !Sub "https://${RESTApConfigService}.execute-api.${AWS::Region}.amazonaws.com/Prod/" # ConfigTable: # Value: !Ref ConfigTable # Description: ConfigTable # CountingTable: # Value: !Ref CountingTable # Description: Counting Table # CoreEventBusName: # Description: CoreEventBus Name # Value: !Ref ServerlesspressoEventBus # SanitizeOrderLambda: # Description: Sanitize Order Lambda Function # Value: !GetAtt SanitizeOrderLambda.Arn # CoreEventBusARN: # Description: CoreEventBus ARN # Value: !GetAtt ServerlesspressoEventBus.Arn # IoTRealtimeName: # Description: IoTRealtime Name # Value: !Ref ServerlesspressoRealtime # IotEndpointAddress: # Description: IotEndpointAddress URL # Value: !GetAtt IotEndpoint.IotEndpointAddress # Export: # Name: !Sub "${AWS::StackName}-IotEndpointAddress"