├── .gitignore
├── .vscode
└── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── README_ja.md
├── batch
├── Dockerfile
├── README.md
├── README_ja.md
├── requirements.txt
└── src
│ └── sample
│ ├── __init__.py
│ └── batch.py
├── docs
└── images
│ ├── job.png
│ ├── keypair_command_en.png
│ ├── keypair_command_ja.png
│ ├── prerequirsite_en.png
│ ├── prerequirsite_ja.png
│ ├── repository_url_en.png
│ ├── repository_url_ja.png
│ ├── template_architecture_en.png
│ ├── template_architecture_ja.png
│ ├── template_architecture_privatelink_en.png
│ ├── template_architecture_privatelink_ja.png
│ ├── template_architecture_serverless_en.png
│ ├── template_architecture_serverless_ja.png
│ ├── template_architecture_serverless_privatelink_en.png
│ └── template_architecture_serverless_privatelink_ja.png
├── functions
├── .gitignore
├── get.ts
├── init.ts
├── lib
│ └── connect.ts
├── package-lock.json
├── package.json
└── post.ts
├── infra
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .prettierrc.json
├── README.md
├── README_ja.md
├── README_serverless.md
├── README_serverless_ja.md
├── bin
│ ├── base.ts
│ ├── batch.ts
│ ├── serverless-webapp.ts
│ └── webapp.ts
├── buildWebEnv.mjs
├── cdk.json
├── docker
│ └── nginx
│ │ ├── Dockerfile
│ │ ├── default.conf
│ │ └── static-content
│ │ └── index.html
├── gulpfile.js
├── jest.config.js
├── lib
│ ├── base-stack.ts
│ ├── batch-stack.ts
│ ├── constructs
│ │ ├── aurora
│ │ │ ├── README.md
│ │ │ ├── README_dbinitlambda.md
│ │ │ ├── aurora.ts
│ │ │ └── dbinitlambda.ts
│ │ ├── codepipeline
│ │ │ ├── README_java.md
│ │ │ ├── README_react.md
│ │ │ ├── codepipeline-webapp-java.ts
│ │ │ └── codepipeline-webapp-react.ts
│ │ ├── ec2
│ │ │ ├── README.md
│ │ │ └── bastion.ts
│ │ ├── ecr
│ │ │ ├── README.md
│ │ │ └── ecr.ts
│ │ ├── ecs
│ │ │ ├── README_BASE.md
│ │ │ ├── README_JOB.md
│ │ │ ├── README_SERVICE.md
│ │ │ ├── ecs-app-base.ts
│ │ │ ├── ecs-app-service.ts
│ │ │ └── ecs-job.ts
│ │ ├── kms
│ │ │ ├── README.md
│ │ │ └── key.ts
│ │ ├── network
│ │ │ ├── README.md
│ │ │ └── network.ts
│ │ ├── s3
│ │ │ ├── README.md
│ │ │ ├── README_webappbucket.md
│ │ │ ├── bucket.ts
│ │ │ └── webappbucket.ts
│ │ └── serverless
│ │ │ ├── README_apigw.md
│ │ │ ├── README_lambda.md
│ │ │ ├── README_serverless_app.md
│ │ │ ├── apigw.ts
│ │ │ ├── lambda.ts
│ │ │ └── serverless-app.ts
│ ├── serverlessapp-stack.ts
│ └── webapp-stack.ts
├── package-lock.json
├── package.json
├── ssl
│ └── openssl_sign_inca.cnf
├── stages.js
└── tsconfig.json
├── package-lock.json
├── webapp-java
├── .gitignore
├── Dockerfile
├── README.md
├── README_ja.md
├── build.gradle
├── buildspec.yaml
├── docs
│ └── images
│ │ └── screenshot.png
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── log4j2.xml
├── settings.gradle
└── src
│ └── main
│ ├── java
│ └── com
│ │ └── example
│ │ └── sampleapp
│ │ └── webapp
│ │ ├── ServletInitializer.java
│ │ ├── WebappApplication.java
│ │ ├── controller
│ │ ├── SampleAppController.java
│ │ └── form
│ │ │ ├── SampleAppForm.java
│ │ │ └── SampleAppFormList.java
│ │ ├── domain
│ │ ├── SampleAppService.java
│ │ └── dto
│ │ │ ├── SampleAppDto.java
│ │ │ └── SampleAppListDto.java
│ │ └── repository
│ │ ├── SampleAppRepository.java
│ │ └── model
│ │ └── SampleApp.java
│ └── resources
│ ├── application.properties
│ ├── data.sql
│ ├── schema.sql
│ ├── static
│ ├── css
│ │ └── bootstrap.min.css
│ └── js
│ │ └── bootstrap.min.js
│ └── templates
│ ├── sampleappform.html
│ └── sampleapplist.html
└── webapp-react
├── .env
├── .eslintrc.js
├── .gitignore
├── .prettierrc.json
├── README.md
├── README_ja.md
├── buildspec.yaml
├── docs
└── images
│ └── screenshot.png
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
├── manifest.json
└── robots.txt
├── sample.env
├── src
├── components
│ ├── Dashboard.tsx
│ ├── RecordForm.tsx
│ ├── RecordFormRow.tsx
│ └── RecordList.tsx
├── index.css
├── index.tsx
├── modules
│ └── requests.ts
├── react-app-env.d.ts
├── reportWebVitals.ts
├── setupTests.ts
└── types
│ └── record.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | */.DS_Store
2 | .DS_Store
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "interactive"
3 | }
4 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Template of Closed Network System Works on AWS
2 | Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Template for Closed Network System Workloads on AWS
2 |
3 | [日本語で読む](./README_ja.md)
4 |
5 | It assumes a closed network scenario and is a template for deploying applications and batch systems accessible from that environment to AWS.
6 | It consists of AWS CDK and Web server sample applications that via CI/CD (AWS CodePipelie) will be deployed to private networks.
7 |
8 | In local government systems that require a high level security and network restrictions, we need to configure our architecture with characteristics from on-premise, like "Closed area networks" and "Allow NW access routes from AWS to on-premise network". We designed the template so that these type of systems can be deployed on AWS.
9 |
10 | We will adopt REPLATFORM, one of the 6Rs, which is AWS's migration strategy, and aims to migrate from an existing on-premise environment to computing and managed DB using containers. REPLATFORM has advantages such as improving performance and reducing costs. The template uses several AWS managed services that will help us to reduce cost and operational workload.
11 | (Ref:[Migrating to AWS Best Practices and Strategies](https://pages.awscloud.com/rs/112-TZM-766/images/Migrating-to-AWS_Best-Practices-and-Strategies_eBook.pdf)
12 |
13 | And we added serverless application version of infra that uses AWS Lambda and React application instead of container.
14 | Please see here you want to know how to deploy serverless application version.
15 |
16 | ## Scope
17 |
18 | ### What the template provides
19 |
20 | - Container execution environment for running Java applications (Spring boot) on Amazon ECS/Fargate
21 |
22 | - In addition to this, a sample application using Spring Boot
23 | - A sample Dockerfile to turn that sample application into a container image
24 | - For sample applications, see [`Webapp-java/readme.md`](./webapp-java/README.md)
25 |
26 | - Serverless application environment for running React application hosted on Amazon S3 and REST API on API Gateway and AWS Lambda.(\*)
27 |
28 | - A sample application using React
29 | - For sample react application, see [`Webapp-react/readme.md`](./webapp-react/README.md).
30 | - Sample REST APIs code is in `functions/`
31 |
32 | - CI/CD environment for continuous application development
33 |
34 | - Pipeline for building and deploying the above sample applications using CodePipeline, CodeCommit, and CodeBuild
35 | - A job execution platform combining Step Functions and Amazon ECS/Fargate that can execute simple job flows
36 |
37 | - In addition to this, a Python sample job script
38 |
39 | - A sample Dockerfile for turning the sample job script into a container image
40 | - For a sample job script, see [`batch/README.md`](./batch/README.md)
41 |
42 | - Maintenance environment for checking application operation and managing RDB
43 | - A secure access where you can test applications and manage databases combining SystemsManager and EC2
44 | - Provides remote desktop connections (Windows Server Instances) and SSH connections (Amazon Linux Instances)
45 |
46 | ### What the template doesn't provide
47 |
48 | - Settings and implementation on the AWS side involved in on-premise connections such as AWS Direct Connect (DX) and AWS Site-to-Site VPN (VPN)
49 | - Please design and implement DX and VPN, which are likely to be necessary for actual use on the user's side
50 | - Application authentication function
51 | - Since this application is a sample, it does not have authentication or authorization functions such as login/logout
52 | - DNS settings for applications
53 | - To check the operation of this template, we will use an endpoint that AWS automatically creates for the ALB
54 | - Operation features
55 | - It does not have integrated management of application and AWS resource logs or the ability to alert and monitor applications
56 |
57 | ## Directories
58 |
59 | This is the directory tree and its overview.
60 |
61 | | Directory | Sub directory | Description |
62 | | ----------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63 | | batch | | Creates a batch container application with Dockerfile |
64 | | | src | python scripts sample app |
65 | | infra | | CDK source code for provisioning the following AWS resources
- Network (VPC and subnet)
- DB (Aurora)
- Compute resources for containers (Amazon ECS, Fargate)
- CI/CD tools (CodePipeline, CodeCommit, CodeDeploy)
- Batch Job Management ( Step Functions, DynamoDB, SNS) |
66 | | | bin | CDK app source code |
67 | | | lib/constructs | Constructs used to build AWS resources
The [Core concept](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/core_concepts.html) explains about what is the difference between Stack and Construct. |
68 | | webapp-java | | Source code of SpringBoot web app with Dockerfile |
69 |
70 | ## Requirement
71 |
72 | - `Node.js` >= `16.13.0`
73 | - `npm` >= `9.2.0`
74 | - `aws-cdk` >= `2.65.0`
75 | - `aws-cdk-lib` >= `2.65.0`
76 | - `OpenSSL` >= `3.0.8`
77 | - `Docker`
78 |
79 | ## Architecture
80 |
81 | ### NW configuration assumptions
82 |
83 | It is assumed that the on-premise NW (on the right side of the image bellow) exists and the AWS network will be connected via Direct Connect or VPN.
84 |
85 | 
86 |
87 | ### Architecture diagram
88 |
89 | This template will deploy AWS resources in the application NW connected by AWS Direct Connect (DX) or AWS Site-to-Site VPN (VPN).
90 |
91 | 
92 |
93 | Is important to mention that in addition to configuring NW routes on DX and VPNs, please have a look at using private links for better network desing in this blog post: [an AWS Transit Gateway that can also be used with a “shared” AWS DirectConnect](https://aws.amazon.com/jp/blogs/news/aws-transit-gateway-with-shared-directconnect/).
94 |
95 | ### Using Private Link
96 |
97 | The template, optionally allows you to provision the architecture by using Private Links. It is recommended for an extra layer of security when designing applications that are deployed in Private networks.
98 |
99 | This is the architecture diagram that is slightly modified by using private links for the services:
100 |
101 | 
102 |
103 | ## How to Deploy
104 |
105 | Please see the following document: [infra/README.md](./infra/README.md)
106 | If you want to deploy serverless application version, please see the following document: [infra/README_serverless.md](./infra/README_serverless.md)
107 |
108 | ## Security
109 |
110 | See [CONTRIBUTING](CONTRIBUTING.md#Security-issue-notifications) for more information.
111 |
112 | ## License
113 |
114 | This library is licensed under the MIT-0 License. See the LICENSE file.
115 |
--------------------------------------------------------------------------------
/README_ja.md:
--------------------------------------------------------------------------------
1 | # Template for Closed Network System Workloads on AWS
2 |
3 | [View this page in English](./README.md)
4 |
5 | 閉域網を前提環境とし、その環境からアクセス可能な Web アプリケーションとバッチシステムを AWS 上に展開するためのテンプレートとなります。
6 | CDK で構築されるインフラ とサンプルアプリのソースコードで構成されています。
7 |
8 | 地方自治体のようなシステムがデプロイされている環境の特性である、「閉域網」や「AWS からオンプレへの NW アクセス経路の確保」を考慮した上で、
9 | AWS のマイグレーション戦略である 6R の一つ、REPLATFORM を採用し、既存のオンプレ環境から、コンテナを利用したコンピューティングやマネージドな DB への移行を目指します。
10 | (ご参考:[AWS への移行:ベストプラクティスと戦略](https://pages.awscloud.com/rs/112-TZM-766/images/Migrating-to-AWS_Best-Practices-and-Strategies_eBook.pdf)
11 | )
12 | REPLATFORM では、サーバの運用負荷の軽減などがメリットになります。本テンプレートでも、AWS のマネージドサービスを活用した形で運用コストの軽減を目指しました。
13 |
14 | また、Container の代わりに、AWS Lambda を利用して API を構築し、React アプリケーションを利用した、サーバーレスアプリケーション版を追加しました。
15 | デプロイ方法については、[こちら](./infra/README_serverless_ja.md)を参照ください。
16 |
17 | ## テンプレートのスコープ
18 |
19 | ### テンプレートで提供されるもの
20 |
21 | - Java アプリケーション(Spring boot)を Amazon ECS/Fargate 上で稼働させるためのコンテナ実行環境(\*)
22 | - これに加え、上記環境下で動作する Spring boot を利用したサンプルアプリケーション
23 | - そのサンプルアプリケーションをコンテナイメージにするためのサンプル Dockerfile
24 | - サンプルアプリケーションについては、[`webapp-java/README.md`](../webapp-java/README_ja.md)をご参照ください
25 | - 閉域網で SPA + REST API を動かすための、Amazon S3、Amazon API Gateway、AWS Lambda を利用したサーバーレスな実行環境(\*)
26 | - React のサンプルアプリケーション
27 | - 詳しくは、[`Webapp-react/readme_ja.md`](./webapp-react/README_ja.md)を参照ください
28 | - React サンプルアプリケーションから呼び出される REST API のサンプルコード
29 | - アプリケーションを継続開発するための CI/CD 環境
30 | - AWS CodePipeline や AWS CodeCommit, AWS CodeBuild を利用した、上記サンプルアプリケーションをビルド、デプロイするためのパイプライン
31 | - 簡易なジョブフローが実行できる、AWS Step Functions、Amazon ECS/Fargate を組み合わせたジョブ実行基盤
32 | - これに加え、上記環境下で動作する、Python のサンプルジョブスクリプト
33 | - サンプルジョブスクリプトをコンテナイメージにするためのサンプル Dockerfile
34 | - サンプルジョブスクリプトについては、[`batch/README.md`](../batch/README_ja.md)をご参照ください
35 | - アプリケーションの動作確認や RDB を管理するためのメンテナンス環境
36 | - SystemsManager と EC2 を組み合わせたアプリケーションのテストや DB の管理を実施できる環境
37 | - リモートデスクトップ接続(Windows Server Instance)と コンソール接続(Amazon Linux Instance)を提供
38 |
39 | \* コンテナ実行環境とサーバーレスな実行環境は、どちらか選んでデプロイしていただく手順をそれぞれの README に記載しています。
40 |
41 | ### テンプレートで提供されないもの
42 |
43 | - AWS Direct Connect(DX)や AWS Site-to-Site VPN(VPN) といったオンプレとの接続に関わる AWS 側の設定や実装
44 | - 本番利用において必要になると思われる DX や VPN の設計・導入については、別途実施ください
45 | - アプリケーションの認証機能
46 | - 本アプリケーションはサンプルのため、ログイン・ログアウトなどの認証機能を持ちません
47 | - アプリケーションの運用機能
48 | - アプリケーションや AWS リソースのログの統合的な管理やアプリケーションに対するアラートや監視の機能は持ちません
49 |
50 | ## ディレクトリ構成
51 |
52 | ディレクトリ構成とその概要です。
53 |
54 | | ディレクトリ | サブディレクトリ | 概要 |
55 | | ------------ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
56 | | batch | | batch コンテナを作成するためのソースファイルと Dockerfile |
57 | | | src | python のスクリプト |
58 | | infra | | AWS リソースをプロビジョニングするための CDK のソースコード
- ネットワーク(VPC やサブネット)
- DB(Aurora)
- コンテナ向けコンピューティングリソース(Amazon ECS、Fargate)
- CI/CD ツール(CodePipeline、CodeCommit、CodeDeploy)
- バッチジョブ管理(Step Functions、DynamoDB、SNS)
が生成される。 |
59 | | | bin | CDK を実行するためのソースコード。 CDK で定義されるところの app に該当 |
60 | | | lib/constructs | AWS リソースを生成するための Stack, Construct が定義されたソースコード
Stack と Construct の違いは[ドキュメント](https://docs.aws.amazon.com/ja_jp/cdk/v2/guide/core_concepts.html)をご参照ください |
61 | | webapp-java | | Spring boot 製のウェブアプリのサンプルソースと Dockerfile |
62 |
63 | ## 前提条件
64 |
65 | - `Node.js` >= `16.13.0`
66 | - `npm` >= `9.2.0`
67 | - `aws-cdk` >= `2.65.0`
68 | - `aws-cdk-lib` >= `2.65.0`
69 | - `OpenSSL` >= `3.0.8`
70 | - `Docker`
71 |
72 | ## アーキテクチャ
73 |
74 | ### NW 構成の前提
75 |
76 | オンプレミス NW と AWS とは、Direct Connect または、VPN で接続されることを前提としています。
77 |
78 | 
79 |
80 | ### テンプレートのアーキテクチャ図
81 |
82 | 本テンプレートでは、AWS Direct Connect(DX) や AWS Site-to-Site VPN(VPN) で接続されるアプリケーション NW 内の AWS リソースが構築されます。
83 |
84 | 
85 |
86 | ### Private Link を利用する場合
87 |
88 | また、前述の構成において、既存 NW との CIDR 重複を回避するために、Private Link の利用を検討するケースもあるかと思います。その場合には、以下の図に示すように、オプションとして Private Link を経由した接続の構築が可能になっています。
89 | Private Link を利用する場合には、[“共有型”AWS DirectConnect でも使える AWS Transit Gateway](https://aws.amazon.com/jp/blogs/news/aws-transit-gateway-with-shared-directconnect/)を参照いただき、最適な NW 設計をご検討ください。
90 |
91 | 
92 |
93 | ## テンプレートのデプロイ方法
94 |
95 | [infra/README.md](./infra/README_ja.md)を参照ください。
96 | サーバーレスアプリケーション版を利用したい方は、[infra/README_serverless_ja.md](./infra/README_serverless_ja.md)を参照ください。
97 |
98 | ## Security
99 |
100 | See [CONTRIBUTING](CONTRIBUTING.md#Security-issue-notifications) for more information.
101 |
102 | ## License
103 |
104 | This library is licensed under the MIT-0 License. See the LICENSE file.
105 |
--------------------------------------------------------------------------------
/batch/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.11.1-alpine
2 |
3 | WORKDIR /usr/src/app
4 | VOLUME /usr/src/app
5 |
6 | COPY requirements.txt ./
7 |
8 | RUN apk update && apk add --upgrade sqlite-libs && apk add --upgrade libcrypto3 && apk add --upgrade libssl3 && apk add curl && apk add -f python3 py3-pip && pip install --no-cache-dir -r requirements.txt
9 |
10 | RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o /usr/src/app/root.pem
11 |
12 | RUN addgroup -S python && adduser -S python -G python
13 | USER python
14 |
15 | COPY . .
16 |
17 | CMD [ "python", "./src/sample/batch.py" ]
--------------------------------------------------------------------------------
/batch/README.md:
--------------------------------------------------------------------------------
1 | # batch
2 |
3 | [日本語で読む](./README_ja.md)
4 |
5 | It's a sample job script that is called by Step Functions that are created by the CDK infra application.
6 |
7 | ## What does it do
8 |
9 | The script will invoke SQL commands by `JOBID` given by Step Functions.
10 | The queries will test `true` or `false` values that are stored in the database. If any returned value is `false` the script will output the record names to a file in S3. Also the job execution result is returned back to the step functions.
11 |
12 | ## How to use
13 |
14 | The job is packed into a docker image that is pushed by this CDK application into ECR. You can trigger the job by the AWS Console or modifiy the job and deploy again to reflect the changes.
15 |
16 | ## Path to Production
17 |
18 | - Please modify the script and modify the Dockerfile follow your environment.
19 | - Please consider CI/CD for job scripts.
20 |
--------------------------------------------------------------------------------
/batch/README_ja.md:
--------------------------------------------------------------------------------
1 | # batch
2 |
3 | [View this page in English](./README.md)
4 |
5 | このソースコードは、infra で構築されたジョブ実行基盤から呼び出されるジョブスクリプトを定義したものです。
6 |
7 | ## 概要
8 |
9 | ジョブ実行基盤から渡される JOBID をもとに異なる処理を呼び出すことが可能です。
10 | 本サンプルでは、データベースに格納されたテストデータの `true/false` を確認し、ジョブの成否を判断し、`false` の設定されているレコード名のリストを S3 に出力し、ジョブの実行結果を Step Functions に返却します。
11 |
12 | ## 利用方法
13 |
14 | infra のデプロイ時に、自動的にジョブスクリプトを含んだコンテナイメージがビルドされ、ECR にプッシュされます。
15 | そのため、本ディレクトリでなにかする必要はありません。
16 | コンテナイメージについては、`Dockerfile` を参照ください。
17 |
18 | ## 本番利用に向けて
19 |
20 | - 環境に合わせ、スクリプトの改修や Dockerfile の修正を実施ください。
21 | - ジョブスクリプトの更新を個別に適用するための、ジョブスクリプトの CI/CD をご検討ください。
22 |
--------------------------------------------------------------------------------
/batch/requirements.txt:
--------------------------------------------------------------------------------
1 | psycopg2-binary
2 | boto3
--------------------------------------------------------------------------------
/batch/src/sample/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/batch/src/sample/__init__.py
--------------------------------------------------------------------------------
/batch/src/sample/batch.py:
--------------------------------------------------------------------------------
1 | import boto3
2 | import datetime
3 | import json
4 | import os
5 | import sys
6 | import psycopg2
7 |
8 | s3 = boto3.resource('s3')
9 |
10 | ENDPOINT=os.environ['DB_ENDPOINT']
11 | PORT="5432"
12 | USER=os.environ['DB_USERNAME']
13 | PASS=os.environ['DB_PASSWORD']
14 | DBNAME="postgres"
15 |
16 | JOB_ID=os.environ['JOB_ID']
17 | BUCKET_NAME=os.environ['BUCKET_NAME']
18 |
19 | KEYS = ('id', 'name', 'job0001_flag', 'job0002_flag', 'job0003_flag', 'job0004_flag', 'job0005_flag')
20 |
21 | CHECK_ERROR_QUERIES={
22 | "Job0001": """SELECT name FROM sampleapp_table WHERE job0001_flag = false;""",
23 | "Job0002": """SELECT name FROM sampleapp_table WHERE job0002_flag = false;""",
24 | "Job0003": """SELECT name FROM sampleapp_table WHERE job0003_flag = false;""",
25 | "Job0004": """SELECT name FROM sampleapp_table WHERE job0004_flag = false;""",
26 | "Job0005": """SELECT name FROM sampleapp_table WHERE job0005_flag = false;""",
27 | }
28 |
29 | TODAY = datetime.date.today()
30 |
31 | def datetime_encoder(datetime_object):
32 | if isinstance(datetime_object, datetime.date):
33 | return datetime_object.isoformat()
34 |
35 | try:
36 | conn = psycopg2.connect(host=ENDPOINT, port=PORT, database=DBNAME, user=USER, password=PASS, sslmode='verify-full', sslrootcert = './root.pem')
37 | cur = conn.cursor()
38 | cur.execute(CHECK_ERROR_QUERIES[JOB_ID])
39 | query_results = cur.fetchall()
40 | ret = []
41 | for qresult in query_results:
42 | ret.append({key:value for key, value in zip(KEYS, qresult)})
43 | except Exception as e:
44 | print("Database connection failed due to {}".format(e))
45 |
46 | if len(query_results) > 0:
47 | try:
48 | key_name = "{0}_failure_result_{1}.json".format(JOB_ID, str(TODAY))
49 | s3_obj = s3.Object(BUCKET_NAME, key_name)
50 | s3_obj.put(Body=json.dumps(ret, ensure_ascii=False, default=datetime_encoder), ContentEncoding='utf-8', ContentType='application/json')
51 | print("Job was failed. Please check the failure records in {0}/{1}".format(BUCKET_NAME, key_name))
52 | except Exception as e:
53 | print("Couldn't put object to S3: {}".format(e))
54 |
55 | sys.exit(1)
56 |
57 | else:
58 | print("Job was success!")
59 | sys.exit(0)
--------------------------------------------------------------------------------
/docs/images/job.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/job.png
--------------------------------------------------------------------------------
/docs/images/keypair_command_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/keypair_command_en.png
--------------------------------------------------------------------------------
/docs/images/keypair_command_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/keypair_command_ja.png
--------------------------------------------------------------------------------
/docs/images/prerequirsite_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/prerequirsite_en.png
--------------------------------------------------------------------------------
/docs/images/prerequirsite_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/prerequirsite_ja.png
--------------------------------------------------------------------------------
/docs/images/repository_url_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/repository_url_en.png
--------------------------------------------------------------------------------
/docs/images/repository_url_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/repository_url_ja.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_en.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_ja.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_privatelink_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_privatelink_en.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_privatelink_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_privatelink_ja.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_serverless_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_serverless_en.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_serverless_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_serverless_ja.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_serverless_privatelink_en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_serverless_privatelink_en.png
--------------------------------------------------------------------------------
/docs/images/template_architecture_serverless_privatelink_ja.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/ad70db9f29f7c0f7991645ce2f1d8d403260a5f4/docs/images/template_architecture_serverless_privatelink_ja.png
--------------------------------------------------------------------------------
/functions/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/functions/get.ts:
--------------------------------------------------------------------------------
1 | import Connection from "./lib/connect";
2 | import { Logger } from "@aws-lambda-powertools/logger";
3 | import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
4 |
5 | const logger = new Logger({ serviceName: "getLambda" });
6 |
7 | export const handler = async (
8 | event: APIGatewayProxyEvent
9 | ): Promise => {
10 | try {
11 | const client = await Connection();
12 | // Connection
13 | await client.connect();
14 | logger.info("connected");
15 |
16 | // Query
17 | const res = await client.query(
18 | "SELECT * FROM sampleapp_table WHERE id = 1"
19 | );
20 | const response = {
21 | statusCode: 200,
22 | body:
23 | res.rows.length > 0 ? JSON.stringify(res.rows[0]) : JSON.stringify(""),
24 | };
25 | return response;
26 | } catch (e) {
27 | logger.error(e.toString());
28 | const response = {
29 | statusCode: 500,
30 | body: JSON.stringify("Server error"),
31 | };
32 | return response;
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/functions/init.ts:
--------------------------------------------------------------------------------
1 | import Connection from "./lib/connect";
2 | import { Logger } from "@aws-lambda-powertools/logger";
3 | import cfnResponse from "cfn-response";
4 |
5 | const logger = new Logger({ serviceName: "initLambda" });
6 |
7 | export const handler = async (event: any, context: any): Promise => {
8 | if (event.RequestType == "Create" || event.RequestType == "Update") {
9 | try {
10 | const client = await Connection();
11 | // Connection
12 | await client.connect();
13 | logger.info("connected");
14 |
15 | // Query
16 | const res1 = await client.query("DROP TABLE IF EXISTS sampleapp_table;");
17 | logger.info(res1);
18 | const res2 = await client.query(
19 | 'CREATE TABLE IF NOT EXISTS sampleapp_table(id serial NOT NULL,name text COLLATE pg_catalog."default" NOT NULL,job0001_flag boolean NOT NULL DEFAULT false,job0002_flag boolean NOT NULL DEFAULT false,job0003_flag boolean NOT NULL DEFAULT false,job0004_flag boolean NOT NULL DEFAULT false,job0005_flag boolean NOT NULL DEFAULT false,CONSTRAINT sample_app_pkey PRIMARY KEY (id));'
20 | );
21 | logger.info(res2);
22 | const res3 = await client.query(
23 | "INSERT INTO sampleapp_table(name, job0001_flag, job0002_flag, job0003_flag, job0004_flag, job0005_flag) VALUES ('test record 1',true,true,true,true,true);"
24 | );
25 | logger.info(res3);
26 | return cfnResponse.send(
27 | event,
28 | context,
29 | cfnResponse.SUCCESS,
30 | { message: Date.now().toString() },
31 | event.PhysicalResourceId
32 | );
33 | } catch (e) {
34 | logger.error(e.toString());
35 | return cfnResponse.send(
36 | event,
37 | context,
38 | cfnResponse.SUCCESS,
39 | { message: Date.now().toString() },
40 | event.PhysicalResourceId
41 | );
42 | }
43 | }
44 | return cfnResponse.send(
45 | event,
46 | context,
47 | cfnResponse.SUCCESS,
48 | { message: Date.now().toString() },
49 | event.PhysicalResourceId
50 | );
51 | };
52 |
--------------------------------------------------------------------------------
/functions/lib/connect.ts:
--------------------------------------------------------------------------------
1 | const referSecrets = async () => {
2 | const { SecretsManagerClient, GetSecretValueCommand } = await import(
3 | "@aws-sdk/client-secrets-manager"
4 | );
5 | const secretsManager = new SecretsManagerClient({
6 | region: process.env.REGION!,
7 | });
8 | const response = await secretsManager.send(
9 | new GetSecretValueCommand({
10 | SecretId: process.env.SECRET_NAME!,
11 | })
12 | );
13 | return JSON.parse(response.SecretString!);
14 | };
15 |
16 | export default async function Connection() {
17 | const { Client } = await import("pg");
18 | const secrets = await referSecrets();
19 | const { Signer } = await import("@aws-sdk/rds-signer");
20 | const signer = new Signer({
21 | region: process.env.REGION!,
22 | username: secrets.username,
23 | hostname: process.env.HOST!,
24 | port: secrets.port,
25 | });
26 | const token = await signer.getAuthToken();
27 | // client settings
28 | const client = new Client({
29 | host: process.env.HOST!,
30 | port: secrets.port,
31 | user: secrets.username,
32 | password: token, //secrets.password,
33 | ssl: true,
34 | });
35 | return client;
36 | }
37 |
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "init.ts",
6 | "dependencies": {
7 | "@aws-lambda-powertools/logger": "^1.13.0",
8 | "@aws-sdk/client-secrets-manager": "^3.427.0",
9 | "@aws-sdk/rds-signer": "^3.427.0",
10 | "cfn-response": "^1.0.1",
11 | "lodash": "^4.17.21",
12 | "pg": "^8.11.3"
13 | },
14 | "scripts": {
15 | "test": "echo \"Error: no test specified\" && exit 1"
16 | },
17 | "devDependencies": {
18 | "@types/cfn-response": "^1.0.8",
19 | "@types/lodash": "^4.14.202",
20 | "@types/node": "^20.10.4"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/functions/post.ts:
--------------------------------------------------------------------------------
1 | import Connection from "./lib/connect";
2 | import { Logger } from "@aws-lambda-powertools/logger";
3 | import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
4 | import { isBoolean } from "lodash";
5 |
6 | const logger = new Logger({ serviceName: "postLambda" });
7 |
8 | export const handler = async (
9 | event: APIGatewayProxyEvent
10 | ): Promise => {
11 | if (!event.body) {
12 | const response = {
13 | statusCode: 400,
14 | body: JSON.stringify("Body is null"),
15 | };
16 | logger.error("Body is null");
17 | return response;
18 | }
19 | const body = JSON.parse(event.body);
20 | const {
21 | id,
22 | job0001_flag,
23 | job0002_flag,
24 | job0003_flag,
25 | job0004_flag,
26 | job0005_flag,
27 | } = body;
28 |
29 | // check if there is data
30 | if (
31 | !id ||
32 | job0001_flag == undefined ||
33 | job0002_flag == undefined ||
34 | job0003_flag == undefined ||
35 | job0004_flag == undefined ||
36 | job0005_flag == undefined
37 | ) {
38 | const response = {
39 | statusCode: 400,
40 | body: JSON.stringify("Some parameters are undefined"),
41 | };
42 | logger.error("Some parameters are undefined");
43 | return response;
44 | }
45 | // check their types and formats
46 | if (Number.isNaN(parseInt(id))) {
47 | logger.error("id is not a number");
48 | return { statusCode: 400, body: JSON.stringify("id is not a number") };
49 | }
50 | if (
51 | !isBoolean(job0001_flag) ||
52 | !isBoolean(job0002_flag) ||
53 | !isBoolean(job0003_flag) ||
54 | !isBoolean(job0004_flag) ||
55 | !isBoolean(job0005_flag)
56 | ) {
57 | logger.error("Any flag parameters are not Boolean");
58 | return {
59 | statusCode: 400,
60 | body: JSON.stringify("Any flag parameters are not Boolean"),
61 | };
62 | }
63 |
64 | try {
65 | const client = await Connection();
66 | // Connection
67 | await client.connect();
68 | logger.info("connected");
69 |
70 | // Query
71 | const res = await client.query(
72 | "UPDATE sampleapp_table SET job0001_flag = $1, job0002_flag = $2, job0003_flag = $3, job0004_flag = $4, job0005_flag = $5 WHERE id = $6",
73 | [job0001_flag, job0002_flag, job0003_flag, job0004_flag, job0005_flag, id]
74 | );
75 | const response = {
76 | statusCode: 200,
77 | body: JSON.stringify(res),
78 | };
79 | return response;
80 | } catch (e) {
81 | logger.error(e.toString());
82 | const response = {
83 | statusCode: 500,
84 | body: JSON.stringify("Server error"),
85 | };
86 | return response;
87 | }
88 | };
89 |
--------------------------------------------------------------------------------
/infra/.eslintignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /cdk.out
--------------------------------------------------------------------------------
/infra/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | es2021: true,
4 | node: true,
5 | },
6 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
7 | parser: '@typescript-eslint/parser',
8 | parserOptions: {
9 | ecmaVersion: 'latest',
10 | sourceType: 'module',
11 | },
12 | plugins: ['@typescript-eslint'],
13 | rules: {},
14 | };
15 |
--------------------------------------------------------------------------------
/infra/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | !.eslintrc.js
3 | !jest.config.js
4 | !stages.js
5 | !gulpfile.js
6 | *.d.ts
7 | node_modules
8 |
9 | # CDK asset staging directory
10 | .cdk.staging
11 | cdk.out
12 | cdk.context.json
13 | cdk-*-outputs.json
14 | .DS_Store
15 | .env
16 | scoutsuite-report*
17 |
18 | # infra
19 | !ssl/openssl_sign_inca.cnf
20 | ssl/*
21 | certificate_arn.json
--------------------------------------------------------------------------------
/infra/.npmignore:
--------------------------------------------------------------------------------
1 | *.ts
2 | !*.d.ts
3 |
4 | # CDK asset staging directory
5 | .cdk.staging
6 | cdk.out
7 |
--------------------------------------------------------------------------------
/infra/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "printWidth": 100
5 | }
6 |
--------------------------------------------------------------------------------
/infra/README_serverless.md:
--------------------------------------------------------------------------------
1 | # Serverless application version
2 |
3 | This is CDK code to build an environment to run serverless sample applications and batch systems on AWS.
4 |
5 | ## Overview
6 |
7 | If the number of accesses is low or there is a time period where there is almost no access, running servers all the time using ECS/Fargate costs a lot compared to the actual usage amount. There are also operating costs for container images.
8 |
9 | In such cases, reduce costs and operational troubles by using serverless by S3 and Lambda.
10 |
11 | Hosting an internal HTTPS static website using ALB, S3, and PrivateLink in a closed network is described in [this blog](https://aws.amazon.com/jp/blogs/networking-and-content-delivery/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/).
12 | This template includes architecture of this blog.
13 | By using this source code, you can automate complicated Internal ALB settings, etc.
14 |
15 | The architecture diagram is as follows. (The area circled red is the difference from the container version)
16 |
17 | 
18 |
19 | When using Private Link, it looks like this:
20 | 
21 |
22 | ## Preparation
23 | ### 1. Configuring the AWS CLI
24 |
25 | In order to use the CDK and deploy this application it is necessary to configure the `AWS CLI`. On a terminal run the following command:
26 |
27 | ```bash
28 | $ aws configure --profile {profile name}
29 | ```
30 |
31 | Run and enter the required information in response to the prompts that appear.
32 |
33 | The access key, secret key, and default region that are displayed when an IAM user is created are checked.
34 | For more information, see [Quick Setup with aws configure - Profiles](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-profiles).
35 |
36 | ### 2. Rewrite stages.js
37 |
38 | This template is using Task Runner [gulp](https://gulpjs.com/) for deployment.
39 | The variables referred to from gulp are defined in `stages.js`, so they can be changed according to each environment.
40 |
41 | ```javascript
42 | default: {
43 | appName,
44 | awsProfile: 'myProfile',
45 | alias: 'default',
46 | deployEnv: 'dev',
47 | notifyEmail: 'default-mail@default-mail.com',
48 | enabledPrivateLink: false,
49 | windowsBastion: true,
50 | linuxBastion: true,
51 | domainName: 'templateapp.local',
52 | },
53 | alias: {
54 | appName: '', // application's name ex: demoapp
55 | awsProfile: '', // aws profile that you configured in step 1
56 | alias: '', // identifier to deploy to same aws account by other teammates. ex: ysuzuki
57 | deployEnv: '' // deploy stage ex: dev, stage, prod
58 | notifyEmail: '', // This e-mail to send message when job was failed.
59 | enabledPrivateLink: , // Whether using PrivateLink or not. true is using PrivateLink, and false is not.
60 | windowsBastion: true, // Whether using Windows Bastion instance or not. true is using it, and false is not.
61 | linuxBastion: true, // Whether using Amazon Linux Bastion instance or not. true is using it, and false is not.
62 | domainName: 'templateapp.local', // It will be registered to Private Hosted Zone.
63 | }
64 | ```
65 |
66 | ### 3. Create self-signed certificate
67 |
68 | Self-signed certificate will be used in this sample to use HTTPS.
69 | Please run this command to import certificate to Amazon Certificate Manager in `infra` dir.
70 | Please install `OpenSSL` to your local environment befeore running these commands.
71 |
72 | ```bash
73 | $ npm install
74 | $ npm run create-certificate -- --{alias}
75 | ```
76 |
77 | ## How to deploy
78 |
79 | ### 1. CDK
80 |
81 | After deployment, the comannds to get keypairs will be shown in same terminal.
82 | If you want to use ssh from your client or RDP connection via FleetManager, please get keypairs by commands like below.
83 |
84 | (Correct keypair ID will be included in fact.)
85 |
86 | 1. The case of Windows instance in ap-northeast-1 region.
87 |
88 | ```
89 | {alias}{stage}{appName}Webapp.WindowsGetSSHKeyForWindowsInstanceCommand = aws ssm get-parameter --name /ec2/keypair/key-XXXXXXXXXXXXXXXXX --region ap-northeast-1 --with-decryption --query Parameter.Value --output text
90 | ```
91 |
92 | 2. The case of Amazon Linux instance in ap-northeast-1 region.
93 |
94 | ```
95 | {alias}{stage}{appName}Webapp.LinuxGetSSHKeyForLinuxInstanceCommand = aws ssm get-parameter --name /ec2/keypair/key-XXXXXXXXXXXXXXXXX --region ap-northeast-1 --with-decryption --query Parameter.Value --output text
96 | ```
97 |
98 | > NOTE:
99 | > If you deploy this template at first, there are many outputs in your terminal.
100 | > So, you may not find these commands in your terminal.
101 | > In this case, please go to CloudFormation's console in your browser.
102 | > And open the `Output` tab of `Webapp stack`. You can see commands in your screen like below image.
103 | > 
104 |
105 | And mail adderess you put in `stages.js` will receive email from Amazon SNS after CDK deployment.
106 | Please do confirmation of this email follow these steps in email to receive notification of job failed.
107 | And job will be start at 21:00 JST on weekdays. The initial data sets registered by deployment of sample web application is set so that all jobs succeed. So no notification is sent.
108 | If you want to confirm the failure notification, please change one of the 5 `trues` to `false` in the sample web application that will be deployed later.
109 |
110 | ### 2. Sample web apps
111 |
112 | Your source code repository was created after deploying CDK.
113 |
114 | > NOTE:
115 | > Your source code repository URL will be shown in your console after deploying CDK or in CloudFormation Console of AWS Management Console like below.
116 | > 
117 |
118 | Set `REACT_APP_ENDPOINT_URL` defined in the `.env` file in the `webapp-react` directory as `https://app.{domainName}/apigw/` ( use `domainName` in `stages.js`.)
119 |
120 | You can deploy sample web application via pipeline by following steps to push source code to your repository.
121 |
122 | ```bash
123 | $ cd./webapp-react
124 | $ git init
125 | $ git remote add origin https://git-codecommit.{your region}.amazonaws.com/v1/repos/{your repository name}
126 | $ git add.
127 | $ git commit -m "Initial commit"
128 | $ git push --set-upstream origin main
129 | $ git checkout -b develop
130 | $ git push --set-upstream origin develop
131 | ```
132 |
133 | > NOTE:
134 | > When the develop branch was changed, this pipeline will be invoked. So, you have to create develop branch.
135 |
136 | If you want to confirm pipeline situation, please access AWS CodePipeline via management console.
137 |
138 |
139 | #### CI/CD Pipeline
140 |
141 | The implementation of this CI/CD is based on the BlackBelt sample: [(Black Belt AWS - Page 52)](https://d1.awsstatic.com/webinars/jp/pdf/services/20201111_BlackBelt_AWS%20CodeStar_AWS_CodePipeline.pdf?page=52)
142 |
143 | If you want to replace it with your own web application or job script, replace the source code you push to CodeCommit with your own and modify the Dockerfile to suit your environment and application.
144 |
145 | ### 3. Testing
146 |
147 | When you want to check web application, you can access the app through the Bastion server on EC2.
148 | To access to the Bastion server via Fleet Manager Remote Desktop, you use the keypair that you've gotten in section [1. CDK].
149 | If you want to know about how to access the Bastion server via Fleet Manager Remote Desktop, please see [Connect to a managed node using Remote Desktop](https://docs.aws.amazon.com/systems-manager/latest/userguide/fleet-rdp.html#fleet-rdp-connect-to-node).
150 |
151 | If you can access to Bastion server, open your browser and enter the domain specified by `app.{domainName}` in `stages.js` to access the web application.
152 |
153 | If the following screen is displayed, it is successful.
154 |
155 | 
156 |
157 | ### 4. Delete environment
158 |
159 | If you want to delete the created environment, execute the following command:
160 |
161 |
162 | ```
163 | $ npm run destroy-serverless -- --{alias}
164 | ```
165 |
166 | Some resources that like a ECR may remain due to the status. So you may need to delete them manually.
167 | Ref:[(ecr): add option to auto delete images upon ECR repository removal #12618 ](https://github.com/aws/aws-cdk/issues/12618)
168 | If destroy command was failed, please check the error message or CloudFormation console to understand what happend and root cause of errors to solve them.
169 |
170 |
171 | ### Additional commands
172 |
173 | Since `diff, list`, which is the CDK command, has already been implemented in gulp, these commands can also be executed via gulp.
174 |
175 | ```
176 | $ npm run diff-serverless -- --{alias}
177 | $ npm run list-serverless -- --{alias}
178 | ```
179 |
180 | ## Considerations in production
181 |
182 | ### S3 bucket names
183 |
184 | In order to communicate, the S3 bucket name must match the website domain name.
185 | S3 bucket names must be unique across all AWS accounts, and this limitation may prevent you from deploying a website with your preferred domain name.
186 |
187 | ### Migration steps from the container version
188 |
189 | If you are using the container version and are thinking about moving to serverless, you need to follow the following steps.
190 |
191 | - Get the latest source code from GitHub, including the serverless version
192 | - `npm run destroy-webapp -- --{alias}` command to delete the deployed Webapp stack
193 | - The certificate has been created, so Implement the deployment according to the ”1. CDK” after `npm install` in function folder
194 | - Since Java application code has been deployed in the existing CodeCommit repository for webapps, delete only the source code while leaving git related files in the webapp-java directory, and copy the webapp-react source code to the webapp-java directory.
195 | - Next, rename the webapp-java directory to webapp-react
196 | - change `.env` by domain name in `stages.js`
197 | - Run the following command to push the react source code
198 |
199 | ```
200 | $ cd webapp-react
201 | $ git add.
202 | $ git commit -m "Initial commit"
203 | $ git push
204 | ```
205 |
--------------------------------------------------------------------------------
/infra/README_serverless_ja.md:
--------------------------------------------------------------------------------
1 | # サーバーレスアプリケーション版
2 |
3 | AWS 上にサーバーレスなサンプルアプリケーションやバッチシステムを動かす環境を構築する CDK のコードです。
4 |
5 | ## 概要
6 |
7 | アクセス数が少ない、またはほとんどアクセスしない時間帯があるようなアプリケーションを、ECS/Fargate を用いて常時稼働しておくと、実際の利用量に対し、費用がかかります。また、コンテナイメージなどの運用コストもあります。
8 |
9 | そのような場合に、ウェブサイト部分を S3 や Lambda を用いてサーバーレスで構成することによって、費用や運用の手間を減らすことができます。
10 |
11 | 閉域網における ALB、S3、PrivateLink による内部 HTTPS 静的 Web サイトのホスティングは、[こちらのブログ](https://aws.amazon.com/jp/blogs/news/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/)に記載されており、このブログをもとに CDK 化したものが、サーバーレス版の本ソースコード群の一部となります。本ソースコードをご利用いただくことで、煩雑な Internal ALB の設定などを自動化することができます。
12 |
13 | 構成図は以下のとおりです。(赤枠で囲ったところがコンテナ版との差分です)
14 |
15 | 
16 |
17 | Private Link を用いた場合、以下の通りになります。
18 | 
19 |
20 | ### 1. AWS CLI の設定
21 |
22 | CDK を利用するため、コマンドを実行する端末で AWS の設定が必要になります。
23 |
24 | ```bash
25 | $ aws configure --profile {プロファイル名}
26 | ```
27 |
28 | と実行し、表示されるプロンプトに応じて、必要な情報を入力してください。
29 |
30 | IAM ユーザ作成時に表示される、アクセスキーとシークレットキー、デフォルトのリージョンが確認されます。
31 | 詳しくは[aws configure を使用したクイック設定 - プロファイル](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-profiles)をご参照ください。
32 |
33 | ### 2. stages.js の書き換え
34 |
35 | 本テンプレートは、タスクランナーの[gulp](https://gulpjs.com/)を利用してデプロイを行います。
36 | gulp から参照される変数が`stages.js`で定義されているため、各自の環境に合わせて変更します。
37 |
38 | ```javascript
39 | default: {
40 | appName,
41 | awsProfile: 'myProfile',
42 | alias: 'default',
43 | deployEnv: 'dev',
44 | notifyEmail: 'johndoe@johndoe.mail.com',
45 | enabledPrivateLink: false,
46 | windowsBastion: true,
47 | linuxBastion: true,
48 | domainName: 'templateapp.local',
49 | },
50 | alias: {
51 | appName: '', // アプリの名前を入力します。 例: demoapp, など
52 | awsProfile: '', // 1で設定したProfile名を入力します。
53 | alias: '', // 個々人で環境面が被るのを回避するため、ユーザ名などの識別子を入力してください。 例: ysuzuki, など
54 | deployEnv: '' // デプロイする環境の面を記載します。例: dev, stage, prod, など
55 | notifyEmail: '', // ジョブが失敗した際の通知先メールアドレス
56 | enabledPrivateLink: false, // PrivateLinkを利用するかどうか。trueは利用し、falseは利用しない
57 | windowsBastion: true, // WindowsのBastionインスタンスを利用する場合はtrue、利用しない場合はfalse
58 | linuxBastion: true, // Amazon LinuxのBastionインスタンスを利用する場合はtrue、利用しない場合はfalse
59 | domainName: '', // Private Hosted Zoneに登録されるドメイン名(このドメイン名がS3のバケット名になり、S3 のバケット名はユニークである必要があるため、必ず変更してください。)
60 | }
61 | ```
62 |
63 | ### 3. 自己署名付き証明書の作成
64 |
65 | HTTPS 通信を実装するために、今回は自己署名付き証明書を用います。
66 | `infra`ディレクトリで次のコマンドを実行し、Amazon Certificate Manager に証明書をインポートしてください。
67 | また、以下のコマンド実行前に、`OpenSSL`のインストールを実施してください。
68 |
69 | ```bash
70 | $ npm install
71 | $ npm run create-certificate -- --{alias}
72 | ```
73 |
74 | ### 4. Lambda 関数に必要なモジュールのインストール
75 |
76 | `functions`ディレクトリで次のコマンドを実行し、Lambda 関数に必要なモジュールをインストールしてください。
77 |
78 | ```bash
79 | $ npm install
80 | ```
81 |
82 | ## デプロイ
83 |
84 | ### 1. CDK
85 |
86 | `infra`ディレクトリで以下のコマンドを実行してください。
87 | 自動的に CDK が実行され、AWS の各リソースが生成されます。
88 |
89 | ```bash
90 | $ npm run deploy-serverless -- --{alias}
91 | ```
92 |
93 | デプロイ後、ターミナル上に以下に示すようなコマンドが出力されますので、コピーして実行してください。
94 | 生成された EC2 インスタンス 用の Keypair がそれぞれ取得できます。
95 | コンソール接続する場合や Fleet Manager から RDP 接続する際には、Keypair の取得を行ってください。(コマンド実行時には Profile の指定をお願いします)
96 |
97 | ```
98 | // regionがap-northeast-1のWindowsインスタンスの場合
99 | $ {alias}{stage}{appName}Webapp.WindowsGetSSHKeyForWindowsInstanceCommand = aws ssm get-parameter --name /ec2/keypair/key-XXXXXXXXXXXXXXXXX --region ap-northeast-1 --with-decryption --query Parameter.Value --output text
100 |
101 | // regionがap-northeast-1のAmazonLinuxインスタンスの場合
102 | $ {alias}{stage}{appName}Webapp.LinuxGetSSHKeyForLinuxInstanceCommand = aws ssm get-parameter --name /ec2/keypair/key-XXXXXXXXXXXXXXXXX --region ap-northeast-1 --with-decryption --query Parameter.Value --output text
103 | ```
104 |
105 | > NOTE:
106 | > 初回デプロイ時は、ターミナルの出力が多いため、Keypair を取得するためのコマンドが見えなくなってしまうことがあります。
107 | > その場合は、ブラウザから CloudFormation のコンソールを開き、Webapp スタックの出力タブからご確認ください。
108 | > 
109 |
110 | また、CDK のデプロイが完了すると、`stages.js` に登録したメールアドレス宛に、Amazon SNS よりサブスクリプションの確認メールが届きます。
111 |
112 | ジョブが失敗した通知を受けるために、届いたメールの内容に従い、サブスクリプションの Confirmation を実施してください。
113 |
114 | また、バッチジョブは平日 21 時に実行される設定になっています。デプロイ時に登録される初期データは、ジョブがすべて成功する設定になっているため、メールは送信されません。
115 | もし、失敗を確認したい場合は、この後デプロイするサンプル Web アプリで、5 つある`true`のいずれかを`false`へ変更してください。
116 |
117 | ### 2. サンプル Web アプリ
118 |
119 | CDK のデプロイが完了したことで、AWS CodeCommit に サンプル Web アプリ用のリポジトリが作成されています。
120 |
121 | > NOTE:
122 | > リポジトリの URL はデプロイをしたターミナルもしくは、CloudFormation のコンソールに表示されます。
123 | > CloudFormation のコンソールを参照する場合は、`baseStack`の`出力`タブを参照ください。
124 | > 
125 |
126 | `webapp-react` ディレクトリの`.env`ファイルに定義された`REACT_APP_ENDPOINT_URL`を、`stages.js`で設定した`domainName`を使って`https://app.{domainName}/apigw/`に置き換えてください。
127 |
128 | その後、以下の手順で、`webapp-react` ディレクトリのソースコードをプッシュすることで、サンプル Web アプリがパイプラインからデプロイされます。
129 |
130 | ```bash
131 | $ cd ./webapp-react
132 | $ git init
133 | $ git remote add origin https://git-codecommit.{your region}.amazonaws.com/v1/repos/{your repository name}
134 | $ git add .
135 | $ git commit -m "Initial commit"
136 | $ git push --set-upstream origin main
137 | $ git checkout -b develop
138 | $ git push --set-upstream origin develop
139 | ```
140 |
141 | > NOTE:
142 | > CodePipeline のトリガーは develop ブランチを監視しています。そのため、develop ブランチの作成が必要になります。
143 |
144 | パイプラインの状況を確認したい場合は、マネジメントコンソールより AWS CodePipeline へアクセスしてください。
145 |
146 | #### CI/CD パイプラインについて
147 |
148 | Web アプリ向けの CI/CD は BlackBelt で紹介されている[構成例(Page 52)](https://d1.awsstatic.com/webinars/jp/pdf/services/20201111_BlackBelt_AWS%20CodeStar_AWS_CodePipeline.pdf)を元に実装しています。
149 |
150 | ご自身の Web アプリケーションに差し替えたい場合は、CodeCommit にプッシュするソースコードをご自身のものに差し替え、ご自身の環境やアプリケーションに合わせ、Dockerfile を修正してください。
151 |
152 | ### 3. 動作確認
153 |
154 | デプロイした Web アプリの動作を確認したい場合、Bastion として構築した Windows が起動している EC2 上でブラウザを起動し、アプリケーションにアクセスします。
155 |
156 | Bastion にアクセスする Keypair は [デプロイ - 1. CDK](#1-cdk) で取得したものを利用し、Fleet Manager 経由でアクセスします。
157 | Fleet Manager を利用した RDP 接続の方法は、[リモートデスクトップを使用してマネージドノードへ接続する](https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/fleet-rdp.html#fleet-rdp-connect-to-node)を参照ください。
158 |
159 | Bastion への RDP 接続ができたら、ブラウザを起動し、`stages.js`の`domainName` で `app.{domainName}` を入力し、アプリケーションにアクセスしてください。
160 |
161 | 次のような画面が表示されたら成功です。
162 |
163 | 
164 |
165 | ### 4. 作成した環境の削除
166 |
167 | 生成した環境を削除したい場合は、以下のコマンドを実行してください。
168 | ECR など、状況によっては残ってしまうリソースもあるため、手動での削除が必要な場合があります。
169 | ご参考:[(ecr): add option to auto delete images upon ECR repository removal #12618 ](https://github.com/aws/aws-cdk/issues/12618)
170 | コマンドが失敗した場合は、エラーメッセージや CloudFormation のコンソールで内容をご確認の上、対応ください。
171 |
172 | ```
173 | $ npm run destroy-serverless -- --{alias}
174 | ```
175 |
176 | ### その他のコマンド
177 |
178 | CDK のコマンドである、`diff, list`は、gulp で実装済みのため、これらのコマンドも gulp 経由で実行可能です。
179 |
180 | ```
181 | $ npm run diff-serverless -- --{alias}
182 | $ npm run list-serverless -- --{alias}
183 | ```
184 |
185 | ## 本番利用時の考慮点
186 |
187 | ### S3 のバケット名について
188 |
189 | 通信を疎通させるために、S3 のバケット名をウェブサイトのドメイン名と一致させる必要があります。
190 | S3 のバケット名は全ての AWS アカウント間でユニークである必要があり、この制約により希望のドメイン名でウェブサイトをデプロイできない場合があります。
191 |
192 | ### コンテナ版からの移行手順
193 |
194 | コンテナ版を使っていて、サーバーレスへの移行を考えているときは、大まかには次のような手順を踏む必要があります。
195 |
196 | - GitHub からサーバーレス版のソースコードを含んだ、最新のソースコードを取得する
197 | - `npm run destroy-webapp -- --{alias}` コマンドを利用し、デプロイ済みの Webapp Stack を削除する
198 | - 証明書の作成は完了しているため、`functions`ディレクトリでLambda 関数に必要なモジュールをインストールしてから、本 README の 1. CDK に従い、デプロイを実施する
199 | - 既存の Webapp 用の CodeCommit リポジトリは、Java アプリケーションコードがデプロイされているため、webapp-java のディレクトリ内の git 関連ファイルを残したまま、ソースコードだけを削除し、webapp-react のソースコードを webapp-java ディレクトリにコピーする。
200 | - 続いて、webapp-java のディレクトリ名を webapp-react に変更する
201 | - `.env` のドメインを `stages.js` のものと一致させる
202 | - 以下のコマンドを実行し、react のソースコードを push する
203 |
204 | ```
205 | $ cd webapp-react
206 | $ git add .
207 | $ git commit -m "Initial commit"
208 | $ git push
209 | ```
210 |
--------------------------------------------------------------------------------
/infra/bin/base.ts:
--------------------------------------------------------------------------------
1 | import { capitalize } from 'lodash';
2 | import * as cdk from 'aws-cdk-lib';
3 |
4 | import { BaseStack } from '../lib/base-stack';
5 | import { DefaultStackSynthesizer } from 'aws-cdk-lib';
6 | import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
7 | import { Aspects } from 'aws-cdk-lib';
8 |
9 | const env = {
10 | account: process.env.CDK_DEFAULT_ACCOUNT,
11 | region: process.env.AWS_REGION || process.env.CDK_DEFAULT_REGION,
12 | };
13 |
14 | const app = new cdk.App();
15 | // Add the cdk-nag AwsSolutions Pack with extra verbose logging enabled.
16 | Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true, reports: true }));
17 |
18 | const stageAlias = app.node.tryGetContext('stage_alias') || 'defaultAlias';
19 | const appName = app.node.tryGetContext('app_name') || 'defaultApp';
20 | const deployEnv = app.node.tryGetContext('deploy_env') || 'defaultEnv';
21 |
22 | const qualifier = `${stageAlias.slice(0, 5)}${deployEnv.slice(0, 5)}`;
23 |
24 | const id = `${capitalize(stageAlias)}${capitalize(deployEnv)}${capitalize(appName)}`;
25 |
26 | const base = new BaseStack(app, `${id}Base`, {
27 | env,
28 | synthesizer: new DefaultStackSynthesizer({
29 | qualifier,
30 | }),
31 | description:
32 | 'BaseStack will provision static resourcse, like a database, repository, and vpc (uksb-1tupboc54) (tag:base).',
33 | });
34 |
35 | // cdk-nag suppressions
36 | NagSuppressions.addStackSuppressions(base, [
37 | {
38 | id: 'CdkNagValidationFailure',
39 | reason: 'refer to https://github.com/cdklabs/cdk-nag/issues/817',
40 | },
41 | ]);
42 |
--------------------------------------------------------------------------------
/infra/bin/batch.ts:
--------------------------------------------------------------------------------
1 | import { capitalize } from 'lodash';
2 | import * as cdk from 'aws-cdk-lib';
3 |
4 | import { BatchStack } from '../lib/batch-stack';
5 | import { DefaultStackSynthesizer } from 'aws-cdk-lib';
6 | import { AwsSolutionsChecks } from 'cdk-nag';
7 | import { Aspects } from 'aws-cdk-lib';
8 |
9 | const env = {
10 | account: process.env.CDK_DEFAULT_ACCOUNT,
11 | region: process.env.AWS_REGION || process.env.CDK_DEFAULT_REGION,
12 | };
13 |
14 | const app = new cdk.App();
15 | // Add the cdk-nag AwsSolutions Pack with extra verbose logging enabled.
16 | Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true, reports: true }));
17 |
18 | const stageAlias = app.node.tryGetContext('stage_alias') || 'defaultAlias';
19 | const appName = app.node.tryGetContext('app_name') || 'defaultApp';
20 | const deployEnv = app.node.tryGetContext('deploy_env') || 'defaultEnv';
21 | const notifyEmail = app.node.tryGetContext('notify_email');
22 | const appVpcId = app.node.tryGetContext('app_vpc_id') || 'defaultVpc';
23 |
24 | if (!notifyEmail) {
25 | throw new Error('No notify email address in stages.js');
26 | }
27 |
28 | const repositoryName = cdk.Fn.importValue('BatchContainerRepositoryName');
29 | const auroraSecretName = cdk.Fn.importValue('SecretName');
30 | const auroraSecurityGroupId = cdk.Fn.importValue('AuroraSecurityGroupId');
31 | const auroraSecretEncryptionKeyArn = cdk.Fn.importValue('AuroraSecretEncryptionKeyArn');
32 |
33 | const qualifier = `${stageAlias.slice(0, 5)}${deployEnv.slice(0, 5)}`;
34 |
35 | const id = `${capitalize(stageAlias)}${capitalize(deployEnv)}${capitalize(appName)}`;
36 |
37 | new BatchStack(app, `${id}Batch`, {
38 | env,
39 | synthesizer: new DefaultStackSynthesizer({
40 | qualifier,
41 | }),
42 | description:
43 | 'BatchStack will provision stepfunctions statemachine and ecs cluster for batch (uksb-1tupboc54) (tag:batch).',
44 | notifyEmail,
45 | repositoryName,
46 | vpcId: appVpcId,
47 | auroraSecretName,
48 | auroraSecurityGroupId,
49 | auroraSecretEncryptionKeyArn,
50 | });
51 |
--------------------------------------------------------------------------------
/infra/bin/serverless-webapp.ts:
--------------------------------------------------------------------------------
1 | import { capitalize } from 'lodash';
2 | import * as cdk from 'aws-cdk-lib';
3 | import { ServerlessappStack } from '../lib/serverlessapp-stack';
4 | import { DefaultStackSynthesizer } from 'aws-cdk-lib';
5 | import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
6 | import { Aspects } from 'aws-cdk-lib';
7 |
8 | const env = {
9 | account: process.env.CDK_DEFAULT_ACCOUNT,
10 | region: process.env.AWS_REGION || process.env.CDK_DEFAULT_REGION,
11 | };
12 |
13 | const app = new cdk.App();
14 | // Add the cdk-nag AwsSolutions Pack with extra verbose logging enabled.
15 | Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true, reports: true }));
16 |
17 | const stageAlias = app.node.tryGetContext('stage_alias') || 'defaultAlias';
18 | const appName = app.node.tryGetContext('app_name') || 'defaultApp';
19 | const deployEnv = app.node.tryGetContext('deploy_env') || 'defaultEnv';
20 | const enabledPrivateLink = app.node.tryGetContext('enabled_privatelink') || false;
21 | const vpcId = app.node.tryGetContext('app_vpc_id') || 'defaultVpc';
22 | const windowsBastion = app.node.tryGetContext('windows_bastion') || false;
23 | const linuxBastion = app.node.tryGetContext('linux_bastion') || false;
24 | const domainName = app.node.tryGetContext('domain_name') || 'defaultDomain';
25 | const certificateArn = app.node.tryGetContext('certificate_arn') || 'defaultCert';
26 |
27 | const auroraSecretName = cdk.Fn.importValue('SecretName');
28 | const auroraSecretArn = cdk.Fn.importValue('SecretArn');
29 | const auroraSecurityGroupId = cdk.Fn.importValue('AuroraSecurityGroupId');
30 | const auroraSecretEncryptionKeyArn = cdk.Fn.importValue('AuroraSecretEncryptionKeyArn');
31 | const auroraEdition = cdk.Fn.importValue('AuroraEdition');
32 | const rdsProxyEndpoint = cdk.Fn.importValue('RdsProxyEndpoint');
33 | const rdsProxyArn = cdk.Fn.importValue('RdsProxyArn');
34 | const containerRepositoryName = cdk.Fn.importValue('WebappContainerRepositoryName');
35 | const sourceRepositoryName = cdk.Fn.importValue('WebappSourceRepositoryName');
36 |
37 | const qualifier = `${stageAlias.slice(0, 5)}${deployEnv.slice(0, 5)}`;
38 |
39 | const id = `${capitalize(stageAlias)}${capitalize(deployEnv)}${capitalize(appName)}`;
40 | const webappStack = new ServerlessappStack(app, `${id}Webapp`, {
41 | env: env,
42 | synthesizer: new DefaultStackSynthesizer({
43 | qualifier,
44 | }),
45 | description: 'ServerlessappStack will provision APIGW, Lambda function, bastions, and CI/CD pipeline (uksb-1tupboc54) (tag:webapp-serverless).',
46 | auroraSecretName,
47 | auroraSecretArn,
48 | auroraSecurityGroupId,
49 | auroraSecretEncryptionKeyArn,
50 | auroraEdition,
51 | rdsProxyEndpoint,
52 | rdsProxyArn,
53 | containerRepositoryName,
54 | enabledPrivateLink: enabledPrivateLink.toLowerCase() === 'true',
55 | testVpcCidr: '10.2.0.0/16',
56 | sourceRepositoryName,
57 | vpcId,
58 | windowsBastion,
59 | linuxBastion,
60 | domainName,
61 | certificateArn,
62 | });
63 | // cdk-nag suppressions
64 | NagSuppressions.addStackSuppressions(webappStack, [
65 | {
66 | id: 'AwsSolutions-IAM5',
67 | reason: 'To use ManagedPolicy',
68 | },
69 | ]);
70 |
--------------------------------------------------------------------------------
/infra/bin/webapp.ts:
--------------------------------------------------------------------------------
1 | import { capitalize } from 'lodash';
2 | import * as cdk from 'aws-cdk-lib';
3 | import { WebappStack } from '../lib/webapp-stack';
4 | import { DefaultStackSynthesizer } from 'aws-cdk-lib';
5 | import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
6 | import { Aspects } from 'aws-cdk-lib';
7 |
8 | const env = {
9 | account: process.env.CDK_DEFAULT_ACCOUNT,
10 | region: process.env.AWS_REGION || process.env.CDK_DEFAULT_REGION,
11 | };
12 |
13 | const app = new cdk.App();
14 | // Add the cdk-nag AwsSolutions Pack with extra verbose logging enabled.
15 | Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true, reports: true }));
16 |
17 | const stageAlias = app.node.tryGetContext('stage_alias') || 'defaultAlias';
18 | const appName = app.node.tryGetContext('app_name') || 'defaultApp';
19 | const deployEnv = app.node.tryGetContext('deploy_env') || 'defaultEnv';
20 | const enabledPrivateLink = app.node.tryGetContext('enabled_privatelink') || false;
21 | const vpcId = app.node.tryGetContext('app_vpc_id') || 'defaultVpc';
22 | const windowsBastion = app.node.tryGetContext('windows_bastion') || false;
23 | const linuxBastion = app.node.tryGetContext('linux_bastion') || false;
24 | const domainName = app.node.tryGetContext('domain_name') || 'defaultDomain';
25 | const certificateArn = app.node.tryGetContext('certificate_arn') || 'defaultCert';
26 |
27 | const auroraSecretName = cdk.Fn.importValue('SecretName');
28 | const auroraSecurityGroupId = cdk.Fn.importValue('AuroraSecurityGroupId');
29 | const auroraSecretEncryptionKeyArn = cdk.Fn.importValue('AuroraSecretEncryptionKeyArn');
30 | const containerRepositoryName = cdk.Fn.importValue('WebappContainerRepositoryName');
31 | const sourceRepositoryName = cdk.Fn.importValue('WebappSourceRepositoryName');
32 |
33 | const qualifier = `${stageAlias.slice(0, 5)}${deployEnv.slice(0, 5)}`;
34 |
35 | const id = `${capitalize(stageAlias)}${capitalize(deployEnv)}${capitalize(appName)}`;
36 | const webappStack = new WebappStack(app, `${id}Webapp`, {
37 | env: env,
38 | synthesizer: new DefaultStackSynthesizer({
39 | qualifier,
40 | }),
41 | description:
42 | 'WebappStack will provision ecs cluster for webapp, load balancers, bastions, and CI/CD pipeline (uksb-1tupboc54) (tag:webapp-container).',
43 | auroraSecretName,
44 | auroraSecurityGroupId,
45 | auroraSecretEncryptionKeyArn,
46 | containerRepositoryName,
47 | enabledPrivateLink: enabledPrivateLink.toLowerCase() === 'true',
48 | testVpcCidr: '10.2.0.0/16',
49 | sourceRepositoryName,
50 | vpcId,
51 | windowsBastion,
52 | linuxBastion,
53 | domainName,
54 | certificateArn,
55 | });
56 | // cdk-nag suppressions
57 | NagSuppressions.addStackSuppressions(webappStack, [
58 | {
59 | id: 'AwsSolutions-IAM5',
60 | reason: 'To use ManagedPolicy',
61 | },
62 | ]);
63 |
--------------------------------------------------------------------------------
/infra/buildWebEnv.mjs:
--------------------------------------------------------------------------------
1 | import lodash from 'lodash';
2 | import { readFile, writeFile } from 'fs/promises';
3 |
4 | const { endsWith, startsWith } = lodash;
5 |
6 | const envFile = '../webapp/.env.production';
7 | const stageName = process.argv[2];
8 |
9 | let cdkOutputData = {};
10 |
11 | async function append(data) {
12 | await writeFile(envFile, data, { flag: 'a' }, (err) => {
13 | if (err) {
14 | console.error(err);
15 | return;
16 | }
17 | });
18 | }
19 |
20 | async function buildWebAppEnv() {
21 | let data = await readFile(new URL('./cdk-infra-outputs.json', import.meta.url));
22 | cdkOutputData = data ? JSON.parse(data) : {};
23 | let stageKeys = {};
24 |
25 | Object.keys(cdkOutputData).forEach((key) => {
26 | if (endsWith(key, 'baseline') && startsWith(key, stageName)) {
27 | stageKeys = cdkOutputData[key];
28 | }
29 | });
30 |
31 | Object.keys(stageKeys).forEach((key) => {
32 | key.includes('userpoolid') ? append(`VITE_COGNITO_USERPOOLID=${stageKeys[key]}\n`) : '';
33 | key.includes('userpoolclientid') ? append(`VITE_COGNITO_WEBCLIENTID=${stageKeys[key]}\n`) : '';
34 | key.includes('identitypoolid') ? append(`VITE_COGNITO_IDENTITYPOOLID=${stageKeys[key]}\n`) : '';
35 | key.includes('cognitourl') ? append(`VITE_COGNITO_ENDPOINT=${stageKeys[key]}\n`) : '';
36 | key.includes('graphqlurl') ? append(`VITE_GRAPHQL_URL=${stageKeys[key]}\n`) : '';
37 | key.includes('region') ? append(`VITE_COGNITO_REGION=${stageKeys[key]}\n`) : '';
38 | });
39 | }
40 |
41 | await writeFile(envFile, '', { flag: 'w+' }, (err) => {
42 | if (err) {
43 | console.error(err);
44 | return;
45 | }
46 | });
47 |
48 | await buildWebAppEnv();
49 |
--------------------------------------------------------------------------------
/infra/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "npx ts-node --prefer-ts-exts bin/base.ts",
3 | "watch": {
4 | "include": ["**"],
5 | "exclude": [
6 | "README.md",
7 | "cdk*.json",
8 | "**/*.d.ts",
9 | "**/*.js",
10 | "tsconfig.json",
11 | "package*.json",
12 | "yarn.lock",
13 | "node_modules",
14 | "test"
15 | ]
16 | },
17 | "context": {
18 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
19 | "@aws-cdk/core:stackRelativeExports": true,
20 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
21 | "@aws-cdk/aws-lambda:recognizeVersionProps": true,
22 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
23 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
24 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
25 | "@aws-cdk/core:checkSecretUsage": true,
26 | "@aws-cdk/aws-iam:minimizePolicies": true,
27 | "@aws-cdk/core:target-partitions": ["aws"],
28 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/infra/docker/nginx/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 |
3 | VOLUME /var/cache/nginx
4 | VOLUME /var/run
5 | VOLUME /etc/nginx/conf.d
6 | VOLUME /usr/share/nginx/html
7 |
8 | COPY default.conf /etc/nginx/conf.d/default.conf
9 | COPY static-content /usr/share/nginx/html
10 |
--------------------------------------------------------------------------------
/infra/docker/nginx/default.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 8080;
3 | listen [::]:8080;
4 | server_name localhost;
5 |
6 | location / {
7 | root /usr/share/nginx/html;
8 | index index.html index.htm;
9 | }
10 | }
--------------------------------------------------------------------------------
/infra/docker/nginx/static-content/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | It works!
4 |
5 |