--- # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AWSTemplateFormatVersion: "2010-09-09" Description: | This template creates a sample web application using Amazon API Gateway and AWS Lambda to display the client IP address. It also uses conditionals to deploy an AWS Service Catalog AppRegistry application in what we are calling the "test" account and shares it with the "prod" account. **WARNING** This template creates an Amazon API Gateway, AWS Lambda function and related resources. You will be billed for the AWS resources used if you create a stack from this template. Parameters: ApplicationName: Type: String Description: AWS Service Catalog AppRegistry application name Default: AppRegistrySampleApp APIGatewayName: Type: String Description: Name of the Amazon API Gateway Default: APIGWHelloIP APIGatewayStageName: Type: String Description: Name of the Amazon API Gateway stage of deployment AllowedPattern: "[a-z0-9]+" IsTestAccountFlag: Type: String Description: Flag to indicate if this is the test account. This determines what to deploy and how to share the application. AllowedValues: - "Yes" - "No" LambdaFunctionName: Type: String Description: Name of the AWS Lambda function AllowedPattern: "[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+" Default: LambdaHelloIP ProdAccountID: Type: String Description: 12 digit AWS Account ID for the prod account AllowedPattern: "^[0-9]{12}$" TestAccountID: Type: String Description: 12 digit AWS Account ID for the test account AllowedPattern: "^[0-9]{12}$" Conditions: IsTestAccount: !Equals [ !Ref IsTestAccountFlag, "Yes" ] NotTestAccount: !Not [ !Equals [ !Ref IsTestAccountFlag, "Yes" ] ] Resources: APIGateway: Type: AWS::ApiGateway::RestApi Properties: Description: Example Amazon API Gateway serving up AWS Lambda provided HTML content EndpointConfiguration: Types: - REGIONAL Name: !Ref APIGatewayName APIGatewayDeployment: Type: AWS::ApiGateway::Deployment DependsOn: APIGatewayMethod Properties: RestApiId: !Ref APIGateway APIGatewayMethod: Type: AWS::ApiGateway::Method DependsOn: - LambdaFunction Properties: AuthorizationType: NONE HttpMethod: GET Integration: IntegrationHttpMethod: POST Type: AWS_PROXY Uri: !Sub arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaFunction.Arn}/invocations ResourceId: !GetAtt APIGateway.RootResourceId RestApiId: !Ref APIGateway APIGatewayStage: Type: AWS::ApiGateway::Stage Properties: DeploymentId: !Ref APIGatewayDeployment MethodSettings: - ResourcePath: "/*" HttpMethod: "*" LoggingLevel: INFO RestApiId: !Ref APIGateway StageName: !Ref APIGatewayStageName APIGatewayUsagePlan: Type: AWS::ApiGateway::UsagePlan Properties: ApiStages: - ApiId: !Ref APIGateway Stage: !Ref APIGatewayStage Description: Usage plan for the API Gateway Quota: Limit: 100 Period: DAY Throttle: BurstLimit: 5 RateLimit: 5 Application: Type: AWS::ServiceCatalogAppRegistry::Application # Only create a new application if this the test account Condition: IsTestAccount Properties: Description: What is my IP address Name: !Ref ApplicationName AppRegistryApplicationAttributeAssociation: # Associate the attribute group with the AppRegistry application Type: AWS::ServiceCatalogAppRegistry::AttributeGroupAssociation Properties: Application: !Ref ApplicationName AttributeGroup: Fn::GetAtt: - AttributeGroup - Id AppRegistryApplicationStackAssociationProd: # Associate this stack with the AppRegistry application Condition: NotTestAccount Type: AWS::ServiceCatalogAppRegistry::ResourceAssociation Properties: Application: !Ref ApplicationName Resource: !Sub ${AWS::StackId} ResourceType: CFN_STACK AppRegistryApplicationStackAssociationTest: # Associate this stack with the AppRegistry application Condition: IsTestAccount DependsOn: - Application Type: AWS::ServiceCatalogAppRegistry::ResourceAssociation Properties: Application: !Ref ApplicationName Resource: !Sub ${AWS::StackId} ResourceType: CFN_STACK AppResourceShare: # Share the application with the prod account Type: "AWS::RAM::ResourceShare" # Only share the application if this is the primary account Condition: IsTestAccount Properties: Name: !Sub "${AWS::StackName}-AppResourceShare" PermissionArns: - !Sub arn:aws:ram::${AWS::Partition}:permission/AWSRAMPermissionServiceCatalogAppRegistryApplicationAllowAssociation ResourceArns: - Fn::GetAtt: - Application - Arn Principals: - !Sub ${ProdAccountID} AttributeGroup: # Create a sample attribute group Type: AWS::ServiceCatalogAppRegistry::AttributeGroup Properties: Name: !Sub "${ApplicationName}-${APIGatewayStageName}" Description: !Sub "Meta data for the ${ApplicationName} application in the ${APIGatewayStageName} environment" Attributes: { "ApplicationType" : "Web Service", "Author" : "AWS", "ComplianceCategory" : "Low", "Email contact": !Sub "${APIGatewayStageName}-development-team@example.com", "Gateway URL" : !Sub "https://${APIGateway}.execute-api.${AWS::Region}.amazonaws.com/${APIGatewayStageName}", "Region" : !Sub "${AWS::Region}", "SecurityCategory" : "Low", "Stack Id": !Sub "${AWS::StackId}", "Stack Name": !Sub "${AWS::StackName}", "Team" : !Sub "${APIGatewayStageName} development team" } AttributeGroupShare: # Shares the attribute group with the other account Type: "AWS::RAM::ResourceShare" Properties: Name: !Sub "${AWS::StackName}-AttributeGroupShare" PermissionArns: - !Sub arn:aws:ram::${AWS::Partition}:permission/AWSRAMPermissionServiceCatalogAppRegistryAttributeGroupReadOnly ResourceArns: - Fn::GetAtt: - AttributeGroup - Arn Principals: - !If [IsTestAccount, !Ref ProdAccountID, !Ref TestAccountID] LambdaFunction: # Python Lambda function that returns the client IP address. Type: AWS::Lambda::Function DependsOn: - LambdaLogGroup Properties: Code: ZipFile: !Sub | def handler(event,context): print("Context: {0}".format(context)) html_text = '' html_text += '
' html_text += '' html_text += '' html_text += '' html_text += '' html_text += '' html_text += 'This page is served from your ${APIGatewayStageName} environment' html_text += '
' html_text += '' html_text += '' html_text += '' return { 'body': html_text, 'headers': { 'Content-Type': 'text/html' }, 'statusCode': 200 } Description: Example AWS Lambda function that returns client IP formatted as HTML FunctionName: !Ref LambdaFunctionName Handler: index.handler MemorySize: 128 Role: !GetAtt LambdaIAMRole.Arn Runtime: python3.9 LambdaIAMRole: # Role for the Lambda function. Only needs permissions to write logs to the log group Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Policies: - PolicyDocument: Version: 2012-10-17 Statement: - Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Effect: Allow Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${LambdaFunctionName}:* PolicyName: lambda LambdaLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${LambdaFunctionName} RetentionInDays: 30 LambdaPermission: # This is required to allow API Gateway to invoke the Lambda function Type: AWS::Lambda::Permission DependsOn: LambdaFunction Properties: Action: lambda:InvokeFunction FunctionName: !Ref LambdaFunctionName Principal: apigateway.amazonaws.com SourceArn: !Sub arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${APIGateway}/*/GET/* Outputs: APIGatewayInvokeURL: Value: !Sub "https://${APIGateway}.execute-api.${AWS::Region}.amazonaws.com/${APIGatewayStageName}"