20200708のAWSに関する記事は22件です。

AWS S3のオブジェクトロックの機能

S3のオブジェクトロックは削除されないように保護する機能です。

作成されたオブジェクトをコンプライアンス上の要請などで削除や変更できないように制限をかけられます。オペレーションミスを防止する目的ではなく、削除の意図を持って削除しようとする人から保護します。

ロックの対象

オブジェクトに対してロックします。バケット全体に対してではありません。

オブジェクトに対する設定というのは特定のKeyの特定のバージョンに対する設定です。特定のKeyに対してロックをかけても、そのKeyに対して引き続き上書き変更や削除を行うことはできます。あくまでロックをかけたそのバージョンを削除できないということです。また、上書きしてできた新しいバージョンにはロックの設定は引き継がれず新規オブジェクトと同じ扱いです。

この機能を使うにはバケットのオブジェクトロックの機能が有効となっている必要があります。

バケットのオブジェクトロック有効化

バケットに対する設定の注意事項:

  • バケットのオブジェクトロック有効化はバケット作成時にのみ設定可能。あとからの変更は不可
  • バケットのオブジェクトロック有効化をするには同時にバージョニングも有効化する必要あり
  • オブジェクトロック有効化されているバケットはバージョニングを止めることができない
  • バケットのオブジェクトロックが有効化だからといって直ちにバケット内すべてのオブジェクトがロックされるわけではない

バケット作成時の画面

オブジェクトロックを有効化するかどうかのみです。

image.png

この他に、オブジェクトに対する設定(ロックをかけるかどうかや、モード、保持期限)のデフォルト値をバケットに対して設定できます。デフォルト値はバケット作成後のPropertiesの画面で設定します。あとから変更もできます。

オブジェクトに対する設定

オブジェクトロックは2つの機能があります。片方だけ使うことも両方同時に使うこともできます。

  • リテンションモード (retention mode)
  • リーガルホールド (legal hold)

リテンションモードにはさらに2つのモードがあります。これはどちらかを選択して使います。

  • ガバナンスモード (governance mode)
  • コンプライアンスモード (compliance mode)

つまり次のうちのいずれかになります。

  • オブジェクトロックをかけない
  • リテンションモードのガバナンスモード
  • リテンションモードのコンプライアンスモード
  • リーガルホールド
  • リテンションモードのガバナンスモードで、かつリーガルホールド
  • リテンションモードのコンプライアンスモードで、かつリーガルホールド

ロックを解除できる条件

  • リテンションモードのガバナンスモード
    • 保持期限が到来すると自動で解除
    • IAMでガバナンスモード解除の権限を持っているユーザまたはロールが解除操作
  • リテンションモードのコンプライアンスモード
    • 保持期限が到来すると自動で解除
  • リーガルホールド
    • IAMでリーガルホールド解除の権限を持っているユーザまたはロールが解除操作

リテンションモードとリーガルホールドの比較

違いは期限があるかどうかです。

リテンションモードは保持期限を設定します。保持期限がすぎたら、リテンションモードが解除されます。

リーガルホールドは無期限です。

どちらも削除できないようにロックされる効果は同じです。

リテンションモードは生成され続けるオブジェクトを一定期間保持する義務がある場合に便利です。

リーガルホールドは事件・事故・訴訟が起きたときに証拠を保全する必要がある場合に便利です。解決するまで無期限に特定のオブジェクトを保護できます。

リテンションモードとリーガルホールドを同時に設定していた場合は、リテンションモードの保持期限をすぎるとリテンションモードのみが解除され、リーガルホールドはそのままです。つまり保持期限を過ぎてもロックされたままです。

リーガルホールドはIAMでリーガルホールド解除の権限を持っていれば解除が可能です。

ガバナンスモードとコンプライアンスモードの比較

違いは期限内にロックを解除できるかどうかです。

ガバナンスモードは期限内であってもIAMでガバナンスモード解除の権限を持っていれば解除が可能です。

コンプライアンスモードは期限内に解除することはできません。たとえroot権限であっても解除できません。

オブジェクトのPropertiesの画面

リテンションモードの選択とリーガルホールドの設定は独立しています。リテンションモードをガバナンスモードまたはコンプライアンスモードにすると、解除される日付(期限)を設定できます。

image.png

image.png

バケットのPropertiesの画面

リテンションモードのデフォルト値を設定できます。保持期限は日数で設定します。リーガルホールドはデフォルトで有効にすることはできません。

image.png

image.png

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Cloud9でphpを実行する方法

Cloud9とは

Cloud9とは、ブラウザのみでコードを記述、実行、デバッグできるクラウドベースの統合開発環境(IDE)です。
Cloud9には開発を行う上で必要となるツールがあらかじめ含まれている(Python,Javascript,PHPなど)ため、インストールや開発環境の設定の手間を省くことができます。

今回はCloud9上でphpを実行する方法を記述していきます。

Cloud9を開く

まずはAWSにログインしましょう。
アカウントが無い方は事前に登録を済ませてください。

ログインすると、AWS マネジメントコンソール画面が開かれるので、
サービスを検索する、の下にある検索欄に「cloud9」と入力してください。
検索欄にリンクが表示されるので、クリックしてcloud9にアクセスします。

cloud9-1.PNG

Cloud9上に新しい環境を作成

次に、phpを実行するための新しい環境を作成していきます。

Cloud9を開くと、以下の画面が表示されるので、
"Create environment"ボタンをクリックします。
image.png

次に、作成する名前を適当に入力し、"Next Step"ボタンをクリックします。
image.png

次に、環境設定を行います。
基本的にデフォルトのままで進めて問題ありません。

  • Environment type
    • EC2:AWS上に新規でサーバを立てる場合
    • SSH:AWS外の既存サーバにアクセスする場合
  • Instance type
    • t2.micro(1 GiB RAM + 1 vCPU)

AWSの無料枠で利用できるのは上記のみですので、デフォルトのままでOKです。
- Platform
- Amazon Linux
- Ubuntu Server

お好みでPlatformを選んでください。

  • Cost-saving setting
    • After 30 minutes

自動でインスタンスをシャットダウンするアイドル時間を選択します。
AWSは従量課金制ですので、特別な理由がない限りはデフォルトのままにしておくことをお勧めします。

ここまで設定後、”Next Step”ボタンをクリックしてください。

Review画面で設定内容を確認し、問題なければ画面下部の
”Create Environment”ボタンをクリックします。
image.png

少し待つと、以下のような画面が表示されます。
これでCloud9上に環境を作成することができました。
image.png

phpファイル作成

左側のメニューからワークスペース名のフォルダを右クリックし、
"New File"ボタンをクリックします。

image.png

拡張子が.phpとなるファイルを作成します。
ここでは「Test.php」としました。
image.png

ファイル作成後、以下のようにソースコードを記載します。

<?php
 echo "Hello,World!";
?>

image.png

ソースコードを入力後、ファイルを保存(Windowsの場合Shift + S、Mac場合はcommand + S)します。

プログラム実行

早速作成したプログラムを動かしてみましょう。

プログラムの実行方法は基本的に二通りあります。
コマンドで実行する方法と、ブラウザでプレビューする方法です。

コマンドで実行
Cloud9の画面下にあるターミナルからプログラムを実行する方法です。

ターミナルに以下のように入力し、Enterを押します。

php 実行するファイル
image.png

実行すると、ターミナル上からプログラムが実行されたことを確認できます。
image.png

ブラウザでプレビュー
こちらはブラウザ上でどのように実行されるのかをプレビューする方法です。

画面右上の”Run”ボタンをクリックします。
image.png

次に、"Preview"をクリックし、”Preview Running Application”ボタンをクリックします。
image.png

右側に新しくウィンドウが表示されます。
右側のウィンドウのURL欄に作成したファイル名を追加し、Enterを押します。
image.png

実行すると、ブラウザ上でどのように表示するかをプレビューで確認することができます。
image.png

Cloud9でphpを実行する方法は以上です。
このように簡単に開発を進めることができますので、Cloud9を活用していきましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでサブドメインなし(wwwなし)からサブドメインあり(wwwあり)へのリダイレクト設定

※本記事は社内ブログの内容を一部変えて転記しています。

背景

