# [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)

設定一個 Team Name (e.g. AWS Jack Hsu)


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