---
# System prepended metadata

title: '[DevAx::Academy][.Net][Module 4] for CloudMaster x Syscom'

---

# [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;
        }
    }
}
```