AWSのS3 + cloudfrontでwebサイトを公開する際に
wwwありのURL(https://www.sink-capital.com/)で公開を行ったのですが、
サブドメインなしのURL(https://sink-capital.com/)だと繋がらないことに気付いたため修正を行いました。

一般的な方法

基本的には
Amazon Route 53 を使って、サブドメイン(www)なしから、ありドメインへリダイレクト
に記載のある方法で、
サブドメインなしのURLから来たリクエストを、
全てサブドメインありのURLにリダイレクトすることを目指しました。

  1. サブドメインなしのURLからリクエストが来る
  2. route53のAレコードaliasでS3にリクエストを飛ばす
  3. S3の設定でサブドメインありのURLにリダイレクト

ただし上記の対応だとhttpsに対応できないという問題点がありました。
(route53とS3だと証明書を入れ込むことができないため)

今回の対応方法

webサイト自体の公開方法と同様にcloudfrontを利用してS3に接続を行いました。

  1. サブドメインなしのURLからリクエストが来る
  2. route53のAレコードaliasでcloudfrontにリクエストを飛ばす
  3. cloudfrontからS3にリクエストを流す
  4. S3の設定でサブドメインありのURLにリダイレクト

実際のコード(terraform)

cloudfront.tf

resource "aws_cloudfront_distribution" "sci_website" {
  enabled = true
  aliases = [
    "${var.root_domain_name}"]
  comment = "${terraform.workspace}-www-root-alias"
  default_root_object = "index.html"
  price_class = "PriceClass_All"

  origin {
    domain_name = "${var.root_domain_name}.s3-website-${var.region}.amazonaws.com"
    origin_id = "S3-${aws_s3_bucket.sci_www_root_alias_bucket.bucket}"

    custom_origin_config {
      http_port = 80
      https_port = 443
      origin_protocol_policy = "http-only"
      origin_ssl_protocols = [
        "TLSv1"]
    }
  }

  default_cache_behavior {
    allowed_methods = [
      "GET",
      "HEAD"]
    cached_methods = [
      "GET",
      "HEAD"]
    target_origin_id = "S3-${aws_s3_bucket.sci_www_root_alias_bucket.bucket}"

    forwarded_values {
      query_string = true
      cookies {
        forward = "none"
      }
    }

    viewer_protocol_policy = "https-only"
    min_ttl = 3600
    default_ttl = 10800
    max_ttl = 86400
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    acm_certificate_arn = "${aws_acm_certificate.sci_com.arn}"
    ssl_support_method = "sni-only"
    minimum_protocol_version = "TLSv1"
  }
}

route53.tf

resource "aws_route53_record" "root" {
  zone_id = "${var.route53_zone_id}"
  name = "${var.root_domain_name}"
  type = "A"

  alias {
    name = "${aws_cloudfront_distribution.sci_website.domain_name}"
    zone_id = "${aws_cloudfront_distribution.sci_website.hosted_zone_id}"
    evaluate_target_health = true
  }
}

resource "aws_route53_record" "cert_validation" {
  zone_id = "${var.route53_zone_id}"
  name = "${aws_acm_certificate.sci_com.domain_validation_options.0.resource_record_name}"
  type = "${aws_acm_certificate.sci_com.domain_validation_options.0.resource_record_type}"
  ttl = 60
  allow_overwrite = true
  records = [
    "${aws_acm_certificate.sci_com.domain_validation_options.0.resource_record_value}"]
}

resource "aws_acm_certificate" "sci_com" {
  provider = "aws.ue1"
  domain_name = "${var.root_domain_name}"
  subject_alternative_names = []
  validation_method = "DNS"
}

resource "aws_acm_certificate_validation" "sci_com_validation" {
  provider = "aws.ue1"
  certificate_arn = "${aws_acm_certificate.sci_com.arn}"
  validation_record_fqdns = [
    "${aws_route53_record.cert_validation.0.fqdn}"]
}

s3.tf

resource "aws_s3_bucket" "sci_www_root_alias_bucket" {
  bucket = "${var.root_domain_name}"
  acl = "private"

  website {
    redirect_all_requests_to = "https://www.${var.root_domain_name}"
  }
}

variable.tf

※ドメイン名はお好きに

variable "route53_zone_id" {}
variable "root_domain_name" {
  default = "sink-capital.com"
}
variable "region" {
  default = "ap-northeast-1"
}

感想

terraform中に出てくる細かい設定がいくつかあり、
一つでも間違えると繋がらなかったりするので意外と大変でした。
特にキャッシュが残るせいで繋がったり繋がらなかったり。。。
ただterraformでかけたので心理的安全性はかなり高くなります。

参照リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel5.4】FormRequestを使うとHttpException This action is unauthorized が表示される。

【開発環境】

Amazon EC2 Linux
Windows 10 HOME
Apache/2.4.43
Laravel Framework 5.4.36
vsftpd: Ver 3.0.2

Tera Term 4.1.105
FFFTP Ver 4.7

Laravel で作成した画面で登録ボタンを押すと、、

1.jpg

2.jpg

こんなエラーメッセージが。。

【エラーメッセージ】

 (1/1) HttpException

This action is unauthorized.
in Handler.php line 133
at Handler->prepareException(object(AuthorizationException))in Handler.php line 109
at Handler->render(object(Request), object(AuthorizationException))in Handler.php line 47
at Handler->render(object(Request), object(AuthorizationException))in Pipeline.php line 82
at Pipeline->handleException(object(Request), object(AuthorizationException))in Pipeline.php line 32
at Pipeline->Illuminate\Routing\{closure}(object(Request))in SubstituteBindings.php line 41
at SubstituteBindings->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in VerifyCsrfToken.php line 65
at VerifyCsrfToken->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in ShareErrorsFromSession.php line 49
at ShareErrorsFromSession->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in StartSession.php line 64
at StartSession->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in AddQueuedCookiesToResponse.php line 37
at AddQueuedCookiesToResponse->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in EncryptCookies.php line 59
at EncryptCookies->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in Pipeline.php line 102
at Pipeline->then(object(Closure))in Router.php line 574
at Router->runRouteWithinStack(object(Route), object(Request))in Router.php line 533
at Router->dispatchToRoute(object(Request))in Router.php line 511
at Router->dispatch(object(Request))in Kernel.php line 176
at Kernel->Illuminate\Foundation\Http\{closure}(object(Request))in Pipeline.php line 30
at Pipeline->Illuminate\Routing\{closure}(object(Request))in TransformsRequest.php line 30
at TransformsRequest->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in TransformsRequest.php line 30
at TransformsRequest->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in ValidatePostSize.php line 27
at ValidatePostSize->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in CheckForMaintenanceMode.php line 46
at CheckForMaintenanceMode->handle(object(Request), object(Closure))in Pipeline.php line 148
at Pipeline->Illuminate\Pipeline\{closure}(object(Request))in Pipeline.php line 53
at Pipeline->Illuminate\Routing\{closure}(object(Request))in Pipeline.php line 102
at Pipeline->then(object(Closure))in Kernel.php line 151
at Kernel->sendRequestThroughRouter(object(Request))in Kernel.php line 116
at Kernel->handle(object(Request))in index.php line 53

【対応】

調べていくとどうやら下記の passesAuthorization関数で

/vendor/laravel/framework/src/Illuminate/Foundation/Http/FormRequest.php
    protected function passesAuthorization()
    {
        if (method_exists($this, 'authorize')) {
            return $this->container->call([$this, 'authorize']);
        }
//#2020708 Edited by 
//         return false;
       return true;
    }

artisan make:requestコマンド実行時に入力パラメータをバリデーションする
FormRequestクラスを作成できます。

ですがそのままだと「false」を無条件に返しているらしく
ここを修正する必要があります。

無事画面上で登録処理が完了しました。
時間があればもう少し深く調査見てみようかな。

【参考】

【Laravel5.8】FormRequestを使うとThis action is unauthorized.が吐き出される

[Laravel] artisan make:requestでFormRequest使用時にThis action is unauthorized.エラーが出る時

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS Config】基本設定と簡単な動作検証

目標

AWS Configを開始し、簡単な動作確認を行う。

きっかけ

参考書レベルでの知識はあったのですが、実際に動かしたことがなく、
また正直いまいちイメージが掴みにくいサービスという印象があったため、
今回ハンズオンで具体的なイメージを持とうと思い本記事を投稿致しました。

AWS Configとは

AWSアカウント内に存在する各種AWSリソース(EC2、EBS、セキュリティグループ、VPCなど)の構成情報を取得し、管理するサービスです。
主要な利用目的としては、以下の2点が挙げられます。

①取得したAWSリソースの構成情報が利用者によって事前定義されたルール(ElasticIPが付与されているか、暗号化ボリュームを利用しているか、必須タグを使用しているか…等)と合致しているかを評価する。
②AWSリソースの構成変更履歴(いつ、どのように作成、変更、削除が行われたか等)を記録する。

より詳しくはAWSドキュメント参考
AWS Config とは

作業の流れ

項番 タイトル
1 Configを開始する
2 動作確認

手順

1.Configを開始する

①AWSコンフィグコンソールへ移動し、今から始めるをクリック
tempsnip.png

②設定
まずはAWS Configで構成情報取得の対象とするAWSリソースを選択します。
今回はデフォルトのこのリージョンでサポートされているすべてのリソースを記録します(AWS Configでサポートされている対象リージョンの全AWSリソースが構成情報管理の対象となります)で設定します(※)。

※補足
グローバルリソース (AWS IAM リソースなど) を含めるを選択すると、IAM ユーザー、グループ、ロール、およびカスタマー管理ポリシーもAWS Configによる構成情報取得の対象となるようです。
またこのリージョンでサポートされているすべてのリソースを記録しますのチェックを外し、特定の型に対象AWSリソースを記載することで、構成情報取得の対象を絞り込むことも可能です(コストパフォーマンスに優れ、かつ注意したいAWSリソースのみに集中出来そうです)

tempsnip.png

次に取得したAWSリソース構成情報のスナップショット格納先S3バケットを設定します。
今回はバケットの作成を選び、新しくバケット作成をしてみます。

tempsnip.png

SNSと連携することで、AWSリソースの構成情報に変更があった場合、Eメール等を利用した通知を行うことができます。
今回は新規作成したSNSトピックと連携させてみます。

Amazon SNSトピックへのストリーム設定の変更と通知。
トピックの作成を選択

tempsnip.png

最後に各種AWSリソースへのアクセス権を付与したIAMロールをAWS Configに付与します。
特に使用したいロールがなければ既存のAWS Configサービスにリンクされたロールを使用でOKかと思います。

tempsnip.png

終わったら次へを押下

②ルール選択
取得したAWSリソースの構成情報と照合するためのルール(※)を選択します。
ここではサンプルとしてEC2のeip-attached(EIPがEC2インスタンスに付与されているか判定)とencrypted-volumes(アタッチされているEBSボリュームが暗号化されているか判定)を選択

※補足
ルールはAWS Configセットアップ後でもカスタマイズ可能です。
ルールにはAWSがベストプラクティスに基づいて提供する「マネージドルール」(今回選択したのはこれ)と、利用者がカスタム開発する「カスタムルール」(Lambdaと連携して動作するようです)が存在します。

tempsnip.png

選択したら次へ押下

③設定内容確認
確認押下
tempsnip.png

④Configダッシュボード作成確認
しばらくしたらAWS Configのダッシュボードが作成されました。
正常に結果反映されるまで数分かかりました。
image.png

⑤SNS通知のセットアップ
SNSコンソールからトピックを選択し、AWS Configと一緒に作成したSNSトピックをクリック
tempsnip.png

対象トピックにはまだサブスクリプション(※)がまだ作成されていないので新規作成します。
サブスクリプションの作成をクリック

※サブスクリプション
あるSNSトピックからのメッセージの受け取り手のこと。
httpやEメール等、様々なプロトコルを介してのトピック通知に対応している。
tempsnip.png

今回はEメールによるSNS通知を利用してみます。
受け取るメールアドレスを入力したら、サブスクリプションの作成をクリック
tempsnip.png

その後、設定したメールアドレスにメールが来るのでConfirm subscriptionをクリックしメールアドレス認証を完了させれば通知設定完了です。
tempsnip.png

2.動作確認

①AWSリソース構成情報と定義ルール間の照合機能
非準拠の定義ルールを全て準拠にしてみます。

<初期状態>
非準拠ルール: 2つ(eip-attachedencrypted-volumesが違反)
非準拠リソース: 3つ(eip-attachedで1つのEIP、encrypted-volumesで2つのEBSボリュームが違反)
image.png

※設定ルール内容
eip-attached:EIPがEC2インスタンスに付与されているか判定
encrypted-volumes:アタッチされているEBSボリュームが暗号化されているか判定

<過程>
eip-attached⇒非準拠EIPをEC2に付与して解消を試みる
tempsnip.png

encrypted-volumes⇒非準拠2ボリュームを暗号化させます(EBSスナップショット取得して暗号化ボリューム作成)。元の非準拠の2ボリュームは削除した

tempsnip.png

各ルール詳細から再評価実行(定期的に評価は行われるが検証迅速化のため実行)
tempsnip.png

<結果>
全ルールを準拠させることが出来ました!
image.png

②AWSリソースの構成変更履歴確認機能
AWS Configでは対象のAWSリソースの作成、変更、削除等が行われた際に、それを構成変更履歴として残します。
試しに、ある1つのセキュリティグループの80番ポートを新たに開けてみたところ、
数分後に変更したセキュリティグループに関するSNSからのメール通知を取得出来ました。
tempsnip.png

メールのリンクをクリックすると、AWSコンソール上からどのような構成情報の変更がなされたかの詳細を確認可能です。
image.png
image.png

また、構成情報のスナップショット格納先として指定したS3バケットには各種リソースの構成情報をまとめたログファイルが格納されているのも確認できました(6時間ごとに格納しているらしい)
image.png

使い終わったら

構成情報取得が不要になった際はAWS Configコンソールの設定オフにするにして余計な課金を防ぎます。
tempsnip.png

またAWS CLIを利用することでAWS Configの設定自体を削除することも可能でした(※)。
※参考にしたサイト
AWSマネジメントコンソールから消せないAWS Configの設定をAWSCLIで綺麗にする方法

# AWS Configの設定状況確認
$ aws configservice describe-delivery-channels
{
    "DeliveryChannels": [
        {
            "snsTopicARN": "arn:aws:sns:ap-northeast-1:932699493995:config-topic",
            "name": "default",
            "s3BucketName": "config-bucket-932699493995"
        }
    ]
}

# AWS Config削除実行
$ aws configservice delete-delivery-channel --delivery-channel-name default

# AWS Config削除確認
$ aws configservice describe-delivery-channels
{
    "DeliveryChannels": []
}

所感

単純に便利なサービスだなと思いました。
これを使用すれば、リソース構築の際のミス設定も(定義ルールとの照らし合わせにより)かなり素早く検知することが可能になるのではないでしょうか。
ちなみに基本的には有効化するのがセオリーなぐらいらしいです(自分も同意出来ます)。
今までの現場で使っていたのいないのか、存在を認識していなかった。。
…今回は以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS Config】AWS Configを設定し簡単な動作検証を行う

目標

AWS Configを開始し、簡単な動作確認を行う。

きっかけ

参考書レベルでの知識はあったのですが、実際に動かしたことがなく、
また正直いまいちイメージが掴みにくいサービスという印象があったため、
今回ハンズオンで具体的なイメージを持とうと思い本記事を投稿致しました。

AWS Configとは

AWSアカウント内に存在する各種AWSリソース(EC2、EBS、セキュリティグループ、VPCなど)の構成情報を取得し、管理するサービスです。
主要な利用目的としては、以下の2点が挙げられます。

①取得したAWSリソースの構成情報が利用者によって事前定義されたルール(ElasticIPが付与されているか、暗号化ボリュームを利用しているか、必須タグを使用しているか…等)と合致しているかを評価する。
②AWSリソースの構成変更履歴(いつ、どのように作成、変更、削除が行われたか等)を記録する。

より詳しくはAWSドキュメント参考
AWS Config とは

作業の流れ

項番 タイトル
1 Configを開始する
2 動作確認

手順

1.Configを開始する

①AWSコンフィグコンソールへ移動し、今から始めるをクリック
tempsnip.png

②設定
まずはAWS Configで構成情報取得の対象とするAWSリソースを選択します。
今回はデフォルトのこのリージョンでサポートされているすべてのリソースを記録します(AWS Configでサポートされている対象リージョンの全AWSリソースが構成情報管理の対象となります)で設定します(※)。

※補足
グローバルリソース (AWS IAM リソースなど) を含めるを選択すると、IAM ユーザー、グループ、ロール、およびカスタマー管理ポリシーもAWS Configによる構成情報取得の対象となるようです。
またこのリージョンでサポートされているすべてのリソースを記録しますのチェックを外し、特定の型に対象AWSリソースを記載することで、構成情報取得の対象を絞り込むことも可能です(コストパフォーマンスに優れ、かつ注意したいAWSリソースのみに集中出来そうです)

tempsnip.png

次に取得したAWSリソース構成情報のスナップショット格納先S3バケットを設定します。
今回はバケットの作成を選び、新しくバケット作成をしてみます。

tempsnip.png

SNSと連携することで、AWSリソースの構成情報に変更があった場合、Eメール等を利用した通知を行うことができます。
今回は新規作成したSNSトピックと連携させてみます。

Amazon SNSトピックへのストリーム設定の変更と通知。
トピックの作成を選択

tempsnip.png

最後に各種AWSリソースへのアクセス権を付与したIAMロールをAWS Configに付与します。
特に使用したいロールがなければ既存のAWS Configサービスにリンクされたロールを使用でOKかと思います。

tempsnip.png

終わったら次へを押下

②ルール選択
取得したAWSリソースの構成情報と照合するためのルール(※)を選択します。
ここではサンプルとしてEC2のeip-attached(EIPがEC2インスタンスに付与されているか判定)とencrypted-volumes(アタッチされているEBSボリュームが暗号化されているか判定)を選択

※補足
ルールはAWS Configセットアップ後でもカスタマイズ可能です。
ルールにはAWSがベストプラクティスに基づいて提供する「マネージドルール」(今回選択したのはこれ)と、利用者がカスタム開発する「カスタムルール」(Lambdaと連携して動作するようです)が存在します。

tempsnip.png

選択したら次へ押下

③設定内容確認
確認押下
tempsnip.png

④Configダッシュボード作成確認
しばらくしたらAWS Configのダッシュボードが作成されました。
正常に結果反映されるまで数分かかりました。
image.png

⑤SNS通知のセットアップ
SNSコンソールからトピックを選択し、AWS Configと一緒に作成したSNSトピックをクリック
tempsnip.png

対象トピックにはまだサブスクリプション(※)がまだ作成されていないので新規作成します。
サブスクリプションの作成をクリック

※サブスクリプション
あるSNSトピックからのメッセージの受け取り手のこと。
httpやEメール等、様々なプロトコルを介してのトピック通知に対応している。
tempsnip.png

今回はEメールによるSNS通知を利用してみます。
受け取るメールアドレスを入力したら、サブスクリプションの作成をクリック
tempsnip.png

その後、設定したメールアドレスにメールが来るのでConfirm subscriptionをクリックしメールアドレス認証を完了させれば通知設定完了です。
tempsnip.png

2.動作確認

①AWSリソース構成情報と定義ルール間の照合機能
非準拠の定義ルールを全て準拠にしてみます。

<初期状態>
非準拠ルール: 2つ(eip-attachedencrypted-volumesが違反)
非準拠リソース: 3つ(eip-attachedで1つのEIP、encrypted-volumesで2つのEBSボリュームが違反)
image.png

※設定ルール内容
eip-attached:EIPがEC2インスタンスに付与されているか判定
encrypted-volumes:アタッチされているEBSボリュームが暗号化されているか判定

<過程>
eip-attached⇒非準拠EIPをEC2に付与して解消を試みる
tempsnip.png

encrypted-volumes⇒非準拠2ボリュームを暗号化させます(EBSスナップショット取得して暗号化ボリューム作成)。元の非準拠の2ボリュームは削除した

tempsnip.png

各ルール詳細から再評価実行(定期的に評価は行われるが検証迅速化のため実行)
tempsnip.png

<結果>
全ルールを準拠させることが出来ました!
image.png

②AWSリソースの構成変更履歴確認機能
AWS Configでは対象のAWSリソースの作成、変更、削除等が行われた際に、それを構成変更履歴として残します。
試しに、ある1つのセキュリティグループの80番ポートを新たに開けてみたところ、
数分後に変更したセキュリティグループに関するSNSからのメール通知を取得出来ました。
tempsnip.png

メールのリンクをクリックすると、AWSコンソール上からどのような構成情報の変更がなされたかの詳細を確認可能です。
image.png
image.png

また、構成情報のスナップショット格納先として指定したS3バケットには各種リソースの構成情報をまとめたログファイルが格納されているのも確認できました(6時間ごとに格納しているらしい)
image.png

使い終わったら

構成情報取得が不要になった際はAWS Configコンソールの設定オフにするにして余計な課金を防ぎます。
tempsnip.png

またAWS CLIを利用することでAWS Configの設定自体を削除することも可能でした(※)。
※参考にしたサイト
AWSマネジメントコンソールから消せないAWS Configの設定をAWSCLIで綺麗にする方法

# AWS Configの設定状況確認
$ aws configservice describe-delivery-channels
{
    "DeliveryChannels": [
        {
            "snsTopicARN": "arn:aws:sns:ap-northeast-1:932699493995:config-topic",
            "name": "default",
            "s3BucketName": "config-bucket-932699493995"
        }
    ]
}

# AWS Config削除実行
$ aws configservice delete-delivery-channel --delivery-channel-name default

# AWS Config削除確認
$ aws configservice describe-delivery-channels
{
    "DeliveryChannels": []
}

所感

単純に便利なサービスだなと思いました。
これを使用すれば、リソース構築の際のミス設定も(定義ルールとの照らし合わせにより)かなり素早く検知することが可能になるのではないでしょうか。
ちなみに基本的には有効化するのがセオリーなぐらいらしいです(自分も同意出来ます)。
今までの現場で使っていたのいないのか、存在を認識していなかった。。
…今回は以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS EC2でApache Tomcatを接続

研修でAWSとTomcat使ってサーバー構築するので備忘録

前提条件

  • AWSにアカウント登録済
  • セキュリティグループ構築済
  • ネットワーク等も構築済

手順

EC2インスタンス作成

  1. コンソールにログインするアドレスでログイン画面に飛んでログイン
  2. EC2>インスタンス画面で、インスタンスの作成
  3. もろもろ適切に設定(このとき既存のネットワークとかセキュリティグループを使った)
  4. 最後まで設定できたら、キーペアの作成とかが出るので控える(今回は既存のものを使用)
  5. インスタンス一覧画面から作ったインスタンスを探す。初期化とかも全部終わったらOK

Apacheインストール

  1. sudo yum -y install httpd Apacheのインストール
  2. sudo service httpd start Apacheの起動
  3. sudo chkconfig httpd on Apacheが自動起動するように設定
  4. sudo chkconfig -–list httpd Apacheがちゃんと起動できてるか確認

Javaインストール

(ここから大体rootユーザーでやってた)
1. sudo yum -y install yum-fastestmirror fastestmirrorのインストール
2. sudo yum -y install java-1.8.0-openjdk-devel Javaのインストール
3. java -version バージョンの確認(確認できれば入ってる)

Tomcatインストール

  1. curl -O http://ftp.meisei-u.ac.jp/mirror/apache/dist/tomcat/tomcat-9/v9.0.37/bin/apache-tomcat-9.0.37.tar.gz TomcatのDL  ※Tomcat公式サイトで適宜最新バージョンを確認してDLするようにしないとうまく解凍できなかったりする
  2. tar -xvzf apache-tomcat-9.0.37.tar.gz 1でDLしたやつを解凍 うまく行くとファイルがずらーっと表示される  ※ずらーっとなるのが嫌だったらオプション部分をいじると多分表示されない
  3. mv ~/apache-tomcat-9.0.37 /opt 2のファイルを移動
  4. chown -R tomcat:tomcat /opt/apache-tomcat-9.0.37 移動させたファイルの所有権を全部tomcatユーザーとかグループに移行
  5. vi /etc/systemd/system/tomcat.service viで以下のように書き込んでサービスの登録を行う
[Unit]
 Description=Apache Tomcat 9
 After=network.target

 [Service]
 User=tomcat
 Group=tomcat
 Type=oneshot
 PIDFile=/opt/apache-tomcat-9.0.37/tomcat.pid
 RemainAfterExit=yes

 ExecStart=/opt/apache-tomcat-9.0.37/bin/startup.sh
 ExecStop=/opt/apache-tomcat-9.0.37/bin/shutdown.sh
 ExecReStart=/opt/apache-tomcat-9.0.37/bin/shutdown.sh;/opt/apache-tomcat-9.0.37/bin/startup.sh

 [Install]
 WantedBy=multi-user.target
  1. chmod 755 /etc/systemd/system/tomcat.service ファイル権限を755に変更
  2. systemctl enable tomcat サービスを有効化

あとはブラウザで「パブリックIP:8080」を叩くとTomcatのページが表示されます。

参考になるページ

https://weblabo.oscasierra.net/installing-tomcat9-centos7-1/
https://qiita.com/LowSE01/items/2c735f22d220f53a6b2f
https://www.oqiita.com/?p=1771

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSサーバーレス連絡フォームの作り方?(AWS Lambda、API Gateway、SES)

AWS S3にサイトを安くて簡単にホスティングできますが、S3は静的なサイトしかホスティングできないですので、従来ならバックエンド処理が必要となるフォーム送信の実装などは難しそうですね。しかし、クラウド時代ではそんな心配はありません!ソリューションはサーバーレスアーキテクチャです!AWSはすでに色々なサーバーレス構築のツールを提供しています。

今回はユーザからお問い合わせやフィードバックを送信するたびに、任意な宛先に通知メールを送信するシンプルなサーバーレスメールサービスを一緒に作ります。AWS LambdaとAPI Gatewayを利用し、簡単なAWSサーバーレス構築を紹介します。

構成図はこんな感じです。
diagram.PNG

処理の流れとしては、ユーザーから連絡フォームより入力した情報を収集し、クライアント側のブラウザからAmazon API Gateway RESTfulサービスに投稿します。Amazon API Gatewayは、収集されたユーザー情報をAWS lambda関数に渡します。AWS Lambda関数は、Eメールを自動生成し、Amazon SESを使用してメールサーバーに転送します。

1.フォームを作成

  • index.htmlファイルを作成し、下記のコードを貼り付けます。
index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>連絡フォーム</title>
    <script src="script.js"></script>
    <link rel="stylesheet " href="style.css ">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js "></script>

</head>


<body>
    <h1>連絡フォーム</h1>
    <form id="contact-form" method="post">
        <h4>名前:</h4>
        <input type="text" id="name-input" placeholder="お名前を入力してください。" class="form-control" /><br />
        <h4>件名:</h4>
        <input type="subject " id="subject " " placeholder="お名前を入力してください。 " class="form-control " /><br />
        <h4>メール:</h4>
        <input type="email " id="email-input " placeholder="メールを入力してください。 " class="form-control " /><br />
        <h4>メッセージ:</h4>
        <textarea id="description-input " rows="3 " placeholder="メッセージを入力してください。 " class="form-control "></textarea><br />
        <div class="button-wrapper "><button type="button " onClick="submitToAPI(event) " class="btn btn-lg ">送信</button></div>

    </form>
</body>
</html>

ブラウザでプレビューを確認できます。こんな感じです。
basic-html-form.PNG

  • 見た目はあまり良くないですので、改善しましょう。style.cssを同じフォルダ内で作成し、下記のコードを貼り付けます。
style.css
* {
    box-sizing: border-box;
}

body {
    text-align: center;
    color: rgb(255, 255, 255);
    background-color: #194680;
    padding-top: 20px;
}

#contact-form {
    margin: auto;
    text-align: left;
    padding: 20px;
    max-width: 430px;
    border: white solid 2px;
    border-radius: 10px;
}

button {
    text-align: center;
    margin-top: 20px;
    font-size: 18px;
    border: white solid 2px;
    background-color: white;
    border-radius: 5px;
}

.button-wrapper {
    text-align: center;
}

input,
textarea {
    width: 380px;
}

textarea {
    height: 200px;
    padding: 10px;
}

input {
    height: 35px;
}

ブラウザではこんな感じになります。
styled-form.PNG

2. AWS Lambda関数の定義

  • AWS Lambda画面で新規関数を作成し、hello-worldのnodejsテンプレを選択します。
    lambda2.PNG

  • 基本的な情報の画面では関数を任意に名付けて、実行ロールは最初選択のままで進みます。
    lambda3.PNG

  • 次の画面の関数コードに下記のコードを切り替えます。

index.js
var AWS = require('aws-sdk');
var ses = new AWS.SES();

//宛先のメールアドレスを入力してください。
var RECEIVER = 'example-receiver@gmail.com';

//送信先のメールアドレスを入力してください。
//送信先のメールはAWSに認証登録する必要があります。
var SENDER = 'example-sender@gmail.com';

//クライアントへのレスポンスヘッダーを設定
var response = {
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": 'application/json',
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Accept",
        "Access-Control-Allow-Methods": "POST,GET,OPTIONS",
        "Access-Control-Allow-Origin": "*",
    },
    "statusCode": 200,
    "body": "{\"result\": \"テスト成功です!\"}"
};

