# [DevAx::Academy][.Net][Module 4] for CloudMaster x Syscom - Short URL to this page ---> https://pse.is/4ejcj9 <----- - Full URL to this page https://hackmd.io/@t-Nm5db5SpqzWkaeZs8sqQ/ryNDIpWXs 會議室連結: https://chime.aws/1030627039 Survey: https://survey.immersionday.com/OwjnjnIVg ## Overview ## ### Lab Document ### - .Net: https://workshops.devax.academy/monoliths-to-microservices-dotnet/ - Dynamodb Operation Lab: https://amazon-dynamodb-labs.com/hands-on-labs.html - Java: https://workshops.devax.academy/monoliths-to-microservices/module4.html ### Lab Env ### - https://dashboard.eventengine.run/login?hash=2fc5-1d3862b634-ff 選擇 One-time password (OTP) ![](https://i.imgur.com/Jf3cEhi.png) 設定一個 Team Name (e.g. AWS Jack Hsu) ![](https://i.imgur.com/1QP4COx.png) ![](https://i.imgur.com/eOXYYQQ.png) Login EC2 - https://workshops.devax.academy/monoliths-to-microservices-dotnet/module0/remote_environment/install_cli/provided_account.html ## Information ## ### Windows Environment variable ### 1. Copy from dashboard set AWS_DEFAULT_REGION=<YOUR_AWS_DEFAULT_REGION> set AWS_ACCESS_KEY_ID=<YOUR_AWS_ACCESS_KEY_ID> set AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY> set AWS_SESSION_TOKEN=<YOUR_AWS_SESSION_TOKEN> 2. AWS CLI profile aws configure set profile.devaxacademy.region %AWS_DEFAULT_REGION% aws configure set profile.devaxacademy.aws_access_key_id %AWS_ACCESS_KEY_ID% aws configure set profile.devaxacademy.aws_secret_access_key %AWS_SECRET_ACCESS_KEY% aws configure set profile.devaxacademy.aws_session_token %AWS_SESSION_TOKEN% aws configure get profile.devaxacademy.region aws configure get profile.devaxacademy.aws_access_key_id aws configure get profile.devaxacademy.aws_secret_access_key aws configure get profile.devaxacademy.aws_session_token ### Dynamodb Developer Guide ### - https://docs.aws.amazon.com/sdkfornet1/latest/apidocs/html/N_Amazon_DynamoDBv2_DataModel.htm - https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.NET.08.html ### Lab 4.1.3 command ### * Load Item ``` REM Widnows CMD echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610175636000\"},\"originCity\": {\"S\": \"Melbourne\"},\"destinationCity\": {\"S\": \"Sydney\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610175633000\"},\"originCity\": {\"S\": \"Melbourne\"},\"destinationCity\": {\"S\": \"Perth\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610190033000\"},\"originCity\": {\"S\": \"Melbourne\"},\"destinationCity\": {\"S\": \"Canberra\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610294800000\"},\"originCity\": {\"S\": \"Sydney\"},\"destinationCity\": {\"S\": \"Perth\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610327200000\"},\"originCity\": {\"S\": \"Sydney\"},\"destinationCity\": {\"S\": \"Melbourne\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610356000000\"},\"originCity\": {\"S\": \"Perth\"},\"destinationCity\": {\"S\": \"Darwin\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610374000000\"},\"originCity\": {\"S\": \"Perth\"},\"destinationCity\": {\"S\": \"Singapore\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610402800000\"},\"originCity\": {\"S\": \"Perth\"},\"destinationCity\": {\"S\": \"Sydney\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610446000000\"},\"originCity\": {\"S\": \"Melbourne\"},\"destinationCity\": {\"S\": \"Sydney\"}}" echo Adding record to DynamoDB aws dynamodb put-item --profile devaxacademy --table-name TravelBuddyTripSectors --item "{\"date\": {\"N\": \"1610500000000\"},\"originCity\": {\"S\": \"Darwin\"},\"destinationCity\": {\"S\": \"Sydney\"}}" echo Done! ``` ### Lab 4.1.3 Operation Lab CMD ### aws dynamodb scan --profile devaxacademy --table-name TravelBuddyTripSectors ### Lab 4.2.1 ### 1. Command - create bucket ``` aws s3 mb s3://idevelop-assets-jackhsu-module4 --profile devaxacademy ``` 2. Bug fix - <font color=blue>buildspec.yml</font> ``` aws cloudformation package --template ../../template.yml --s3-bucket idevelop-assets-xxxx-module4 --output-template ../../template-export.yml ``` 3. Bug fix - <font color=blue>src/TripSearch/Services/TripService.cs</font> ``` using System.Collections.Generic; using Amazon.DynamoDBv2.DataModel; using Amazon.DynamoDBv2.DocumentModel; using TripSearch.Interfaces; using TripSearch.Models; using System.Diagnostics; using System; namespace TripSearch.Services { public class TripService: ITripService { private IDynamoDBService _dynamoDbService; private const string DESTINATION_CITY_INDEX = ""; public TripService(IDynamoDBService dynamoDbService) { _dynamoDbService = dynamoDbService; } public List<TripSector> FindAllTrips() { var scanConditions = new List<ScanCondition>(); var tripSectors = _dynamoDbService.GetDynamoDbContext() .ScanAsync<TripSector>(scanConditions).GetRemainingAsync().Result; return tripSectors; } public List<TripSector> FindTripsFromCity(string city) { var operationConfig = new DynamoDBOperationConfig(); operationConfig.ConsistentRead = false; operationConfig.IndexName = "originCity-index"; var tripSectors = _dynamoDbService.GetDynamoDbContext() .QueryAsync<TripSector>(city, operationConfig ).GetRemainingAsync().Result; return tripSectors; } public List<TripSector> FindTripsToCity(string city) { var operationConfig = new DynamoDBOperationConfig { ConsistentRead = false, IndexName = DESTINATION_CITY_INDEX, OverrideTableName = "SomeTableName" }; var queryConditions = new [] { new QueryCondition("destinationCity", QueryOperator.Equal, city)}; var tripSectors = _dynamoDbService.GetDynamoDbContext().QueryAsync<TripSector>(queryConditions, operationConfig).GetRemainingAsync().Result; return tripSectors; } } } ``` * Repliace idevelop-assets-<font color=#FF0000>abhmish</font> => idevelop-assets-<font color=#FF0000>xxxx-module4</font> 2.2 Zip folder to TripSearch 3. Command - s3 cp ``` aws s3 cp .\TripSearch.zip s3://idevelop-assets-jackhsu-module4/src/TripSearch.zip --profile devaxacademy ``` 4. Command - cloudformation deploy ``` aws cloudformation deploy --template-file .\codepipeline.yml --stack-name TripSearchCodePipeline --capabilities CAPABILITY_AUTO_EXPAND CAPABILITY_NAMED_IAM CAPABILITY_IAM --parameter-overrides CodeS3Bucket=idevelop-assets-xxxx-module4 CodeS3Key=src/TripSearch.zip ProjectId=tripsearch --profile devaxacademy ``` * Repliace idevelop-assets-<font color=#FF0000>abhmish</font> => idevelop-assets-<font color=#FF0000>xxxx-module4</font> ### Lab 4.3.1 ### 1. Command - git clone CloudFormation -> mod-xxxxxx -> Output GitUserName GitPassword git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/tripsearch 2. Bug fix - <font color=blue>template.yml</font> * <font color=red>Remove Line 229 => StageName: "prod"</font> ``` AWSTemplateFormatVersion: 2010-09-09 Transform: - AWS::Serverless-2016-10-31 Resources: LambdaGetTripsExecRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" Policies: - PolicyName: "LambdaFunctionsAccess" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: "dynamodb:*" Resource: "*" - Effect: "Allow" Action: - "xray:PutTraceSegments" - "xray:PutTelemetryRecords" Resource: "*" - Effect: "Allow" Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: "*" - Effect: "Allow" Action: - "iam:GetRole" - "iam:CreateRole" - "iam:DeleteRole" - "iam:PassRole" - "iam:PutRolePolicy" - "iam:DeleteRolePolicy" - "lambda:ListTags" - "lambda:TagResource" - "lambda:UntagResource" - "ec2:DescribeSecurityGroups" - "ec2:DescribeSubnets" - "ec2:DescribeVpcs" - "ec2:CreateNetworkInterface" - "ec2:AttachNetworkInterface" - "ec2:DescribeNetworkInterfaces" - "ec2:DeleteNetworkInterface" Resource: "*" LambdaGetAllTrips: Type: AWS::Serverless::Function Properties: Handler: TripSearch::TripSearch.Function::FindAllTripsHandler Runtime: dotnetcore3.1 CodeUri: './src/TripSearch/bin/Release/netcoreapp3.1/TripSearch.zip' MemorySize: 256 Timeout: 30 Role: !GetAtt LambdaGetTripsExecRole.Arn Policies: - AWSLambdaFullAccess Environment: Variables: TripSector_DYNAMODB: TravelBuddyTripSectors TripSector_DYNAMODB_INDEX: originCity-index LambdaGetTripsFromCity: Type: AWS::Serverless::Function Properties: Handler: TripSearch::TripSearch.Function::FindTripsFromCityHandler Runtime: dotnetcore3.1 CodeUri: './src/TripSearch/bin/Release/netcoreapp3.1/TripSearch.zip' MemorySize: 256 Timeout: 30 Role: !GetAtt LambdaGetTripsExecRole.Arn Policies: - AWSLambdaFullAccess Environment: Variables: TripSector_DYNAMODB: TravelBuddyTripSectors TripSector_DYNAMODB_INDEX: originCity-index RESTAPI: Type: "AWS::ApiGateway::RestApi" Properties: Body: swagger: "2.0" info: version: "1.0" title: "iDevelop - Trip Search API" description: "Returns a list of trips" basePath: "/prod" schemes: - "https" paths: /trips: get: consumes: - "application/json" produces: - "application/json" responses: "200": description: "200 response" schema: $ref: "#/definitions/Empty" headers: Access-Control-Allow-Origin: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\n \"payload\" : {},\n \"operation\" : \"GET\"\ ,\n \"correlationId\" : \"$context.requestId\"\n}" uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${!stageVariables.envTrips}/invocations" passthroughBehavior: "when_no_templates" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws" options: consumes: - "application/json" produces: - "application/json" responses: "200": description: "200 response" schema: $ref: "#/definitions/Empty" headers: Access-Control-Allow-Origin: type: "string" Access-Control-Allow-Methods: type: "string" Access-Control-Allow-Headers: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\"statusCode\": 200}" passthroughBehavior: "when_no_match" type: "mock" /tripsfromcity/{city}: get: consumes: - "application/json" produces: - "application/json" responses: "200": description: "200 response" schema: $ref: "#/definitions/Empty" headers: Access-Control-Allow-Origin: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\n \"cityName\": \"$input.params('city')\" \n}" uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${!stageVariables.envTripsFromCity}/invocations" passthroughBehavior: "when_no_templates" httpMethod: "POST" contentHandling: "CONVERT_TO_TEXT" type: "aws" options: consumes: - "application/json" produces: - "application/json" responses: "200": description: "200 response" schema: $ref: "#/definitions/Empty" headers: Access-Control-Allow-Origin: type: "string" Access-Control-Allow-Methods: type: "string" Access-Control-Allow-Headers: type: "string" x-amazon-apigateway-integration: responses: default: statusCode: "200" responseParameters: method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'" method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" method.response.header.Access-Control-Allow-Origin: "'*'" requestTemplates: application/json: "{\"statusCode\": 200}" passthroughBehavior: "when_no_match" type: "mock" definitions: Empty: type: "object" title: "Empty Schema" RESTAPIDeployment: Type: "AWS::ApiGateway::Deployment" Properties: RestApiId: !Ref RESTAPI Description: "Production deployment" StageName: "prod" StageDescription: Description: "Production Stage" Variables: envTrips: !Ref LambdaGetAllTrips envTripsFromCity: !Ref LambdaGetTripsFromCity LambdaGetTripsAllPermissions: Type: "AWS::Lambda::Permission" Properties: FunctionName: !GetAtt LambdaGetAllTrips.Arn Action: "lambda:InvokeFunction" Principal: "apigateway.amazonaws.com" SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RESTAPI}/*/trips LambdaGetTripFromCityPermissions: Type: "AWS::Lambda::Permission" Properties: FunctionName: !GetAtt LambdaGetTripsFromCity.Arn Action: "lambda:InvokeFunction" Principal: "apigateway.amazonaws.com" SourceArn: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RESTAPI}/*/tripsfromcity/{city} ``` ### Lab 4.3.3 ### * Bug fix - <font color=blue>src/TripSearch/Services/TripService.cs</font> ``` using System.Collections.Generic; using Amazon.DynamoDBv2.DataModel; using Amazon.DynamoDBv2.DocumentModel; using TripSearch.Interfaces; using TripSearch.Models; using System.Diagnostics; using System; namespace TripSearch.Services { public class TripService: ITripService { private IDynamoDBService _dynamoDbService; private const string DESTINATION_CITY_INDEX = ""; public TripService(IDynamoDBService dynamoDbService) { _dynamoDbService = dynamoDbService; } public List<TripSector> FindAllTrips() { var scanConditions = new List<ScanCondition>(); var tripSectors = _dynamoDbService.GetDynamoDbContext() .ScanAsync<TripSector>(scanConditions).GetRemainingAsync().Result; return tripSectors; } public List<TripSector> FindTripsFromCity(string city) { var operationConfig = new DynamoDBOperationConfig(); operationConfig.ConsistentRead = false; operationConfig.IndexName = "originCity-index"; var tripSectors = _dynamoDbService.GetDynamoDbContext() .QueryAsync<TripSector>(city, operationConfig ).GetRemainingAsync().Result; return tripSectors; } public List<TripSector> FindTripsToCity(string city) { var operationConfig = new DynamoDBOperationConfig { ConsistentRead = false, IndexName = DESTINATION_CITY_INDEX, OverrideTableName = "SomeTableName" }; var queryConditions = new [] { new QueryCondition("destinationCity", QueryOperator.Equal, city)}; var tripSectors = _dynamoDbService.GetDynamoDbContext().QueryAsync<TripSector>(queryConditions, operationConfig).GetRemainingAsync().Result; return tripSectors; } } } ```