# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # 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. # This template is used to create the resources required for the AML .NET Modernization workshop. # The template can be executed standalone by providing network configurations or can be run as a nested template using the Event Engine driver template. --- AWSTemplateFormatVersion: "2010-09-09" Description: .NET modernization workshop template. Creates resources required for the AML Unishop Workshop. Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Network configurations. These are required parameters." Parameters: - "VPCId" - "SubnetIdList" - "VSSecurityGroupId" - Label: default: "MSSQL Instance configurations." Parameters: - "InstanceIdentifier" - "MasterUsername" - "MasterUserPassword" - Label: default: "Aurora Instance configurations." Parameters: - "AuroraDbName" - "AuroraUsername" - "AuroraPassword" - "AuroraDbClusterName" - "AuroraDbInstanceName1" - Label: default: "Elastic Beanstalk configurations." Parameters: - "ApplicationName" - "SourceS3Bucket" - "SourceS3BucketKey" - "EnvironmentName" - Label: default: "Elastic Beanstalk Locust App configurations." Parameters: - "LocustApplicationName" - "LocustSourceS3Bucket" - "LocustSourceS3BucketKey" - "LocustEnvironmentName" - Label: default: "Elastic Container Service configurations." Parameters: - "ClusterName" - Label: default: "Hosted Zone configurations." Parameters: - "HostedZone" Parameters: InstanceIdentifier: Type: String Description: "Name of the MSSQL Instance" Default: "unishop" MasterUsername: Type: String Description: "MSSQL connection user name" Default: "admin" MasterUserPassword: NoEcho: True Type: String Description: "MSSQL connection user password" Default: "dMdLgX6sZoXmOU2rnWTS" # ----------------------------------- ApplicationName: Type: String Description: "Elastic Beanstalk Application Name" Default: "LegacyApplication" SourceS3Bucket: Type: String Description: "Bucket name of the application code" Default: "unishop-dotnet" SourceS3BucketKey: Type: String Description: "Object key of the application source code" Default: "UnishopLegacy.zip" EnvironmentName: Type: String Description: "Elastic Beanstalk Environment name" Default: "LegacyApplication" LocustApplicationName: Type: String Description: "Elastic Beanstalk Application Name" Default: "LoadGenerator" LocustSourceS3Bucket: Type: String Description: "Bucket name of the application code" Default: "unishop-dotnet" LocustSourceS3BucketKey: Type: String Description: "Object key of the application source code" Default: "locusttest.zip" LocustEnvironmentName: Type: String Description: "Elastic Beanstalk Environment name" Default: "LoadGenerator" # ----------------------------------- ClusterName: Type: String Description: "Elastic Container Service cluster name" Default: "UnishopCluster" # ----------------------------------- AuroraUsername: Description: Username for Aurora DB Type: String Default: postgres AuroraPassword: NoEcho: True Description: UserPassword for Aurora DB Type: String Default: mYRyeT1IJamnWxAYowyR AuroraDbClusterName: Description: Name of the Aurora Cluster Type: String Default: inventorydb AuroraDbInstanceName1: Description: Name of the primary Aurora database instance Type: String Default: instance1 AuroraDbName: Description: Name of the database in Aurora Cluster Type: String Default: inventorydb # ----------------------------------- HostedZone: Type: String Description: "Domain name for the hosted resources" Default: "unishop.local" # ----------------------------------- VpcId: Type: String Description: "VPC to deploy the resources" Default: "" SubnetIdList: Type: CommaDelimitedList Description: "Subnet Id to deploy the instance" Default: "" VSSecurityGroupId: Type: String Description: "The Security Group of the EC2 Dev Instance" Default: "" # ----------------------------------- StaticHostingBucketName: Type: String Description: "Name of the bucket used to host static UI content" Default: "unishopui" Resources: # Elastic Beanstalk resources. # Deploy the monolithic application in a Windows Server instance. # Securtiy groups for resource. EBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "Security Group to allow EB aplication to accept HTTP requests" SecurityGroupIngress: - IpProtocol: 'tcp' FromPort: 80 ToPort: 80 CidrIp: '0.0.0.0/0' SecurityGroupEgress: - IpProtocol: -1 CidrIp: '0.0.0.0/0' VpcId: !Ref VpcId # IAM roles to provide permissions. EBEC2Role: Type: AWS::IAM::Role Properties: RoleName: 'elastic-beanstalk-ec2-modernization-workshop-role' Description: "This role enables EC2 configuration" Path: '/' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier' - 'arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker' - 'arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier' AssumeRolePolicyDocument: Version: "2008-10-17" Statement: - Effect: "Allow" Principal: Service: - 'ec2.amazonaws.com' Action: "sts:AssumeRole" # Service role to provide permissions. EBServiceRole: Type: AWS::IAM::Role Properties: RoleName: 'elastic-beanstalk-service-modernization-workshop-role' Description: "This role enables EB Service configuration" Path: '/' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkEnhancedHealth' - 'arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkService' AssumeRolePolicyDocument: Version: "2008-10-17" Statement: - Effect: "Allow" Principal: Service: - 'elasticbeanstalk.amazonaws.com' Action: "sts:AssumeRole" #Instance profile for the EB role. EBInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - Ref: EBEC2Role # Elastic Beanstalk Application. EBApp: Type: AWS::ElasticBeanstalk::Application Properties: ApplicationName: !Ref ApplicationName Description: "AWS Elastic Beanstalk Legacy .NET Framework application" # Elastic Beanstalk Application version. Source code pulled from S3 bucket. EBAppVersion: Type: AWS::ElasticBeanstalk::ApplicationVersion Properties: ApplicationName: Ref: "EBApp" Description: "AWS Elastic Beanstalk Legacy .NET Framework application version" SourceBundle: S3Bucket: !Join [ '-', [!Ref SourceS3Bucket, !Ref 'AWS::Region']] S3Key: !Ref SourceS3BucketKey # Elastic Beanstalk Application config template. Specifies the platform. EBConfig: Type: AWS::ElasticBeanstalk::ConfigurationTemplate Properties: ApplicationName: Ref: "EBApp" Description: "AWS Elastic Beanstalk Legacy .NET Framework configuration template" PlatformArn: !Join [':', ["arn:aws:elasticbeanstalk", !Ref 'AWS::Region', ":platform/IIS 10.0 running on 64bit Windows Server 2019"]] # Elastic Beanstalk Monolithic Application environment. EBEnvironment: Type: AWS::ElasticBeanstalk::Environment DependsOn: - MSSQLDatabaseInstance - RDSDNSRecord Properties: ApplicationName: Ref: "EBApp" Description: "AWS Elastic Beanstalk Environment running Legacy .NET Framework" EnvironmentName: !Ref EnvironmentName TemplateName: Ref: "EBConfig" OptionSettings: - Namespace: 'aws:elasticbeanstalk:environment' OptionName: 'EnvironmentType' Value: 'SingleInstance' - Namespace: 'aws:elasticbeanstalk:environment' OptionName: 'ServiceRole' Value: !Ref EBServiceRole - Namespace: 'aws:autoscaling:launchconfiguration' OptionName: "IamInstanceProfile" Value: !Ref EBInstanceProfile - Namespace: 'aws:ec2:vpc' OptionName: "AssociatePublicIpAddress" Value: true - Namespace: 'aws:ec2:vpc' OptionName: 'VpcId' Value: !Ref VpcId - Namespace: 'aws:ec2:vpc' OptionName: 'Subnets' Value: !Select [0, !Ref SubnetIdList] - Namespace: 'aws:autoscaling:launchconfiguration' OptionName: "SecurityGroups" Value: !GetAtt EBSecurityGroup.GroupId Tier: Name: "WebServer" Type: "Standard" VersionLabel: Ref : "EBAppVersion" # Deploy Locust load testing application in Amazon Linux instance with Docker support. # Elastic Beanstalk Application. EBLocustApp: Type: AWS::ElasticBeanstalk::Application Properties: ApplicationName: !Ref LocustApplicationName Description: "AWS Elastic Beanstalk Docker Locust application" # Elastic Beanstalk Application version. Source code pulled from S3 bucket. EBLocustAppVersion: Type: AWS::ElasticBeanstalk::ApplicationVersion Properties: ApplicationName: Ref: "EBLocustApp" Description: "AWS Elastic Beanstalk Docker Locust application version" SourceBundle: S3Bucket: !Join [ '-', [!Ref LocustSourceS3Bucket, !Ref 'AWS::Region']] S3Key: !Ref LocustSourceS3BucketKey # Elastic Beanstalk Application config template. Specifies the platform. EBLocustConfig: Type: AWS::ElasticBeanstalk::ConfigurationTemplate Properties: ApplicationName: Ref: "EBLocustApp" Description: "AWS Elastic Beanstalk Docker Locust configuration template" PlatformArn: !Join [':', ["arn:aws:elasticbeanstalk", !Ref 'AWS::Region', ":platform/Docker running on 64bit Amazon Linux"]] # Elastic Beanstalk locust application environment. EBLocustEnvironment: Type: AWS::ElasticBeanstalk::Environment Properties: ApplicationName: Ref: "EBLocustApp" Description: "AWS Elastic Beanstalk Environment running Docker Locust" EnvironmentName: !Ref LocustEnvironmentName TemplateName: Ref: "EBLocustConfig" OptionSettings: - Namespace: 'aws:elasticbeanstalk:environment' OptionName: 'EnvironmentType' Value: 'SingleInstance' - Namespace: 'aws:elasticbeanstalk:environment' OptionName: 'ServiceRole' Value: !Ref EBServiceRole - Namespace: 'aws:autoscaling:launchconfiguration' OptionName: "IamInstanceProfile" Value: !Ref EBInstanceProfile - Namespace: 'aws:ec2:vpc' OptionName: "AssociatePublicIpAddress" Value: false - Namespace: 'aws:ec2:vpc' OptionName: 'VpcId' Value: !Ref VpcId - Namespace: 'aws:ec2:vpc' OptionName: 'Subnets' Value: !Select [0, !Ref SubnetIdList] Tier: Name: "WebServer" Type: "Standard" VersionLabel: Ref : "EBLocustAppVersion" # Database resources. # RDS MSSQL Database to store Legacy application data. # MSSQL Database security group. Allow connections from the Monolithic application, VS Dev instance and DMS resources. MSSQLVPCSecurityGroup: Type: AWS::EC2::SecurityGroup DependsOn: - DMSSecurityGroup Properties: GroupDescription: "Security Group to allow applications to connect to RDS Instance" SecurityGroupIngress: - IpProtocol: 'tcp' FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !GetAtt EBSecurityGroup.GroupId - IpProtocol: 'tcp' FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !Ref VSSecurityGroupId - IpProtocol: 'tcp' FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !GetAtt DMSSecurityGroup.GroupId - IpProtocol: 'tcp' FromPort: 1433 ToPort: 1433 SourceSecurityGroupId: !Ref ECSContainerSecurityGroup SecurityGroupEgress: - IpProtocol: -1 CidrIp: '0.0.0.0/0' VpcId: !Ref VpcId # MSSQL Database subnet. MSSQLDBSubnet: Type: 'AWS::RDS::DBSubnetGroup' Properties: DBSubnetGroupDescription: "DB Subnet Group to create RDS in a VPC" DBSubnetGroupName: "UnishopSubnetGroup" SubnetIds: !Ref SubnetIdList # MSSQL Database. SQL Express server hosted in the specified subnet. MSSQLDatabaseInstance: Type: 'AWS::RDS::DBInstance' Properties: DBInstanceIdentifier: !Ref InstanceIdentifier DBInstanceClass: db.t2.micro AllocatedStorage: 20 Engine: sqlserver-ex EngineVersion: 14.00.3281.6.v1 LicenseModel: license-included MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword DBSubnetGroupName: !Ref MSSQLDBSubnet VPCSecurityGroups: - !GetAtt MSSQLVPCSecurityGroup.GroupId # RDS Aurora database to store inventory information after database migration and service stragling. # Aurora security group. Allow connections from the ECS, VS Dev instance and DMS resources. AuroraSecurityGroup: Type: AWS::EC2::SecurityGroup DependsOn: - DMSSecurityGroup Properties: GroupDescription: "Security Group to allow applications in ECS and VS dev machine to connect to Aurora Database" SecurityGroupIngress: - IpProtocol: 'tcp' FromPort: 5432 ToPort: 5432 SourceSecurityGroupId: !Ref ECSContainerSecurityGroup - IpProtocol: 'tcp' FromPort: 5432 ToPort: 5432 SourceSecurityGroupId: !Ref VSSecurityGroupId - IpProtocol: 'tcp' FromPort: 5432 ToPort: 5432 SourceSecurityGroupId: !GetAtt DMSSecurityGroup.GroupId SecurityGroupEgress: - IpProtocol: -1 CidrIp: '0.0.0.0/0' VpcId: !Ref VpcId # Aurora Database subnet. AuroraSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: CloudFormation managed Aurora DB subnet group. SubnetIds: !Ref SubnetIdList # Aurora database cluster configuration. AuroraCluster: Type: AWS::RDS::DBCluster Properties: Engine: aurora-postgresql EngineVersion: '11.6' DBClusterIdentifier: !Ref AuroraDbClusterName DatabaseName: !Ref AuroraDbName DBClusterParameterGroupName: default.aurora-postgresql11 MasterUsername: !Ref AuroraUsername MasterUserPassword: !Ref AuroraPassword Port: 5432 DBSubnetGroupName: !Ref AuroraSubnetGroup VpcSecurityGroupIds: [!Ref AuroraSecurityGroup] # Aurora databse instance. Hosts a postgresql server. AuroraDatabasePrimaryInstance: Type: AWS::RDS::DBInstance DependsOn: [AuroraSubnetGroup, AuroraCluster] Properties: Engine: aurora-postgresql DBClusterIdentifier: !Ref AuroraCluster DBInstanceIdentifier: !Ref AuroraDbInstanceName1 DBInstanceClass: db.t3.medium DBSubnetGroupName: !Ref AuroraSubnetGroup # Container resources. # These include resources that are required to deploy and run containers on Amazon ECS. # ECR Repository to store the microservice images. ECRRepository: Type: 'AWS::ECR::Repository' Properties: RepositoryName: "unishop-repository" # ECS Cluster to run services and tasks. ECSCluster: Type: 'AWS::ECS::Cluster' Properties: ClusterName: !Ref ClusterName ClusterSettings: - Name: containerInsights Value: enabled # ECS TaskExecution role, allow ECS to pull image and store logs. ECSTaskExecutionRole: Type: AWS::IAM::Role Properties: RoleName: 'ecs-task-execution-modernization-workshop-role' AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - "ecs-tasks.amazonaws.com" Action: - "sts:AssumeRole" ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy' # ECS Task role (an empty role, user can attach policies if needed). # The role used by the task itself to use other services. ECSTaskRole: Type: AWS::IAM::Role Properties: RoleName: 'ecs-task-modernization-workshop-role' AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - "ecs-tasks.amazonaws.com" Action: - "sts:AssumeRole" ManagedPolicyArns: - 'arn:aws:iam::aws:policy/AmazonSNSFullAccess' - 'arn:aws:iam::aws:policy/AmazonRekognitionFullAccess' - 'arn:aws:iam::aws:policy/AmazonS3FullAccess' # Security group for ECS tasks/service. ECSContainerSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: VpcId: !Ref VpcId GroupDescription: Security group for ECS tasks/service GroupName: ECSClusterSecurityGroup # DMS related resources. # Database migration is done from MS SQL to Aurora postgressql server. # IAM role to provide permissions for DMS. DMSVPCRole: Type: AWS::IAM::Role Properties: RoleName: 'dms-vpc-role' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AmazonDMSVPCManagementRole' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - "dms.amazonaws.com" Action: - "sts:AssumeRole" Path: / # IAM role to allow logging with cloudwatch. DMSCloudWatchRole: Type: AWS::IAM::Role Properties: RoleName: 'dms-cloudwatch-logs-role' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AmazonDMSCloudWatchLogsRole' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - "dms.amazonaws.com" Action: - "sts:AssumeRole" Path: / # Security group for DMS instance. DMSSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: "Security Group to allow access to DMS instance" GroupName: "DMS Replication Instance Security Group" SecurityGroupEgress: - IpProtocol: -1 CidrIp: '0.0.0.0/0' VpcId: !Ref VpcId # Ingress security group for DMS. DMSSGSelfIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref DMSSecurityGroup IpProtocol: -1 SourceSecurityGroupId: !Ref DMSSecurityGroup # DMS replication instance to perform migrations. DMSReplicationInstance: DependsOn: - DMSVPCRole Type: AWS::DMS::ReplicationInstance Properties: ReplicationInstanceClass: dms.t2.micro AllocatedStorage: 5 AllowMajorVersionUpgrade: false PubliclyAccessible: false ReplicationInstanceIdentifier: unishop-replication-instance VpcSecurityGroupIds: [!Ref DMSSecurityGroup] # DMS source endpoint. Set to the MSSQL database. SourceEndpoint: Type: AWS::DMS::Endpoint Properties: EndpointType: source EngineName: sqlserver DatabaseName: Unishop EndpointIdentifier: unishop-legacy-mssql-endpoint Password: !Ref MasterUserPassword Port: !GetAtt MSSQLDatabaseInstance.Endpoint.Port ServerName: !GetAtt MSSQLDatabaseInstance.Endpoint.Address SslMode: none Username: !Ref MasterUsername # DMS target end point. Set to the Aurora database. TargetEndpoint: Type: AWS::DMS::Endpoint Properties: EndpointType: target EngineName: aurora-postgresql DatabaseName: !Ref AuroraDbName EndpointIdentifier: unishop-inventory-postgres-endpoint Password: !Ref AuroraPassword Port: !GetAtt AuroraDatabasePrimaryInstance.Endpoint.Port ServerName: !GetAtt AuroraDatabasePrimaryInstance.Endpoint.Address SslMode: none Username: !Ref AuroraUsername # Lambda function resources. # IAM Role for Lambda function DynamoDB acess DynamoDBAccessRole: Type: 'AWS::IAM::Role' Properties: RoleName: 'dynamodb-access-modernization-workshop-role' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - 'sts:AssumeRole' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess' # Path: / # Policies: # - PolicyName: FullAccessToDDB # PolicyDocument: # Version: 2012-10-17 # Statement: # - Effect: Allow # Action: # - "dynamodb:List*" # - "dynamodb:DescribeReservedCapacity*" # - "dynamodb:DescribeLimits" # - "dynamodb:DescribeTimeToLive" # Resource: '*' # - Effect: Allow # Action: # - "dynamodb:BatchGet*" # - "dynamodb:DescribeStream" # - "dynamodb:DescribeTable" # - "dynamodb:Get*" # - "dynamodb:Query" # - "dynamodb:Scan" # - "dynamodb:BatchWrite*" # - "dynamodb:CreateTable" # - "dynamodb:Delete*" # - "dynamodb:Update*" # - "dynamodb:PutItem" # Resource: "arn:aws:dynamodb:*:*:table/UnicornBasket" # Route 53 hosted zone. # A hosted zone to map a DNS with the created resource endpoints. # Private hosted zone in the specified VPC. AppHostedZone: Type: AWS::Route53::HostedZone Properties: HostedZoneConfig: Comment: "Hosted zone to route resource endpoints" Name: !Ref HostedZone VPCs: - VPCId: !Ref VpcId VPCRegion: !Ref 'AWS::Region' # DNS name attached to the locust load tester. LocustDNSRecord: Type: AWS::Route53::RecordSet Properties: HostedZoneId: !Ref AppHostedZone Comment: "DNS for locust load testing service" Name: !Join [ '.',['locust', !Ref HostedZone ]] Type: A TTL: '900' ResourceRecords: - !GetAtt EBLocustEnvironment.EndpointURL # DNS name attached to Monolithic application. EBAppDNSRecord: Type: AWS::Route53::RecordSet Properties: HostedZoneId: !Ref AppHostedZone Comment: "DNS for legacy application" Name: !Join [ '.',['app', !Ref HostedZone ]] Type: A TTL: '900' ResourceRecords: - !GetAtt EBEnvironment.EndpointURL # DNS name attached to the MSSQL RDS instance. RDSDNSRecord: Type: AWS::Route53::RecordSet DependsOn: - MSSQLDatabaseInstance Properties: HostedZoneId: !Ref AppHostedZone Comment: "DNS for RDS MSSQL service" Name: !Join [ '.',['db', !Ref HostedZone ]] Type: CNAME TTL: '900' ResourceRecords: - !GetAtt MSSQLDatabaseInstance.Endpoint.Address # S3 resources # S3 bucket with public objects enabled. # Enables static website hosting. StaticHostingBucket: Type: AWS::S3::Bucket DeletionPolicy: Delete Properties: BucketName: !Join - '-' - - !Ref 'StaticHostingBucketName' - !Ref 'AWS::AccountId' PublicAccessBlockConfiguration: IgnorePublicAcls: false BlockPublicAcls: false BlockPublicPolicy: true RestrictPublicBuckets: true