exports.handler = function(event, context, callback) {
    console.log('受理したイベント:', event);
    sendEmail(event, function(err, data) {
        context.done(err, null);
    });
    callback(null, response);
};

//メール送信処理
function sendEmail(event, done) {
    var params = {
        Destination: {
            ToAddresses: [
                RECEIVER
            ]
        },
        Message: {
            Body: {
                Text: {
                    Data: '● お名前: ' + event.name + '\n● 件名: ' + event.subject + '\n● メール: \n' + event.email + '\n● 内容: \n' + event.desc,
                    Charset: 'UTF-8'
                }
            },
            Subject: {
                Data: event.name + 'からの連絡フォームが来ました!',
                Charset: 'UTF-8'
            }
        },
        Source: SENDER
    };
    ses.sendEmail(params, done);
    return {
        response,
        body: JSON.stringify(params),
        statusCode: 200
    }
}
  • 同じLambda管理画面で、上にスクロールし、アクセス権限のタブに切り替え、ロール名のリンクをクリックします。

lambda-policy.PNG

  • 当関数のIAMポリシーをSES権限を追加します。IAM画面で該当ポリシーをクリックしポリシーを編集 ->JSONのフォーマットに切り替えし、下記のコードを追加します。

lambda-policy3.PNG

{
  "Sid": "FormPolicy",
  "Effect": "Allow",
  "Action": "ses:SendEmail",
  "Resource": "*"
}

こんな感じです。
lambda-policy2.PNG

3. APIゲートウェイでAPI構築

  • AWSメインコンソールよりAPI Gateway管理画面にアクセスし、APIを作成をクリックします。
    api.PNG

  • 次の画面ではREST API構築をクリックします。

api2.PNG

  • 次の画面はREST APIの選択のままで、API名前を任意に付けます。

api2a.PNG

  • 次の画面は上にあるアクションをクリックし、メッソド作成をクリック、POSTを選択します。

api3.PNG

  • 右側の設定画面では、統合タイプLambdaで、Lambda関数は先ほど作成したものを選択します。

api4b.PNG

  • CORSエラーにならないように、CORS機能も有効にします。アクションのドロップダウンをCORS有効化をクリックし、設定はそのままで保存します。

api4c.PNG

  • API Gatewayの最新アップデートでは、ゲートウェイのレスポンスも設定しないと、CORSエラーが発生するため、設定しましょう。右のゲートウェイレスポンス → DEFAULT 4XX編集の順番にナビゲートします

api5a.PNG

  • レスポンスヘッダーに下記の値を追加し、保存します。
  Access-Control-Allow-Origin: '*'

こんな感じです。
api5b.PNG

これでAPIの構築は完了です。

4. SES送信用のメール検証登録

mail.PNG

verifiedの状態になったら、OKです!

  • Lambda画面に戻して、左上にあるテストで動作確認できます。lambda-test.PNG

  • サンプルテストJSONデータ:

{
  "name": "value1",
  "subject": "value2",
  "email": "test@test.com",
  "desc": "value3"
}

5.全コンポーネントを連携する

  • 最後のステップではすべてを繋ぎます!index.htmlと同じいフォルダでscript.jsを作成し、下記のコードを貼り付け、var URLurlの2ヶ所に先ほど設定したAPI GatewayのエンドポイントURLを差し替えます。
script.js
function submitToAPI(e) {
    e.preventDefault();
    //設定したAPI GatewayのエンドポイントURLをここに入れます。
    var URL = "API-GATEWAY-ENDPOINT-URL";

    //フォームの入力値をチェック
    var name = /[A-Za-z]{1}[A-Za-z]/;
    if (!name.test($("#name-input").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    var subject = /[A-Za-z]{1}[A-Za-z]/;
    if (!subject.test($("#subject").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    if ($("#email-input").val() == "") {
        alert("メールを入力してください。");
        return;
    }

    var email = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,6})?$/;
    if (!email.test($("#email-input").val())) {
        alert("メールアドレスは正しくありません。");
        return;
    }

    var name = $("#name-input").val();
    var subject = $("#subject").val();
    var email = $("#email-input").val();
    var desc = $("#description-input").val();
    var data = {
        name: name,
        subject: subject,
        email: email,
        desc: desc
    };

    $.ajax({
        type: "POST",
        //設定したAPI GatewayのエンドポイントURLをここに入れます。
        url: "API-GATEWAY-ENDPOINT-URL",
        dataType: "json",
        crossDomain: "true",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(data),


        success: function() {
            // フォームをクリアし、送信成功のメッセージを表示する
            alert("メッセージが送信されました!");
            document.getElementById("contact-form").reset();
            location.reload();
        },
        error: function() {
            // 送信エラーのメッセージを表示する
            alert("メッセージ送信失敗!");
        }
    });
}

6. 結論

フォームより受信するメールはこんな感じです。
demo.PNG

これでAmazon S3で静的Webサイトをホストしても、AWSアーキテクチャを利用し、ユーザよりサイト管理者へフォーム送信機能を実装できました!

初投稿です、、いかがでしょうか、、(´・ω・`)
コメント、フィードバックなど大変モチベーションになりますので、
是非よろしくお願いいたします!
それでは、また今度! (`・∀・´)ノ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambda、Amazon API Gateway、Amazon SESを使用し、S3静的ウェブサイトの動的な連絡フォームを作りましょう!?

AWS S3にサイトを安く簡単にホスティングできますが、S3は静的なサイトしかホスティングできないですので、従来ならバックエンド処理が必要になるフォーム送信の実装などは難しそうですね。しかし、クラウド時代ではそんな心配はありません!それははサーバーレスアーキテクチャです!AWSはすでに色々なサーバーレス構築のツールを提供しています。

この投稿ではユーザからお問い合わせやフィードバックを送信するたびに、任意な宛先に通知メルを送信するシンプルなサーバーレスメールサービスを一緒に作ります。AWS Lambdaと API Gatewayを利用し、AWSサーバーレスの構築を学びましょう!

構成図はこんな感じです。
diagram.PNG

処理の流れとしては、ユーザーから連絡フォームより入力した情報を収集し、クライアント側のブラウザからAmazon API Gateway RESTfulサービスに投稿します。Amazon API Gatewayは、収集されたユーザー情報をAWS lambda関数に渡します。AWS Lambda関数は、Eメールを自動生成し、Amazon SESを使用してメールサーバーに転送します。

1.フォームを作成

  • index.htmlファイルを作成し、下記のコードを貼り付けます。
index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>連絡フォーム</title>
    <script src="script.js"></script>
    <link rel="stylesheet " href="style.css ">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js "></script>

</head>


<body>
    <h1>連絡フォーム</h1>
    <form id="contact-form" method="post">
        <h4>名前:</h4>
        <input type="text" id="name-input" placeholder="お名前を入力してください。" class="form-control" /><br />
        <h4>件名:</h4>
        <input type="subject " id="subject " " placeholder="お名前を入力してください。 " class="form-control " /><br />
        <h4>メール:</h4>
        <input type="email " id="email-input " placeholder="メールを入力してください。 " class="form-control " /><br />
        <h4>メッセージ:</h4>
        <textarea id="description-input " rows="3 " placeholder="メッセージを入力してください。 " class="form-control "></textarea><br />
        <div class="button-wrapper "><button type="button " onClick="submitToAPI(event) " class="btn btn-lg ">送信</button></div>

    </form>
</body>
</html>

ブラウザでプレビューを確認できます。こんな感じです。
basic-html-form.PNG

  • 見た目はあまり良くないですので、改善しましょう。style.cssを同じフォルダ内で作成し、下記のコードを貼り付けます。
style.css
* {
    box-sizing: border-box;
}

body {
    text-align: center;
    color: rgb(255, 255, 255);
    background-color: #194680;
    padding-top: 20px;
}

#contact-form {
    margin: auto;
    text-align: left;
    padding: 20px;
    max-width: 430px;
    border: white solid 2px;
    border-radius: 10px;
}

button {
    text-align: center;
    margin-top: 20px;
    font-size: 18px;
    border: white solid 2px;
    background-color: white;
    border-radius: 5px;
}

.button-wrapper {
    text-align: center;
}

input,
textarea {
    width: 380px;
}

textarea {
    height: 200px;
    padding: 10px;
}

input {
    height: 35px;
}

ブラウザではこんな感じになります。
styled-form.PNG

2. AWS Lambda関数の定義

  • AWS Lambda画面で新規関数を作成し、hello-worldのnodejsテンプレを選択します。
    lambda2.PNG

  • 基本的な情報の画面では関数を任意に名付けて、実行ロールは最初選択のままで進みます。
    lambda3.PNG

  • 次の画面の関数コードに下記のコードを切り替えます。

index.js
var AWS = require('aws-sdk');
var ses = new AWS.SES();

//宛先のメールアドレスを入力してください。
var RECEIVER = 'example-receiver@gmail.com';

//送信先のメールアドレスを入力してください。
//送信先のメールはAWSに認証登録する必要があります。
var SENDER = 'example-sender@gmail.com';

//クライアントへのレスポンスヘッダーを設定
var response = {
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": 'application/json',
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Accept",
        "Access-Control-Allow-Methods": "POST,GET,OPTIONS",
        "Access-Control-Allow-Origin": "*",
    },
    "statusCode": 200,
    "body": "{\"result\": \"テスト成功です!\"}"
};

exports.handler = function(event, context, callback) {
    console.log('受理したイベント:', event);
    sendEmail(event, function(err, data) {
        context.done(err, null);
    });
    callback(null, response);
};

//メール送信処理
function sendEmail(event, done) {
    var params = {
        Destination: {
            ToAddresses: [
                RECEIVER
            ]
        },
        Message: {
            Body: {
                Text: {
                    Data: '● お名前: ' + event.name + '\n● 件名: ' + event.subject + '\n● メール: \n' + event.email + '\n● 内容: \n' + event.desc,
                    Charset: 'UTF-8'
                }
            },
            Subject: {
                Data: event.name + 'からの連絡フォームが来ました!',
                Charset: 'UTF-8'
            }
        },
        Source: SENDER
    };
    ses.sendEmail(params, done);
    return {
        response,
        body: JSON.stringify(params),
        statusCode: 200
    }
}
  • 同じLambda管理画面で、上にスクロールし、アクセス権限のタブに切り替え、ロール名のリンクをクリックします。

lambda-policy.PNG

  • 当関数のIAMポリシーをSES権限を追加します。IAM画面で該当ポリシーをクリックしポリシーを編集 ->JSONのフォーマットに切り替えし、下記のコードを追加します。

lambda-policy3.PNG

{
  "Sid": "FormPolicy",
  "Effect": "Allow",
  "Action": "ses:SendEmail",
  "Resource": "*"
}

こんな感じです。
lambda-policy2.PNG

3. APIゲートウェイでAPI構築

  • AWSメインコンソールよりAPI Gateway管理画面にアクセスし、APIを作成をクリックします。
    api.PNG

  • 次の画面ではREST API構築をクリックします。

api2.PNG

  • 次の画面はREST APIの選択のままで、API名前を任意に付けます。

api2a.PNG

  • 次の画面は上にあるアクションをクリックし、メッソド作成をクリック、POSTを選択します。

api3.PNG

  • 右側の設定画面では、統合タイプLambdaで、Lambda関数は先ほど作成したものを選択します。

api4b.PNG

  • CORSエラーにならないように、CORS機能も有効にします。アクションのドロップダウンをCORS有効化をクリックし、設定はそのままで保存します。

api4c.PNG

  • API Gatewayの最新アップデートでは、ゲートウェイのレスポンスも設定しないと、CORSエラーが発生するため、設定しましょう。右のゲートウェイレスポンス → DEFAULT 4XX編集の順番にナビゲートします

api5a.PNG

  • レスポンスヘッダーに下記の値を追加し、保存します。
  Access-Control-Allow-Origin: '*'

こんな感じです。
api5b.PNG

これでAPIの構築は完了です。

4. SES送信用のメール検証登録

mail.PNG

verifiedの状態になったら、OKです!

5.全コンポーネントを連携する

  • 最後のステップではすべてを繋ぎます!index.htmlと同じいフォルダでscript.jsを作成し、下記のコードを貼り付け、var URLurlの2ヶ所に先ほど設定したAPI GatewayのエンドポイントURLを差し替えます。
script.js
function submitToAPI(e) {
    e.preventDefault();
    //設定したAPI GatewayのエンドポイントURLをここに入れます。
    var URL = "API-GATEWAY-ENDPOINT-URL";

    //フォームの入力値をチェック
    var name = /[A-Za-z]{1}[A-Za-z]/;
    if (!name.test($("#name-input").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    var subject = /[A-Za-z]{1}[A-Za-z]/;
    if (!subject.test($("#subject").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    if ($("#email-input").val() == "") {
        alert("メールを入力してください。");
        return;
    }

    var email = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,6})?$/;
    if (!email.test($("#email-input").val())) {
        alert("メールアドレスは正しくありません。");
        return;
    }

    var name = $("#name-input").val();
    var subject = $("#subject").val();
    var email = $("#email-input").val();
    var desc = $("#description-input").val();
    var data = {
        name: name,
        subject: subject,
        email: email,
        desc: desc
    };

    $.ajax({
        type: "POST",
        //設定したAPI GatewayのエンドポイントURLをここに入れます。
        url: "API-GATEWAY-ENDPOINT-URL",
        dataType: "json",
        crossDomain: "true",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(data),


        success: function() {
            // フォームをクリアし、送信成功のメッセージを表示する
            alert("メッセージが送信されました!");
            document.getElementById("contact-form").reset();
            location.reload();
        },
        error: function() {
            // 送信エラーのメッセージを表示する
            alert("メッセージ送信失敗!");
        }
    });
}

6. 結論

これでAmazon S3で静的Webサイトをホストしても、AWSアーキテクチャを利用し、ユーザよりサイト管理者へフォーム送信機能を実装できました!

初投稿です、、いかがでしょうか(´・ω・`)
コメント、フィードバックなど是非よろしくお願いいたします!
それでは、また今度! (`・∀・´)ノ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambda、API Gateway、SESを使用し、S3静的ウェブサイトの動的な連絡フォームを作りましょう!?

AWS S3にサイトを安くて簡単にホスティングできますが、S3は静的なサイトしかホスティングできないですので、従来ならバックエンド処理が必要となるフォーム送信の実装などは難しそうですね。しかし、クラウド時代ではそんな心配はありません!ソリューションはサーバーレスアーキテクチャです!AWSはすでに色々なサーバーレス構築のツールを提供しています。

今回はユーザからお問い合わせやフィードバックを送信するたびに、任意な宛先に通知メールを送信するシンプルなサーバーレスメールサービスを一緒に作ります。AWS LambdaとAPI Gatewayを利用し、簡単なAWSサーバーレス構築を紹介します。

構成図はこんな感じです。
diagram.PNG

処理の流れとしては、ユーザーから連絡フォームより入力した情報を収集し、クライアント側のブラウザからAmazon API Gateway RESTfulサービスに投稿します。Amazon API Gatewayは、収集されたユーザー情報をAWS lambda関数に渡します。AWS Lambda関数は、Eメールを自動生成し、Amazon SESを使用してメールサーバーに転送します。

1.フォームを作成

  • index.htmlファイルを作成し、下記のコードを貼り付けます。
index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>連絡フォーム</title>
    <script src="script.js"></script>
    <link rel="stylesheet " href="style.css ">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js "></script>

</head>


<body>
    <h1>連絡フォーム</h1>
    <form id="contact-form" method="post">
        <h4>名前:</h4>
        <input type="text" id="name-input" placeholder="お名前を入力してください。" class="form-control" /><br />
        <h4>件名:</h4>
        <input type="subject " id="subject " " placeholder="お名前を入力してください。 " class="form-control " /><br />
        <h4>メール:</h4>
        <input type="email " id="email-input " placeholder="メールを入力してください。 " class="form-control " /><br />
        <h4>メッセージ:</h4>
        <textarea id="description-input " rows="3 " placeholder="メッセージを入力してください。 " class="form-control "></textarea><br />
        <div class="button-wrapper "><button type="button " onClick="submitToAPI(event) " class="btn btn-lg ">送信</button></div>

    </form>
</body>
</html>

ブラウザでプレビューを確認できます。こんな感じです。
basic-html-form.PNG

  • 見た目はあまり良くないですので、改善しましょう。style.cssを同じフォルダ内で作成し、下記のコードを貼り付けます。
style.css
* {
    box-sizing: border-box;
}

body {
    text-align: center;
    color: rgb(255, 255, 255);
    background-color: #194680;
    padding-top: 20px;
}

#contact-form {
    margin: auto;
    text-align: left;
    padding: 20px;
    max-width: 430px;
    border: white solid 2px;
    border-radius: 10px;
}

button {
    text-align: center;
    margin-top: 20px;
    font-size: 18px;
    border: white solid 2px;
    background-color: white;
    border-radius: 5px;
}

.button-wrapper {
    text-align: center;
}

input,
textarea {
    width: 380px;
}

textarea {
    height: 200px;
    padding: 10px;
}

input {
    height: 35px;
}

ブラウザではこんな感じになります。
styled-form.PNG

2. AWS Lambda関数の定義

  • AWS Lambda画面で新規関数を作成し、hello-worldのnodejsテンプレを選択します。
    lambda2.PNG

  • 基本的な情報の画面では関数を任意に名付けて、実行ロールは最初選択のままで進みます。
    lambda3.PNG

  • 次の画面の関数コードに下記のコードを切り替えます。

index.js
var AWS = require('aws-sdk');
var ses = new AWS.SES();

//宛先のメールアドレスを入力してください。
var RECEIVER = 'example-receiver@gmail.com';

//送信先のメールアドレスを入力してください。
//送信先のメールはAWSに認証登録する必要があります。
var SENDER = 'example-sender@gmail.com';

//クライアントへのレスポンスヘッダーを設定
var response = {
    "isBase64Encoded": false,
    "headers": {
        "Content-Type": 'application/json',
        "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,Accept",
        "Access-Control-Allow-Methods": "POST,GET,OPTIONS",
        "Access-Control-Allow-Origin": "*",
    },
    "statusCode": 200,
    "body": "{\"result\": \"テスト成功です!\"}"
};

exports.handler = function(event, context, callback) {
    console.log('受理したイベント:', event);
    sendEmail(event, function(err, data) {
        context.done(err, null);
    });
    callback(null, response);
};

//メール送信処理
function sendEmail(event, done) {
    var params = {
        Destination: {
            ToAddresses: [
                RECEIVER
            ]
        },
        Message: {
            Body: {
                Text: {
                    Data: '● お名前: ' + event.name + '\n● 件名: ' + event.subject + '\n● メール: \n' + event.email + '\n● 内容: \n' + event.desc,
                    Charset: 'UTF-8'
                }
            },
            Subject: {
                Data: event.name + 'からの連絡フォームが来ました!',
                Charset: 'UTF-8'
            }
        },
        Source: SENDER
    };
    ses.sendEmail(params, done);
    return {
        response,
        body: JSON.stringify(params),
        statusCode: 200
    }
}
  • 同じLambda管理画面で、上にスクロールし、アクセス権限のタブに切り替え、ロール名のリンクをクリックします。

lambda-policy.PNG

  • 当関数のIAMポリシーをSES権限を追加します。IAM画面で該当ポリシーをクリックしポリシーを編集 ->JSONのフォーマットに切り替えし、下記のコードを追加します。

lambda-policy3.PNG

{
  "Sid": "FormPolicy",
  "Effect": "Allow",
  "Action": "ses:SendEmail",
  "Resource": "*"
}

こんな感じです。
lambda-policy2.PNG

3. APIゲートウェイでAPI構築

  • AWSメインコンソールよりAPI Gateway管理画面にアクセスし、APIを作成をクリックします。
    api.PNG

  • 次の画面ではREST API構築をクリックします。

api2.PNG

  • 次の画面はREST APIの選択のままで、API名前を任意に付けます。

api2a.PNG

  • 次の画面は上にあるアクションをクリックし、メッソド作成をクリック、POSTを選択します。

api3.PNG

  • 右側の設定画面では、統合タイプLambdaで、Lambda関数は先ほど作成したものを選択します。

api4b.PNG

  • CORSエラーにならないように、CORS機能も有効にします。アクションのドロップダウンをCORS有効化をクリックし、設定はそのままで保存します。

api4c.PNG

  • API Gatewayの最新アップデートでは、ゲートウェイのレスポンスも設定しないと、CORSエラーが発生するため、設定しましょう。右のゲートウェイレスポンス → DEFAULT 4XX編集の順番にナビゲートします

api5a.PNG

  • レスポンスヘッダーに下記の値を追加し、保存します。
  Access-Control-Allow-Origin: '*'

こんな感じです。
api5b.PNG

これでAPIの構築は完了です。

4. SES送信用のメール検証登録

mail.PNG

verifiedの状態になったら、OKです!

5.全コンポーネントを連携する

  • 最後のステップではすべてを繋ぎます!index.htmlと同じいフォルダでscript.jsを作成し、下記のコードを貼り付け、var URLurlの2ヶ所に先ほど設定したAPI GatewayのエンドポイントURLを差し替えます。
script.js
function submitToAPI(e) {
    e.preventDefault();
    //設定したAPI GatewayのエンドポイントURLをここに入れます。
    var URL = "API-GATEWAY-ENDPOINT-URL";

    //フォームの入力値をチェック
    var name = /[A-Za-z]{1}[A-Za-z]/;
    if (!name.test($("#name-input").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    var subject = /[A-Za-z]{1}[A-Za-z]/;
    if (!subject.test($("#subject").val())) {
        alert("2文字以上記入してください。");
        return;
    }
    if ($("#email-input").val() == "") {
        alert("メールを入力してください。");
        return;
    }

    var email = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,6})?$/;
    if (!email.test($("#email-input").val())) {
        alert("メールアドレスは正しくありません。");
        return;
    }

    var name = $("#name-input").val();
    var subject = $("#subject").val();
    var email = $("#email-input").val();
    var desc = $("#description-input").val();
    var data = {
        name: name,
        subject: subject,
        email: email,
        desc: desc
    };

    $.ajax({
        type: "POST",
        //設定したAPI GatewayのエンドポイントURLをここに入れます。
        url: "API-GATEWAY-ENDPOINT-URL",
        dataType: "json",
        crossDomain: "true",
        contentType: "application/json; charset=utf-8",
        data: JSON.stringify(data),


        success: function() {
            // フォームをクリアし、送信成功のメッセージを表示する
            alert("メッセージが送信されました!");
            document.getElementById("contact-form").reset();
            location.reload();
        },
        error: function() {
            // 送信エラーのメッセージを表示する
            alert("メッセージ送信失敗!");
        }
    });
}

6. 結論

フォームより受信するメールはこんな感じです。
demo.PNG

これでAmazon S3で静的Webサイトをホストしても、AWSアーキテクチャを利用し、ユーザよりサイト管理者へフォーム送信機能を実装できました!

初投稿です、、いかがでしょうか(´・ω・`)
コメント、フィードバックなど是非よろしくお願いいたします!
それでは、また今度! (`・∀・´)ノ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS 認定ソリューションアーキテクトアソシエイト(SAA-C02)取得するまでにやったこと

AWS-Cloud-alt_light-bg@4x.pngAWS-Cloud_light-bg@4x.png

はじめに

DX 技術本部の yu-yama です。
AWS 認定ソリューションアーキテクトアソシエイトを取得したので取得までの流れを記します。
AWS 経験は 2, 3 年で、コアサービスは一通り触っているが、それぞれ本番環境などで使い込んではいないというレベルです。

TL;DR

学習期間

  • 一カ月と少し(40 日)

教材候補

ハンズオン系

これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座(SAA-C02試験対応版)

  • 特徴

    • ハンズオンメインの動画で定番。
    • 難易度は Udemy の 21 時間の模擬問題と同等。
    • 模擬問題が 3 つついている。
    • セールで買うとお得。
  • メモ

    • 1.25 ~ 1.5 倍速がおススメと書かれているサイトもあったが、ハンズオンは 1 倍でも速いと感じる部分があり、停止しつつ進めた。リソースの作成にかかる時間は上記 21H に含まれていないので実際はもっとかかる。
    • 思ったより時間がかかったため、今回はこれのみで受験。

Web 問題集系

Udemy 【SAA-C02 版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6 回分 390 問)

  • 特徴

    • 難易度が本番よりも高い。
    • 出題範囲が広く、本番の問題と出題傾向が似ている。テスト後に、分野ごとに正答率が表示されることも魅力の一つ。各問題に対する解説も充実。
    • おススメのやり方:毎日 10~15 問くらい解く。そして、解説を読み込む。休みの日には、模擬問題 1 回分(65 問)を解く。
    • とのこと。
  • メモ

    • 上記 Udemy のハンズオンのものを買えば模擬問題が 3 つついているため、こちらは不要と判断。

AWS Web 問題集で学習しよう

  • 特徴

    • 難易度は本番と同じくらいか、ちょっと易しいくらい。とのこと。
  • メモ

    • 見た感じ良さそうだった。当初 Udemy ハンズオン後、この問題集を購入し解こうと思っていた。

Amazon AWS 資格取得のための演習問題集(完全無料、オリジナル)

  • 特徴

    • 完全無料
  • メモ

    • 各 SAA 取得者の感想を見ていると、上記 koiwaclub の次によく挙げられていた。

参考書系

  • ハンズオンで使用したサービスの知識を深めるために利用する

徹底攻略 AWS 認定 ソリューションアーキテクト アソシエイト教科書

  • 特徴

    • 定番の黒本とのこと。
  • メモ

    • 書籍は情報の更新に追随するのが難しい。AWS の各サービスは日々更新されているため、今回は不採用

AWS 認定資格試験テキスト AWS 認定 ソリューションアーキテクト

  • メモ
    • 上記と同じ理由で不採用。昔は良かったのかもしれない。

学習方法

  1. 試験ガイドを参照し、試験範囲を確認する

  2. Udemy(ハンズオン)を全てやりきる
    ほぼ毎日少しづつ進めた

  3. Udemy(ハンズオン)についている問題集をやる(1 周目)
    正解・不正解関わらず解説も全部読む

  4. Udemy(ハンズオン)についている問題集をやる(2 周目)
    間違えた問題と見直したい問題の解説を読む

模擬試験

  • 各 SAA 取得者の感想で試験の形式に慣れるため受けるべしと書かれているが、Udemy ハンズオン教材の模擬試験を解いている場合は不要と思います。

本番当日

  • 予約しようと思った時点で受けたい日の午前の回が埋まっていたため午後 13:30 ~受験。

  • 午前中に模擬問題で間違えた箇所を見直しました。

  • ピアソン VUE で受験しました。

    • PSI は監督員と遠隔でチャットをしながら、カメラで常時監視されながらの試験
    • 「キーボードから手を離してはいけない」とかプレッシャーが半端ないとか見たので。
  • 受験開始予定時刻 30 分前に到着。30 分前に到着しても待つことなく受けられました。

  • 必要なものは身分証明 2 点(免許証+保険証)のみ。

  • 会場入って、名前を言って身分証明見せて本人確認後、規約を読む。読んだあと、写真撮影。

  • すぐにテストするか聞かれるので全ての荷物をロッカーに預けて試験。

本番

  • まず全問解いて後で見直すものと確信を持てるものを分ける。65 問解いたところで大体 1 時間弱残るはず。
  • 後で見直すものを一通り見直して試験終了
  • 試験終了後アンケートがあり、終わると結果が表示されます。結果表示が地味なのでスキップしてしまわないように注意。

感想

  • 思ったより難しかったです。正解が分かるというよりは不正解は分かるという感じで。あまり自信なく"試験を終了する"ボタンを押したのですが合格最低スコア+100 程度でした。良かった・・・

  • 試験直前に見ていたメモを張り付けておきます。ここら辺で?があると多分落ちると思います。

-   S3(CORS やトラスファーアクセスレーション、ストレージタイプなど様々な中身の質問で 10 問弱はあったかと。かなりの頻度です)

    -   CORS:Cross-Origin Resource Sharding クロスドメインによるアクセス制御を可能にする
    -   トランスファーアクセスレーション: エッジロケーションを使ってクライアントと高速でデータをやり取りする
    -   S3 ストレージタイプ

        -   STANDARD
            -   複数個所にデータを複製するため耐久性が非常に高い
        -   STANDARD-IA
            -   可用性は 99.9%, 耐久性は 99.999999999%
            -   **低頻度** アクセスストレージ
            -   読み込みはすぐにできるため、突然の利用にも対応
            -   処理中のデータを保存する先としては不向き
            -   スタンダードに比べて安価
        -   One Zone-IA
            -   STANDARD-IA より安価
            -   データの冗長率が低い(1 つの AZ )、またデータの読み出しに対して課金あり
            -   一時的な置き場所の使い方
        -   RRS(非推奨)
            -   Reduced Reduncancy Storage
            -   低冗長化ストレージ
        -   Amazon Glacier
            -   最安のアーカイブ用ストレージ
            -   データ抽出にコストと時間(3-5H)を要する
            -   ライフサイクルマネジメントで指定
            -   長期保存向け(最低保持期間は 90 日のため、例えば 30 日で削除しても 90 日分の料金を取られてしまう)
            -   アーカイブを取り出す 3 つの機能として、「迅速」、「標準」、「大容量」が用意されている

-   CloudFront(S3 との組合せなど含めて)
    -   OAI: Origin Access Identity ユーザーがコンテンツにアクセスできなくなる日時の制御や、コンテンツへのアクセスに使用できる IP アドレスの制御が可能
-   SQS を組み入れた構成(これだけで 3 ~ 4 問)
-   VPC の設定
-   Lambda
-   RDS の負荷軽減策や冗長構成を問う問題
-   CloudFormation テンプレート内の記述の解読
    -   Egress:true:アウトバウンド
    -   Egress:false:インバウンド
-   EC2 はリザーブドインスタンスなどのコスト割安な選択など
-   EBS
    -   SSD
        -   汎用 SSD:汎用
        -   プロビジョンド IOPS(SSD):高い I/O 性能に依存する NoSQL やアプリ
            -   一番コストはかかる
    -   HDD
        -   スループット最適化 HDD
            -   ビッグデータ処理
            -   プロビジョンド IOPS よりも安価
        -   コールド HDD
            -   ログデータなどアクセス頻度が低いデータ向け
            -   バックアップやアーカイブ
            -   低コストの HDD ボリューム
-   AutoScaling の様々な使い方
-   Snowball か Snowball エッジの選択
-   ECS

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS 認定セキュリティ – 専門知識(SCS-C01)合格体験記

AWS 認定セキュリティ – 専門知識(SCS-C01) の合格体験記を書きます。

1.はじめに

「DX時代、セキュリティを語れなければエンジニアとして価値がない」
と会社の上司が言っていました。

「おっしゃるとおりです!(従順)」
私はSCS-C01を受験することに決めました。

AWS認定セキュリティ(SCS-C01)とは?

SCS-C01は、権限/認証/暗号化などAWS上のセキュリティを対象とした認定試験です。
認定試験概要ページのリンク

項目 内容 備考
形式 単一/複数選択形式
実施形式 テストセンター
時間 170分
受験料金 30,000 円(税別) 模擬試験 4,000 円(税別)
言語 英語、日本語、韓国語、中国語 (簡体字)
最低合格スコア 750 / 1000 点

2.私のスペック

受験を決めた段階で、私のスペックは以下のとおりです。

  • オンプレでインフラ設計構築を6年ほど
  • AWSでシステム設計構築を1年ほど
  • 「ソリューションアーキテクト – プロフェッショナル」を取得済み

3.準備

受験までに行ったことを時系列順に記載します。

①出題範囲や合格ラインの確認

②問題の雰囲気を把握

③模擬結果からどれだけ対策が必要か判断

  • 公式の模擬試験
    • 出題数は20問
      • 総合スコア: 75%
      • 1.0  Incident Response: 66%
      • 2.0  Logging and Monitoring: 100%
      • 3.0  Infrastructure Security: 83%
      • 4.0  Identity and Access Management: 75%
      • 5.0  Data Protection: 33%

結果は数時間後メールで通知されます。
問題を解いている時点でSAPより遥かに簡単と感じたので、対策に長期間かけないことにしました。
(参考)実際にかかった試験対策の時間は、SAPは4ヶ月くらい、SCSは1週間くらい

④対策:公式ドキュメントや実機操作で弱点補完

SCS-C01はいわゆる試験対策向け参考書&問題集がなさそうだったので、公式ドキュメントでがんばって対策しました。

※ちなみに、「公式の有償トレーニングは絶対に受けない」という謎のプライドがあります。
 他のベンダー資格然り、公式の高価な教材を買ってまで受かりたくない気持ちが強いです。

模擬試験から、設計と構築のどちらの知識も網羅するように理解しました。

  • well-architectedでベストプラクティス把握
  • AWS Docsと実機操作で構築知識を習得
    • 「Incident Response」分野の対策
      • ロギング方法からアラート/分析方法まで理解、実際に作ってみる
        • CloudTrail
        • Athena
        • サーバロギング(LogsAgent)
        • CloudWatchLogs/ログメトリクス
        • GuardDuty
        • Inspector
        • AWS Config
        • VPCフローログ
        • etc
      • インシデント問題解決のための方法を理解、実際に作ってみる
        • IAM/S3/KMSなどPolicy書き方
        • SSM PatchManager / RunCommand / Secrets Manager
        • etc
    • 「Data Protection」分野の対策
      • KMSを操作してみる
        • CMKやAWS管理キーなどの種類把握
        • Policyの書き方
      • ストレージ/NWサービスの暗号化方法を理解、作ってみる
        • S3バケット暗号化
        • EBS暗号化
        • ELB+CertificateManager
        • インターネット接続+VPN
        • DirectConnect+VPN(座学だけ)

4.試験

  • 場所
    • 歌舞伎座テストセンター
  • 実際の試験について
    • 個人的に以下の印象でした。
      • 基礎を理解していればわかる6割
      • 深い内容を問われる2割
      • 意味不明な文章が1割
  • 結果
    • 787点で合格でした(あまり高得点ではないですね笑)

goukaku.png

5.自身の振り返りとオススメ対策

概ね対策通りに問題が出題されたように思います。
それでも高得点が取れなかった理由は、各分野の理解度が中途半端だったからだと思います。

あくまで個人的な印象ですが、これからSCS試験を受ける方は以下の知識が前提にあると勉強しやすいと思います。

  • AWSにおけるロギングの全体像を掴む
    • リンクがいい感じに纏まっていました。
  • サイバー攻撃の種類と対策方法の把握
    • リンクがいい感じに纏まっていました。
  • リソースpolicy、ユーザーpolicyをゼロベースで書けるくらいの理解度になる
    • AWS Docsを読みながら実際に操作してみるといいです。

6.まとめ

よく耳にするセキュリティという言葉ですが、AWSにおいても例外ではありません。
むしろ、AWSは何よりもセキュリティを重視する方針だそうです。
※AWS Innovateで登壇者の方が言っていました。

SCS-C01を勉強することでセキュリティについて理解し、AWSというサービスを最大限に有効活用できるようになりたいですね。

付録:合格者特典について

今回、SCS合格した際の特典は以下の内容でした。
オンラインストアに関しては、SAAやSAPとも違うラインナップでした。

  • 認定のデジタルアイコン
  • 次の試験が半額になる割引コード
  • AWS認定LinkedInコミュニティへのアクセス権限
  • AWS認定試験開発ワークショップに参加権
  • 次の模擬試験が無料になる割引コード
  • オンラインストアのアクセスコード
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AmazonConnect】エージェント階層

はじめに

弊社社内電話をAmazonConnectへ移行しました。
学んだことを記載していきます。

エージェント階層-概要

エージェント階層は、レポート作成のためにエージェントをチームやグループに整理する方法です。
5階層までエージェント階層を定義することができ、いずれかの階層にエージェントを紐づけることができます。
以下、エージェント階層作成手順を記載します。

【必要なアクセス許可】
エージェント階層を作成するには、セキュリティプロファイルに [表示 - エージェント階層] アクセス許可が必要です。

※Adminはデフォルトで必要なアクセス許可が付与されてます。
※注記
エージェント階層には場所とスキルセットのデータが含まれる場合があるため、エージェント階層情報をリアルタイムメトリクスレポートで表示するためにも、このアクセス許可が必要です。
image.png
その他、詳細はAWS公式ドキュメントをご参照ください。
AWS > ドキュメント > Amazon Connect > 管理者ガイド >エージェントのセットアップ階層

エージェント階層-作成

①、[ユーザー]、[エージェント階層] を選択します。
image.png
②、名前を入力し、[+] を選択して、階層の最初のレベルを作成します。
 ※名前は任意です。
image.png
③、[+] を選択して、階層にレベルを追加します。
image.png
④、[保存] を選択して、変更を適用するか、[キャンセル] を選択して元に戻します。

 ヒント
 [保存] ボタンがアクティブでない場合は、エージェント階層を作成または編集するアクセス許可がありません。
image.png

エージェント階層-階層追加

階層を作成したら、グループ、チーム、およびエージェントを上から順に追加できます。
各ユーザーの階層への紐づけは、ユーザー管理画面で紐づけます。

①、[ユーザー]、[エージェント階層] を選択します。
image.png

②、[+]追加ボタンをクリック。階層に追加したい名前を入力し、レ点をクリック。
image.png
補足:level5階層まで追加したイメージ。
image.png

③、ユーザー管理にてユーザーと階層を紐づけるため、ユーザーの編集画面を表示する。
image.png

④、ユーザと紐づけたい階層を選択し、保存ボタンをクリック
image.png

まとめ

AmazonConnectを使用するうえで必須の機能ではありませんが、エージェントの場所およびスキルセットに基づいて整理すると、検索時やレポート出力の際に活用できるのでとても便利です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AmazonConnect】クイック接続(問い合わせの転送の設定)

はじめに

弊社社内電話をAmazonConnectへ移行しました。
学んだことを記載していきます。

クイック接続-概要

クイック接続は、通話転送機能のことです。
転送先のリストを事前作成することで、エージェントが通話時に転送リストを利用し転送することが可能です。

クイック接続を作成するときは、次のいずれかの宛先を指定できます。

・[エージェント] — 問い合わせフローの一部として特定のエージェントに転送されます。
コントロールパネル (CCP) のクイック接続のリストに個別に表示する場合は、各エージェントを手動で追加する必要があります。
・ [キュー] — 問い合わせフローの一部としてキューに転送されます。
・ [外部] — 外線番号を直接入力することで、外部へ転送されます。

【重要】
・エージェントとキューのクイック接続は、エージェントが問い合わせを転送するときにのみ CCP に表示されます。
・CCPに表示するには以下ステップ1、ステップ2の作業が必要です。

その他、詳細はAWS公式ドキュメントをご参照ください。

以下、ステップ1より作成手順を記載していきます。

ステップ 1: クイック接続を作成する

1.クイック接続を作成するには
[ルーティング]、[クイック接続]、[新規追加] の順に選択します。
image.png
image.png
2.接続の名前を入力します。タイプを選択し、宛先 (電話番号やエージェント名など)、問い合わせフロー (該当する場合)、説明を指定します。
image.png
問い合わせフローのタイプにご留意ください。

3.クイック接続をさらに追加するには、[新規追加] を選択します。
4.[保存] を選択します。
image.png

ステップ 2: エージェントがクイック接続を確認できるようにする

エージェントが問い合わせを転送したときに CCP でクイック接続を確認できるようにするには
1.クイック接続を作成した後で、[Routing]、[Queues] に移動し、問い合わせのルーティング先として適切なキューを選択します。
image.png
image.png
2.[キューの編集] ページの [クイック接続] ボックスで、作成したクイック接続を検索します。
image.png
3.クイック接続を選択し、[保存] を選択します。
image.png

ヒント
エージェントには、ルーティングプロファイル内にあるキューのすべてのクイック接続が表示されます。

クイック接続-通話の転送管理

転送が開始されると、お客様は保留になり、転送先に接続されます。
image.png

まとめ

予めクイック接続(転送リスト)を作成しておくと、スムーズに担当者へ転送することが可能になるのでお勧めです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon Chime SDK で16ビデオ通話セッションを超えてみる。

この記事はこちらでも紹介しています。
https://cloud.flect.co.jp/entry/2020/07/08/153456

前回は、Amazon Chime SDKの新機能を使って作ったホワイトボード機能についてご紹介しました。

https://qiita.com/wok/items/83a8700d5c09cc1747d0

今回もAmazon Chime SDKについての話になります。
少しトリッキーな使い方を実験をしてみたのでご紹介したいと思います。

具体的には、単一のSPA(Single Page Application)から複数の会議室に同時に参加できるかを実験しました。
これができれば、次のことが可能になると考えています。

  • (オンラインセミナーなどで、)Amazon Chime SDKの会議室あたりの最大接続数を超える人数に向けた講義が可能になる。
  • (オンラインセミナーなどで、)グループ単位で受講者のプライバシーを保護できる。

実験の結果、次のデモのように、単一のユーザ(登壇者)が複数の会議室に参加することで、会議室あたりのビデオ通話可能な最大ユーザ数(16名)を超えるユーザに対し、同時にビデオ通話ができることが確認できました。
また、登壇者以外の各会議室の参加者(受講者)間の会話や映像は他の会議室には伝わらないため、会議室間でプライバシーも保護することができます。

ezgif-5-2cd8468121a2.gif

このデモでは登壇者が2つの会議室に参加することで、30名のユーザとビデオ通話しています。1

Amazon Chime SDKのビデオ通話セッション数と、大規模なオンラインセミナー

私が所属する会社は、AWS, GCP, Azureを代表とするマルチクラウドを扱えることを強みとしたクラウドインテグレータです。
当然、オンライン会議システムのソリューションを提案する場合も、Amazon Chime SDKと他のオンライン会議システムの強み弱みを比較検討するわけですが、
Amazon Chime SDKは会議室あたりのビデオ通話の最大参加者数(セッション数)が、例えばZoomのビデオウェビナープランなどの100名と比較して、16名と少ないです(2020年7月現在)。2
とはいえ、Amazon Chime SDKには、オンライン会議への接続形態として、下記の2パターンあり音声通話参加者は250名まで接続可能です。

image.png

そして、必要に応じてオンラインのまま、16のビデオ通話セッションを音声通話参加者に割当直してビデオ通話参加者にすることができます(下図)。
つまり、例えば発言中のユーザにビデオ通話セッションを割り当て直して、顔を映すということができます。

対面の講演やセミナーを考えると、質疑の場面以外では受講者が他の受講者の顔を見ることは、あまり多くないと思います。
なので、発表中は登壇者にビデオ通話セッションを割り当てて、質疑の際に質問者(特定の受講者)にビデオ通話セッションを割り当てるという構成にすれば、
Amazon Chime SDKのビデオ通話セッション数でも十分な場合も多いのではないかと考えられます。
image.png

しかし、音声通話参加者が250名を超える場合や、下図のように登壇者側が16名以上の受講者の反応をビデオ映像で見たい場合などには、普通にAmazon Chime SDKを使うだけでは対応できません。
もし、受講者を複数の会議室に分散して参加させ、それらの会議室に登壇者が同時に参加できるようになれば、これを解決できると思われます。
image.png

なお、少し横道にそれますが、16名を超える登壇者がいる場合はAmazon Chime SDKだと対応しきれないかもしれません。
この場合でも適宜ビデオ通話セッションを割り当て直せば十分なことも多いとは思いますが、
討論会でパネリスト(≒登壇者)のリアクションを常時ビデオ映像として配信したいといった要件がある場合は対応が難しいと思われます。

グループで参加したいオンラインセミナー

また、私個人が経験したことではありますが、オンラインの勉強会に参加したときに、発言するとそれが全体に伝わるので躊躇してしまうことがありました。
簡単な理解の確認であれば、横にいる同僚や知り合いにコソコソ話をして確認したいところですが、オンラインの勉強会だとslackなど別のコミュニケーションツールを使って会話をする必要があって面倒です。
最悪の場合、別のツールなのでこちらが話しかけても気づいてもらえない場合があります(涙)。
オンラインセミナーの受講にグループで参加して、受講中はグループ内だけでコミュニケーションが取れる方法があればありがたいと思いました。

また、セミナーの内容によっては、遠方の家族、親戚も一緒にオンラインセミナーで勉強したいが、他人には参加してることが知られたくないようなものがあるかもしれません。
例えば、医療に関わる話であったり、受験や結婚、資産運用などがそれに当たるかもしれません。
こういったものは、シビアな内容のものが多いので家族間で適宜感想を確認しながら受講するということが多いのではないかと思います。

これらについても、下図のように家族やグループ毎に会議室を割り当てて、それらの会議室に登壇者が同時に参加できるようになれば、会議室内の会話や映像は他の会議室には伝わらないので、解決できそうです。
image.png

実装のポイント

ということで、今回はSPAで上記のような構成を作れるかを実験してみました。
今回の実験の内容を含むデモプログラム全体は下記のgitリポジトリにアップしてありますので、ポイントだけ確認していきます。

といっても、特に難しいところはありません。
基本的には単一の会議室に入る処理を繰り返し、それぞれのエンドポイントや参加者を管理しておくようにしておくだけでできます。

たとえば、今回のデモプログラムでは、参加中会議室をそれぞれJoinedMeetingというInterfaceで管理をしています。
このInterfaceには、会議室毎に作成した、MeetingSessionConfigurationMeetingSessionに加え、参加者情報やビデオタイルといった各会議室固有の情報を格納します。

export interface JoinedMeeting{
    meetingSessionConfiguration: MeetingSessionConfiguration,
    meetingSession: MeetingSession,
    roster: { [attendeeId: string]: Attendee },
    videoTileStates: { [id: number]: VideoTileState },
    outputAudioElement: HTMLAudioElement | null,
    <snip>
}

これをSPA全体で複数管理できるようにしてあげます。
今回のデモプログラムではmeetingIdをキーとした連想配列で管理しています。

export interface AppState {
    joinedMeetings : {[id:string]:JoinedMeeting},
    <snip>
}

そして、新しく会議室に参加するたびに、その会議室のMeetingSession.audioVideoからビデオ入力やマイク入力、スピーカーを設定すればよいです。
デバイスの選択し直しの場合も、同様にすべての参加中の会議室のMeetingSession.audioVideoから設定してあげればよいです。
例えば、マイク入力を切り替える場合は次のような感じになります。

    selectInputAudioDevice = (deviceId: string) => {
        const currentSettings = this.state.currentSettings // <- 使用するデバイスなどをグローバルに管理している前提。
        currentSettings.selectedInputAudioDevice = deviceId

        Object.keys(this.state.joinedMeetings).forEach((x:string)=>{
            this.state.joinedMeetings[x].meetingSession.audioVideo.chooseAudioInputDevice(deviceId); // <- ここで設定。
        })
        this.setState({ currentSettings: currentSettings })
    }

その他、考慮が必要なポイント

基本的には上記の通りで良いのですが、複数の会議室に入ると、その分の映像を送受信するデータ量が増加します。
これらについては、Amazon Chime SDKのAPIで送信データ量をコントロールできるようになっているので、そこで調整出来るようにしておくと良いと思います。
特に受講者側は、デフォルトで解像度を下げておくほうが良いかもしれません。


    selectInputVideoResolution = (value: string) =>{
        <snip>
        Object.keys(this.state.joinedMeetings).forEach((x:string)=>{
            this.state.joinedMeetings[x].meetingSession!.audioVideo.chooseVideoInputQuality(
                videoConfig.width, videoConfig.height, videoConfig.frameRate, videoConfig.maxBandwidthKbps // videoConfigで解像度やフレームレート、最大データ送信量が設定してある前提
            );
        })      
        <snip>
    }

FLECT Amazon Chime Meeting

今回説明した機能は、FLECT研究開発室が開発しているビデオ会議を使った新機能のテストベッドに組み込まれています。
下記のリポジトリに公開していますので、ご興味を持たれましたらアクセスしてみてください。

https://github.com/FLECT-DEV-TEAM/FLECT_Amazon_Chime_Meeting

最後に

今回は、Amazon Chime SDKを使ったSPAから複数の会議室に参加できるかを実験してみました。
ネットワークなど計算リソースに関して考慮すべき点は残っていますが、大枠としては可能であることがわかりました。
これにより、複数の会議室から大規模なオンラインセミナーの会場を構成できる見込みを得ることができました。
また、大規模な会場においてグループ間でのプライバシーを保護できる見込みも得ることができました。

弊社FLECTでは、Amazon Chime SDKを用いてこれらの機能を組み込んだシステムの提案、構築をしております。
興味をお持ちいただけたら是非ご相談ください。

次回は、またAmazon Chimeで遊ぶか、以前紹介したマルチバーコードリーダの技術的な内容をご紹介するかをしようと思ってます。
では。

参考・リソース

会議室の参加者のカメラ画像は、次のサイトの動画を用いて擬似的に作成しました。

https://pixabay.com/ja/videos


  1. このデモでは、動画をダミーのカメラデバイスに流し込むことで30名のユーザ(受講者)を模擬している。 

  2. Zoom [https://zoom.us/jp-jp/webinar.html:embed:cite

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CodeBuildでサポートされているはずのランタイムがインストールできない場合の対処

※ 2020年7月8日現在
※ AWSにはフィードバック済み

イメージのバージョンを上げると解決する。

現象

GitHubとAWS CodeBuildを連携して、テストの自動実行をしていた。
プロジェクトで利用しているNode.jsのバージョンを10から12に上げる機会があり、同じくCodeBuildで扱うNode.jsのバージョンも上げることにした。

CodeBuildで扱うNode.jsは、CodeBuildでサポートされているランタイムを利用していた。
また、バージョンはbuildspec.ymlで管理していた。

12.xに対応していない可能性があるので、ドキュメントも念のため確認した。

https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html
phases/install/runtime-versions
The following supported runtimes can be specified.

Runtime name Version Specific version Specific major and latest minor version Latest version Image/Images
nodejs 12 nodejs: 12 nodejs: 12.x nodejs: latest All Amazon Linux 2 images
Ubuntu standard:3.0
Ubuntu standard:4.0

(追記)AWSにフィードバックを送信したところ、"All Amazon Linux 2 images"の部分を"All supported Amazon Linux 2 images"に変更してくれました。

イメージはamazonlinux2-x86_64-standard:1.0を利用していた。
表のImage/Imagesの値には、All Amazon Linux 2 imagesと書かれている。
どうやらうまいこと12.xに上げることができそうだ。

そこで、buildspec.ymlの内容を以下のように変更してみた。

version: 0.2

phases:
  install:
    runtime-versions:
-     nodejs: 10
+     nodejs: 12

  pre_build:
    commands:
      - npm install

  build:
    commands:
      - npm test -- --coverage

artifacts:
  files:
    - coverage/lcov-report/**/*

早速CIを実行してみたら。。エラーが発生してしまった。

YAML_FILE_ERROR: Unknown runtime version named '12' of nodejs. This build image has the following versions: 10, 8

12がサポートされていない。。ドキュメントではサポートしている記載があるのにどうして。。
12という記述方法がまずかったのかもしれない。12.xで修正し、再度実行してみたが、今度は違うエラーが発生してしまった。

YAML_FILE_ERROR: Major version of alias '12.x' is not supported in runtime 'nodejs'.

対処

もしかしたらイメージに問題があるのかもしれない。
試しに利用しているイメージを以下のように変更してみた。

amazonlinux2-x86_64-standard:1.0amazonlinux2-x86_64-standard:3.0

CIを実行してみたところ、エラーが発生しなくなった!
どうやらAWSのドキュメントの修正漏れだったようだ。(もしくは私が何か見逃しているか)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon EC2 LinuxでLaravelのphp artisan db:seedが上手くいかない時

【エラーメッセージ】

[ec2-user@ip-172-31-11-201 sample]$ php artisan db:seed

In DatabaseSeeder.php line 14:

  Parse error: syntax error, unexpected end of file

【開発環境】

Amazon EC2 Linux
Windows 10 HOME
Apache/2.4.43
Laravel Framework 5.4.36
vsftpd: Ver 3.0.2

Tera Term 4.1.105
FFFTP Ver 4.7

【対応】

ローカルで同じファイルで実行出来たのでAWSに上げる過程で何をやったか?と考えてみると、、

FTPか?

まさかと思って文字コードの設定を見てみると

SJIS になっていた。。
(FFFTPでは初期設定してもローカル側の文字コードが勝手に変わる現象を思い出した。)

これをUTF8に直して再び 「php artisan db:seed」を実行すると

[ec2-user@ip-172-31-11-201 sample]$ php artisan db:seed
Seeding: BooksTableSeeder

と成功しテーブルへの設定も問題なし。

1.jpg

【参考】

Laravel入門 - 使い方チュートリアル

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AmazonConnect】問い合わせの検索

はじめに

弊社社内電話をAmazonConnectへ移行しました。
学んだことを記載していきます。

問い合わせの検索-概要

インスタンス内の問い合わせについて、2 年前までの問い合わせを一度に 14 日間検索できます。
特定のクエリの検索結果は、返される最初の 10,000 件に制限されます。

会話が記録されたかどうかを確認するには、[Manager monitor (Manager モニター] のアクセス許可を持つプロファイルに割り当てる必要があります。

その他、詳細はAWS公式ドキュメントをご参照ください。

以下、検索方法を記載していきます。

問い合わせの検索-検索方法

① [CallCenterManager] セキュリティプロファイルが割り当てられたユーザーアカウント、または[問い合わせの検索] のアクセス許可が有効になっているユーザーアカウントを使用して、AmazonConnect にログインします。※Admin権限にはデフォルトで付与されてます。

② Amazon Connect で、[メトリクスおよび品質]、[問い合わせの検索] の順に選択します。
image.png

③ ページのフィルターを使用して、検索を絞り込みます。

以下、各フィルターの説明をします。

■開始日、終了日
 開始日、終了日の範囲のデータを検索します。
 一度に最大 14 日間検索できます。

image.png

■タイムゾーン
 選択した都市の時間帯に合わせて検索します。
image.png

■初期化メソッド
 以下の一覧から特定の操作を検索します。
image.png

■電話番号
 指定の電話番号で検索します。
image.png

■キュー
 顧客がオペレータと通話する際の待ち行列を検索します。
image.png

■エージェントログイン
 特定のエージェントを指定し検索します。
image.png

■最小・最大やり取り時間(秒)
 最小・最大のやり取り時間を指定し検索します。
image.png

■チャネル
 顧客とやり取りした通信経路(音声、チャット)を選択して検索できます。
 ※音声の場合、顧客の電話番号を指定して検索できます。
 音声、チャットが選択されていない場合、検索はレポートの時間範囲内のすべての連絡先を返します。
image.png

④ 検索結果に追加の列を表示するには、[追加フィールド] を展開して、表示する他のデータを選択します。[適用] を選択して列を表示します。
※検索結果に追加して表示したい項目を選択できます。
image.png

まとめ

問い合わせの検索を活用することで、「いつ、だれが、だれと、どんな手段で、どのくらいやり取りしたのか」等が分かります!
特定エージェントへ負荷が偏っていないか・・・、特定のお客様からの架電が多い!等を分析し、意思決定に活かせると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Terraform 入門

概要

職場でTerraform勉強会を開催することになったので、勉強会の資料のベースとなる内容を記事にしました。
これからTerraformを触りはじめる方が最低限知っておけば良さそうだと思った内容をまとめています。この記事では、構築対象のリソースはAWSを想定しています。


対象読者

  • Terraformを使ったことがなく、これから触ってみたい方
  • AWSを手動で構築したことがあるけれど、コードで管理したことがない方

この記事のゴール

  • Terraformの概要を把握する
  • 基本的な文法を覚える
  • 必要なドキュメントから自力で情報を探せるようになる

この記事では、0からTerraformプロジェクトを構築できるようになることは目指していません。
(ディレクトリ構成の話などは長くなってしまうので、それはまた別の記事にまとめたい)


導入


Terraformとは

HashiCorpが開発しているインフラ構成をコードを使って管理するためのツールです。
HCLという記法でコードを記述し、インフラの状態を管理することができます。

この記事で取り上げるAWSだけでなく、複数のプロバイダーに対応しているのが特徴です。
Providers - Terraform by HashiCorp

  • AWS
  • Azure
  • GCP
  • Datadogなど

また、Terraform以外にもAWSリソースをコードで管理するためのツールとしてAWS公式が出しているものもあります。

  • AWS CloudFormation

    • AWS専用のツール
    • JSONまたはYAML形式でテンプレートを作成
  • AWS Cloud Development Kit (AWS CDK)

    • AWS専用のツール
    • プログラミング言語でリソースを定義(TypeScript, JavaScript, Python, Java, C#)
    • コードからCloudFormationのテンプレートが作成され実行される

Terraformを使うと何が嬉しいのか

  • インフラの構築や変更を自動化できる
    • 操作ミスを防ぐことができる
    • 複数環境に構築する場合など、作業を減らすことができる
  • インフラの状態をコードで管理できる
    • Gitなどでバーション管理されている場合、過去の変更内容まで管理できる
  • リモートワークしていてもレビューしやすい

Terraform 実行環境の構築

Terraformを使ってAWSリソースを構築するために必要な環境構築です。

  • AWS CLIのインストール

AWS CLI のインストールを参考にインストールしてください。
v1とv2が存在しますが、プロジェクトに合わせて選択してください。

  • AWS クレデンシャルの登録

今回は、名前付きプロファイルとして設定してください。Terraformでprofileを指定することで複数環境への構築が簡単にできます。
設定ファイルと認証情報ファイルの設定を参考に登録します。

sample-dev,sample-prodというprofileを設定した例です。

~/.aws/credentials
[sample-dev]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

[sample-prod]
aws_access_key_id=AKIAI44QH8DHBEXAMPLE
aws_secret_access_key=je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
  • tfenvを使ってterraformをインストール

tfenvはTerraformの複数のバージョン管理することができるツールです。
使用方法は、下記の記事にまとまっていたので参考に載せさせて頂きます。
tfenvでTerraformのバージョン管理をする

tfenvを使わずにDockerを使用してもいいです。Terraformはマイナーバージョンでも破壊的な変更を行う事があるのでバージョンを気軽に切り替えられるようになっていればOKです。


ここまでで導入はおしまいです。


Terraform 入門


tfstateファイルの管理

最初に、tfstateファイルという重要なファイルについて理解しましょう。

tfstateファイルはTerraformで管理しているインフラの状態を記録するためのファイルです。
Terraformによってインフラを構築すると、自動的にインフラの状態がtfstateファイルに記録されます。インフラに変更を加える場合などは、このtfstateファイルを見てリソースの追加・変更があるかどうかを判断されます。

この自動で生成されるtfstateファイルですが、保存する場所を指定しない場合はTerraformを実行したディレクトリに保存されます。
ローカル環境にtfstateファイルがあると、メンバーが最新のtfstateファイルからリソースの状況を参照することができなくなってしまうため、通常はS3バケットなどで管理します。こうすることで、メンバー間でtfstateファイルを共有することが可能になります。

注意点:S3バケットにバージョニングを設定しておくことをお勧めします。tfstateファイルを間違って変更、削除してしまった場合に復旧することができます。


ディレクトリ構成について

Terraformの解説を進めるあたってサンプルとなるディレクトリ構造を定義し、これに沿って説明を進めたいと思います。

プロダクトでTerraformを利用する場合、複数環境にAWSリソースを構築するケースはよくあるパターンだと思います。
複数環境に対応するための構成として下記のディレクトリ構成を紹介します。

  ├ modules/
  │  ├ vpc/
  │  └ api/
  │
  └ environments/
     ├ dev/
     │ ├ network/  <- 作業ディレクトリ
     │ └ api/      <- 作業ディレクトリ
     └ prod/
       ├ network/  <- 作業ディレクトリ
       └ api/      <- 作業ディレクトリ

このディレクトリ構成のポイントは下記の通りです。

  • /moduleディレクトリに複数の環境で使用されるmoduleを作成する
  • 環境ごとのディレクトリを用意する
  • tfstateファイルを作業ディレクトリ毎に作成する

環境ごとの作業ディレクトリ配下でTerraformのコマンド(teraform initterraform apply)を実行します。


このディレクトリ構造のメリット・デメリット

  • メリット

    • 複数環境に対応している
    • 環境差分を許容できる(dev環境だけに必要なコードは、/devディレクトリ配下にのみ追加する)
    • moduleを使用することで共通のコードで定義できる(同じようなコードを何度も書くことが減る)
    • tfstateファイルを分割することで、問題があった場合の影響範囲が小さくなる
    • tfstateファイルを分割することで、terraform plan/applyの実行時間が短くなる
  • デメリット

    • 子モジュールの設計が難しい
    • 環境分に同じようなコードを書く必要がある(/dev/prodディレクトリ配下に同じようなコードが追加される)

このディレクトリ構成以外にも、複数環境に対応する方法があります。下記がわかりやすかったので参考にしてみてください。
Terraformのベストなプラクティスってなんだろうか | フューチャー技術ブログ


Moduleについて

ディレクトリ構成の解説の中で出てきたmoduleについて説明します。

moduleは、複数のリソースをまとまった単位で定義することができます。moduleを使用することでリソースを簡潔に定義し、複数回呼び出して再利用性を高めることができます。ディレクトリ構成の解説では、複数環境に構築するためにmoduleを利用していました。

また、このmoduleは子モジュールと呼ばれます。
作業ディレクトリの.tfファイルはすべてルートモジュールとなり、このルートモジュールから子モジュールを呼び出すことができます。

moduleをどのように設計するのかはチームによって異なると思いますが、ここではmoduleの設計については触れません。


基本的なコマンド

Terraformの具体的なコードの書き方を学ぶ前に、基本的なコマンドを見ていきましょう。
Terraformをどのように実行し、AWSリソースが構築されるまでの流れが把握できれば良いです。

まず基本のコマンドとして、下記の5つを理解しましょう。
これらを扱うことができれば、AWSにリソースを構築、変更、削除ができます。

また、これらのコマンドはディレクトリ構成の解説の中で出てきた、作業ディレクトリで実行します。


terraform init

初めに必ず実行する必要があります。
ワークスペースの初期化やプラグインがダウンロードされます。


terraform plan

変更内容が表示されます。
コードを作成、変更した場合は、terraform planすることで実行内容に問題ないかを確認します。


terraform apply

変更を実行します。ここで完了すると、実際にAWSにリソースが構築されます。
変更内容が表示されるので、問題なければyesを入力します。


terraform destroy

リソースを破棄します。
破棄対象のリソースが表示されるので、問題なければyesを入力します。


terraform fmt

インデントなどのスタイルをフォーマットするためのコマンドです。
terraform fmt -recursiveで再帰的にフォーマットできます。
Gitにコミットする前は実行するようにしましょう。


基本的構文(1)

ここまででTerraformを実行する流れをイメージできたのではないかと思います。
ここからは、Terraformのコードを書くために必要となる構文を理解していきましょう。

ここで紹介する構文はルートモジュールで使用します。


terraform

Terraformに関する設定を定義します。

  • Backend

tfstateファイルをS3バケットで管理するための設定です。
今回のサンプルでは、profileも指定します。

backend.tf
terraform {
  backend "s3" {
    bucket  = "dev-tfstate"
    key     = "ecr/terraform.tfstate"
    region  = "ap-northeast-1"
    profile = "sample-dev"
  }
}
  • Versions

Terraformとプロバイダーのバージョンを指定します。

インストールされているTerraformのバージョンとここで定義されているバージョンが合っていないと、terraform initiした際にエラーとなります。
プロバイダーはここで指定したバージョンがインストールされます。

versions.tf
terraform {
  required_version = "0.12.24"

  required_providers {
    aws = "2.57.0"
  }
}

provider

AWSを利用するための定義をします。
今回のサンプルでは、profileも指定します。

provider.tf
provider "aws" {
  region  = "ap-northeast-1"
  profile = "sample-dev"
}

AWS以外も定義できます。Datadogの例。

provider.tf
provider "datadog" {
  api_key = var.datadog_api_key
  app_key = var.app_key
}

基本的構文(2)

最低限知っていると良さそうなものを紹介します。


Input Variables

変数を定義します。
ルートモジュールで変数を宣言すると、CLIオプションと環境変数を使用してそれらの値を設定できます。
子モジュールで宣言すると、呼び出し元のモジュールのmoduleブロックで値を渡す必要があります。

variable "image_id" {
  type        = string
}

resource "aws_instance" "api" {
  instance_type = "t2.micro"
  ami           = var.image_id
}

Local Values

モジュール内に閉じて使える変数です。

locals {
  service_name = "forum"
  owner        = "Community Team"
}

Resources

インフラのリソースのことです。

CloudWathLogsを構築するリソースの例。

cloudwatchlog.tf
resource "aws_cloudwatch_log_group" "api" {
  name              = local.cloudwatch_log_name
  retention_in_days = local.cloudwatch_log_retention_in_days
}

参考:AWS: aws_cloudwatch_log_group - Terraform by HashiCorp


Data Sources

Terraformの外部で定義された情報を利用する際に使います。

Terrafromの外部で作成されたsample-vpcという名前のVPCのIDを取得する例。

datasources.tf
data "aws_vpc" "vpc" {
  filter {
     name   = "tag:Name"
     values = ["sample-vpc"]
   }
}

locals {
  vpc_id = data.aws_vpc.vpc.id
}

参考:AWS: aws_vpc - Terraform by HashiCorp


Output

指定したリソースの情報を出力できます。
terraform applyした際に、ターミナルに出力されます。ここで出力した値は、別のtfstateから参照できます。


remote_state

異なるtfstateファイルに記録されているリソースの情報を参照することができます。
ディレクトリ構成のサンプルだと、vpcのIDをapiのモジュールから参照したいケースなどで使用できます。


Terraformバージョンについての注意

Terraform 0.12で構文に変更がありました。
記述が大きく変更されているので、注意が必要です。

詳細は下記を参考にしてください。


ドキュメント


公式ドキュメント
各Resourcesのパラメータ等はここで確認します。
新しくリソースを追加する時は、まずここを見ます。

Terraform Recommended Practices
公式が公開しているベストプラクティス。
設計方針を決める前に一通り見ておく事を推奨します。

Terraform Module Registry
Terraformの開発元である、HashiCorp社が作成したmodule等を見る事が出来ます。
リソースを追加する際は、ここを参考にすると良いです。

terraform-providers/terraform-provider-aws
AWSプロバイダーのGitHubのリポジトリです。
CHANGELOG.md を見ると、バージョンアップにより対応された機能を確認することができます。
使用しているAWSプロバイダーのバージョンが古くてTerrafrom実行時にエラーになることがたまにあり、その際に見に行ったりします。


終わり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS-CDK】認証付きWebAPIをSolutions Constructsを使って手軽に構築

Solutions ConstructsでAWSインフラ構築

先日、AWS Solutions Constructsが発表されました。これはAWS-CDKにおいて、抽象化されたリソースを表すConstructsのよく使う組み合わせをWell-Architected準拠の設定込みで一発で構築できるライブラリです。例えばLambda + DynamoDBCloudFront + S3などの組み合わせのパターンが25個(2020/7月現在)提供されています。

これをつかってパパっとAWS上にWebAPIを構築したいと思います。

注意

2020/7/6現在、Solutions ConstructsのstabilityはすべてExperimentalとなっています。
本番環境などでの使用はよく検討した上で行ってください。

環境

  • AWS-CDK CLI v1.47.0
    • ※ Solutions ConstrucsはCDK v1.46.0以上に対応しています。
  • TypeScript v3.9.2
  • npm v6.14.5
  • node.js v12.18.0

使用するConstructs

今回は以下のSolutions Constructsを使用します。

構成図は以下のとおりです。

aws-web-api-infra.png

CDKによって、API Gateway, Lambda, Cognito, DynamoDBテーブルを作成します。
さらに、各リソースのオプションはデフォルトで下記のWell-Architected準拠の設定が施されています。

  • Cognito
    • ユーザープールに対するパスワードポリシーの設定
    • ユーザープールに対するアドバンスドセキュリティの有効化
  • API Gateway
    • エンドポイントのエッジ最適化設定
    • アクセスログの有効化
    • 最小限のIAMロールの設定
    • すべてのメソッドに対するIAM認証設定
  • Lambda
    • 最小限のIAMロールの設定
    • ログ出力の有効化
    • keep-alive時のコネクション再利用設定(Node.jsファンクション)
  • DynamoDB
    • オンデマンドモードの設定
    • AWSマネージドCMKによる暗号化設定

作成手順

AWS-CDK CLIをグローバルインストールします。

npm install -g aws-cdk

インストール後にプロジェクトを作成します。

cdk init app --language=typescript

今回使用するライブラリをインストールします。(SolutionsConstructsの最新verが1.47.0なので統一)

npm install -s @aws-cdk/aws-lambda@1.47.0 @aws-cdk/aws-apigateway@1.47.0 @aws-cdk/aws-dynamodb@1.47.0 @aws-cdk/aws-cognito@1.47.0 @aws-solutions-constructs/aws-cognito-apigateway-lambda@1.47.0 @aws-solutions-constructs/aws-lambda-dynamodb@1.47.0

Constructsを使ってCDKを記述します。

import * as cdk from "@aws-cdk/core";
import * as lambda from "@aws-cdk/aws-lambda";
import { CognitoToApiGatewayToLambda } from "@aws-solutions-constructs/aws-cognito-apigateway-lambda";
import { LambdaToDynamoDB } from "@aws-solutions-constructs/aws-lambda-dynamodb";
import {
  AttributeType,
  BillingMode,
  TableEncryption,
} from "@aws-cdk/aws-dynamodb";

export class CdkWebappStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here
    const apigwToLambda = new CognitoToApiGatewayToLambda(
      this,
      "CognitoToApiGatewayToLambdaPattern",
      {
        deployLambda: true,
        lambdaFunctionProps: {
          runtime: lambda.Runtime.NODEJS_12_X,
          handler: "index.handler",
          code: lambda.Code.asset(`${__dirname}/lambda`),
        },
      }
    );

    new LambdaToDynamoDB(this, "LambdaToDynamoPattern", {
      deployLambda: false,
      existingLambdaObj: apigwToLambda.lambdaFunction, // 生成済みのLambdaを使用
      dynamoTableProps: {
        partitionKey: { name: "id", type: AttributeType.STRING },
        // デフォルトの設定は以下の通り
        //   キャパシティーモード: オンデマンド
        //   暗号化タイプ: KMS - AWSマネージドCMK
        // 無料枠で使用するために設定を変更している。
        billingMode: BillingMode.PROVISIONED,
        readCapacity: 5,
        writeCapacity: 5,
        encryption: TableEncryption.DEFAULT,
      },
    });
  }
}

lib/lambdaディレクトリにはlambdaのソースを配置します。

index.js
const { DynamoDB } = require("aws-sdk");

exports.handler = async function (event) {
  console.log("request:", JSON.stringify(event, undefined, 2));

  // create AWS SDK clients
  const dynamo = new DynamoDB();

  // APIのパスをパーティションキーとして、アクセス回数を記録する
  await dynamo
    .updateItem({
      TableName: process.env.DDB_TABLE_NAME,
      Key: { id: { S: event.path } },
      UpdateExpression: "ADD hits :incr",
      ExpressionAttributeValues: { ":incr": { N: "1" } },
    })
    .promise();

  return {
    statusCode: 200,
    headers: { "Content-Type": "text/plain" },
    body: `Hello, AWS Solutions Constructs! You've hit ${event.path}\n`,
  };
};

以下のコマンドでインフラをデプロイできます

npm run build && cdk deploy

cdk deploy実行時にリソースのデプロイ確認があるので、yを入力してデプロイを開始しましょう。

API Gatewayのコンソールから/fooメソッドのテストをおこなうと正常にレスポンスが返されることが確認できます。

スクリーンショット 2020-07-08 00.40.13.png

また、DynamoDBに、アクセスしたパスの文字列とアクセス回数が記録されていることが確認できます。

スクリーンショット 2020-07-08 00.41.51.png

まとめ

このように、Solution ConstructsではAWSでよく使われるリソースの組み合わせをConstructsをして提供してくれています。それを利用することで、爆速で今までよりもすばやく、かつ、Well-Architectedに準拠したインフラを構築することが可能になりました。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS-CDK】Solutions Constructsを使って手軽に認証付きWebAPIを構築

Solutions ConstructsでAWSインフラ構築

先日、AWS Solutions Constructsが発表されました。これはAWS-CDKにおいて、抽象化されたリソースを表すConstructsのよく使う組み合わせをWell-Architected準拠の設定込みで一発で構築できるライブラリです。例えばLambda + DynamoDBCloudFront + S3などの組み合わせのパターンが25個(2020/7月現在)提供されています。

これをつかってパパっとAWS上にWebAPIを構築したいと思います。

注意

2020/7/6現在、Solutions ConstructsのstabilityはすべてExperimentalとなっています。
本番環境などでの使用はよく検討した上で行ってください。

環境

  • AWS-CDK CLI v1.47.0
    • ※ Solutions ConstrucsはCDK v1.46.0以上に対応しています。
  • TypeScript v3.9.2
  • npm v6.14.5
  • node.js v12.18.0

使用するConstructs

今回は以下のSolutions Constructsを使用します。

構成図は以下のとおりです。

aws-web-api-infra.png

CDKによって、API Gateway, Lambda, Cognito, DynamoDBテーブルを作成します。
さらに、各リソースのオプションはデフォルトで下記のWell-Architected準拠の設定が施されています。

  • Cognito
    • ユーザープールに対するパスワードポリシーの設定
    • ユーザープールに対するアドバンスドセキュリティの有効化
  • API Gateway
    • エンドポイントのエッジ最適化設定
    • アクセスログの有効化
    • 最小限のIAMロールの設定
    • すべてのメソッドに対するIAM認証設定
  • Lambda
    • 最小限のIAMロールの設定
    • keep-alive時のコネクション再利用設定(Node.jsファンクション)
  • DynamoDB
    • オンデマンドモードの設定
    • AWSマネージドCMKによる暗号化設定

作成手順

AWS-CDK CLIをグローバルインストールします。

npm install -g aws-cdk

インストール後にプロジェクトを作成します。

cdk init app --language=typescript

今回使用するライブラリをインストールします。(SolutionsConstructsの最新verが1.47.0なので統一)

npm install -s @aws-cdk/aws-lambda@1.47.0 @aws-cdk/aws-apigateway@1.47.0 @aws-cdk/aws-dynamodb@1.47.0 @aws-cdk/aws-cognito@1.47.0 @aws-solutions-constructs/aws-cognito-apigateway-lambda@1.47.0 @aws-solutions-constructs/aws-lambda-dynamodb@1.47.0

Constructsを使ってCDKを記述します。

import * as cdk from "@aws-cdk/core";
import * as lambda from "@aws-cdk/aws-lambda";
import { CognitoToApiGatewayToLambda } from "@aws-solutions-constructs/aws-cognito-apigateway-lambda";
import { LambdaToDynamoDB } from "@aws-solutions-constructs/aws-lambda-dynamodb";
import {
  AttributeType,
  BillingMode,
  TableEncryption,
} from "@aws-cdk/aws-dynamodb";

export class CdkWebappStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // The code that defines your stack goes here
    const apigwToLambda = new CognitoToApiGatewayToLambda(
      this,
      "CognitoToApiGatewayToLambdaPattern",
      {
        deployLambda: true,
        lambdaFunctionProps: {
          runtime: lambda.Runtime.NODEJS_12_X,
          handler: "index.handler",
          code: lambda.Code.asset(`${__dirname}/lambda`),
        },
      }
    );

    new LambdaToDynamoDB(this, "LambdaToDynamoPattern", {
      deployLambda: false,
      existingLambdaObj: apigwToLambda.lambdaFunction, // 生成済みのLambdaを使用
      dynamoTableProps: {
        partitionKey: { name: "id", type: AttributeType.STRING },
        // デフォルトの設定は以下の通り
        //   キャパシティーモード: オンデマンド
        //   暗号化タイプ: KMS - AWSマネージドCMK
        // 無料枠で使用するために設定を変更している。
        billingMode: BillingMode.PROVISIONED,
        readCapacity: 5,
        writeCapacity: 5,
        encryption: TableEncryption.DEFAULT,
      },
    });
  }
}

lib/lambdaディレクトリにはlambdaのソースを配置します。

index.js
const { DynamoDB } = require("aws-sdk");

exports.handler = async function (event) {
  console.log("request:", JSON.stringify(event, undefined, 2));

  // create AWS SDK clients
  const dynamo = new DynamoDB();

  // APIのパスをパーティションキーとして、アクセス回数を記録する
  await dynamo
    .updateItem({
      TableName: process.env.DDB_TABLE_NAME,
      Key: { id: { S: event.path } },
      UpdateExpression: "ADD hits :incr",
      ExpressionAttributeValues: { ":incr": { N: "1" } },
    })
    .promise();

  return {
    statusCode: 200,
    headers: { "Content-Type": "text/plain" },
    body: `Hello, AWS Solutions Constructs! You've hit ${event.path}\n`,
  };
};

以下のコマンドでインフラをデプロイできます

npm run build && cdk deploy

cdk deploy実行時にリソースのデプロイ確認があるので、yを入力してデプロイを開始しましょう。

API Gatewayのコンソールから/fooメソッドのテストをおこなうと正常にレスポンスが返されることが確認できます。

スクリーンショット 2020-07-08 00.40.13.png

また、DynamoDBに、アクセスしたパスの文字列とアクセス回数が記録されていることが確認できます。

スクリーンショット 2020-07-08 00.41.51.png

まとめ

このように、Solution ConstructsではAWSでよく使われるリソースの組み合わせをConstructsをして提供してくれています。それを利用することで、爆速で今までよりもすばやく、かつ、Well-Architectedに準拠したインフラを構築することが可能になりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Stack Overflow Developer Survey 2020の要点整理

 この記事の目的

世界のディベロッパーのトレンドを追う上で、非常に重要な資料であるStack Overflow Developer Surveyの最新版である2020年版の要点を整理すること。自分の理解のためだけでなく、毎年出されているにもかかわらず、日本語での情報共有が少ない本調査の要点を日本語で整理することで、多くの日本人の開発者が世界のトレンドを理解することを期待する。

原文
https://insights.stackoverflow.com/survey/2020

 調査概要

・2020年2月に世界中の65,000人以上のディベロッパーにアンケートを実施(注:回答者はアメリカ、インド、ヨーロッパに集中)。
・コロナウイルスが流行る前に調査が実施されたため、仕事や賃金のデータは現時点での情報と異なる可能性があることに注意。

 キーポイント

(1)最も愛された言語はRust, Typescript, Pythonの順番。Rustは五年連続で一位。
(2)回答者の8割がDevOpsが重要だと考えている。
(3)回答者の9割が、プログラミングで壁に当たった時、Stack Overflowを参照している。

 職種

・複数回答ありで、55%がバックエンド、フルスタックと答え、37%がフロントエンドのエンジニアと回答。
・プログラミング経験年数の層の中で一番高い割合が5年から9年で回答者の30%を占めた。5年以下と答えたのは17%。
・一方で、仕事としてプログラミングした経験は5年以下が最多で回答者の40%を占めた。
・回答者の85%は10代でプログラミングを開始した。

 学歴

・46%が学士号を、23%が修士号を取得。また回答者の6割以上が学部時代にコンピューターサイエンスを専攻していた。

 言語その他のランキング

(1)最もポピュラーな技術
Screenshot from 2020-07-07 23-43-55.png

(2)最もポピュラーなフレームワーク
Screenshot from 2020-07-07 23-48-13.png

(3)最もポピュラーなプラットフォーム
Screenshot from 2020-07-07 23-49-21.png

(4)最も必要とされている技術
Screenshot from 2020-07-07 23-51-06.png

(5)最も必要とされているフレームワーク
Screenshot from 2020-07-07 23-52-06.png

(6)最も必要とされているプラットフォーム
Screenshot from 2020-07-07 23-53-30.png

(7)技術ごとの年収ランキング(米ドル換算)
Screenshot from 2020-07-07 23-57-39.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む