├── .vscode ├── settings.json └── launch.json ├── usecases ├── webapp-java │ ├── lib │ │ ├── construct │ │ │ ├── index.ts │ │ │ ├── ecr │ │ │ │ ├── README.md │ │ │ │ └── ecr.ts │ │ │ ├── s3 │ │ │ │ ├── README.md │ │ │ │ └── bucket.ts │ │ │ ├── kms │ │ │ │ ├── README.md │ │ │ │ └── key.ts │ │ │ ├── codepipeline │ │ │ │ └── README.md │ │ │ ├── ec2 │ │ │ │ └── README.md │ │ │ ├── network │ │ │ │ └── README.md │ │ │ ├── ecs │ │ │ │ ├── README_SERVICE.md │ │ │ │ ├── README_BASE.md │ │ │ │ ├── README_JOB.md │ │ │ │ └── ecs-app-base.ts │ │ │ └── aurora │ │ │ │ └── README.md │ │ ├── domain-stack.ts │ │ ├── cicd-stack.ts │ │ ├── network-stack.ts │ │ ├── webapp-stack.ts │ │ └── storage-stack.ts │ ├── batch │ │ ├── src │ │ │ └── sample │ │ │ │ ├── __init__.py │ │ │ │ └── batch.py │ │ ├── requirements.txt │ │ ├── Dockerfile │ │ ├── README_ja.md │ │ └── README.md │ ├── .npmignore │ ├── docker │ │ └── nginx │ │ │ ├── static-content │ │ │ └── index.html │ │ │ ├── default.conf │ │ │ └── Dockerfile │ ├── ssl │ │ └── openssl_sign_inca.cnf │ ├── docs │ │ └── images │ │ │ ├── job.png │ │ │ ├── screenshot.png │ │ │ ├── repository_url_ja.png │ │ │ ├── architecture.drawio.png │ │ │ └── keypair_command_ja.png │ ├── webapp │ │ ├── src │ │ │ └── main │ │ │ │ ├── resources │ │ │ │ ├── data.sql │ │ │ │ ├── application.properties │ │ │ │ ├── schema.sql │ │ │ │ └── templates │ │ │ │ │ ├── sampleapplist.html │ │ │ │ │ └── sampleappform.html │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── sampleapp │ │ │ │ └── webapp │ │ │ │ ├── controller │ │ │ │ ├── form │ │ │ │ │ ├── SampleAppFormList.java │ │ │ │ │ └── SampleAppForm.java │ │ │ │ └── SampleAppController.java │ │ │ │ ├── domain │ │ │ │ ├── dto │ │ │ │ │ ├── SampleAppListDto.java │ │ │ │ │ └── SampleAppDto.java │ │ │ │ └── SampleAppService.java │ │ │ │ ├── repository │ │ │ │ ├── SampleAppRepository.java │ │ │ │ └── model │ │ │ │ │ └── SampleApp.java │ │ │ │ ├── WebappApplication.java │ │ │ │ └── ServletInitializer.java │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── settings.gradle │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── log4j2.xml │ │ ├── buildspec.yaml │ │ ├── README_ja.md │ │ ├── README.md │ │ ├── build.gradle │ │ └── gradlew.bat │ ├── jest.config.js │ ├── .gitignore │ ├── test │ │ └── webapp-java.test.ts │ ├── parameter_template.ts │ ├── package.json │ ├── tsconfig.json │ └── scripts │ │ └── create_certificate.sh ├── webapp-react │ ├── batch │ │ ├── src │ │ │ └── sample │ │ │ │ ├── __init__.py │ │ │ │ └── batch.py │ │ ├── requirements.txt │ │ ├── Dockerfile │ │ ├── README_ja.md │ │ └── README.md │ ├── functions │ │ ├── .gitignore │ │ ├── package.json │ │ ├── get.ts │ │ ├── lib │ │ │ └── connect.ts │ │ ├── init.ts │ │ └── post.ts │ ├── webapp │ │ ├── sample.env │ │ ├── .prettierrc.json │ │ ├── public │ │ │ ├── robots.txt │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ └── index.html │ │ ├── docs │ │ │ └── images │ │ │ │ └── screenshot.png │ │ ├── src │ │ │ ├── vite-env.d.ts │ │ │ ├── types │ │ │ │ └── record.ts │ │ │ ├── setupTests.ts │ │ │ ├── index.css │ │ │ ├── reportWebVitals.ts │ │ │ ├── modules │ │ │ │ └── requests.ts │ │ │ ├── index.tsx │ │ │ └── components │ │ │ │ ├── RecordForm.tsx │ │ │ │ ├── RecordList.tsx │ │ │ │ ├── RecordFormRow.tsx │ │ │ │ └── Dashboard.tsx │ │ ├── tsconfig.node.json │ │ ├── .gitignore │ │ ├── buildspec.yaml │ │ ├── vite.config.ts │ │ ├── README_ja.md │ │ ├── index.html │ │ ├── README.md │ │ ├── tsconfig.json │ │ └── package.json │ ├── .npmignore │ ├── ssl │ │ └── openssl_sign_inca.cnf │ ├── jest.config.js │ ├── .gitignore │ ├── lib │ │ ├── construct │ │ │ ├── ecr │ │ │ │ ├── README.md │ │ │ │ └── ecr.ts │ │ │ ├── codepipeline │ │ │ │ ├── README.md │ │ │ │ └── codepipeline-webapp-react.ts │ │ │ ├── s3 │ │ │ │ ├── README.md │ │ │ │ └── bucket.ts │ │ │ ├── kms │ │ │ │ ├── README.md │ │ │ │ └── key.ts │ │ │ ├── ec2 │ │ │ │ └── README.md │ │ │ ├── network │ │ │ │ └── README.md │ │ │ ├── aurora │ │ │ │ ├── README_dbinitlambda.md │ │ │ │ ├── README.md │ │ │ │ └── dbinitlambda.ts │ │ │ ├── serverless │ │ │ │ ├── README_lambda.md │ │ │ │ ├── README_apigw.md │ │ │ │ └── README_serverless_app.md │ │ │ └── ecs │ │ │ │ └── README_JOB.md │ │ ├── domain-stack.ts │ │ ├── cicd-stack.ts │ │ ├── storage-stack.ts │ │ └── serverlessapp-stack.ts │ ├── parameter_template.ts │ ├── package.json │ └── tsconfig.json └── infraops-console │ ├── webapp │ ├── .dockerignore │ ├── .gitignore │ ├── postcss.config.js │ ├── tailwind.config.js │ ├── app │ │ ├── entry.client.tsx │ │ ├── utils │ │ │ ├── error.server.ts │ │ │ ├── session.server.ts │ │ │ ├── abac-filter.server.ts │ │ │ └── jwt-verify.server.ts │ │ ├── hooks │ │ │ └── useDebounce.ts │ │ ├── components │ │ │ ├── RequirementBadge.tsx │ │ │ ├── Label.tsx │ │ │ ├── Slot.tsx │ │ │ ├── index.ts │ │ │ ├── RefreshButton.tsx │ │ │ ├── Input.tsx │ │ │ ├── Modal.tsx │ │ │ ├── UniversalLink.tsx │ │ │ ├── Select.tsx │ │ │ ├── ErrorAlert.tsx │ │ │ ├── Table.tsx │ │ │ └── Button.tsx │ │ ├── routes │ │ │ ├── _index.tsx │ │ │ ├── api.instance-types.tsx │ │ │ ├── logout.tsx │ │ │ ├── api.services.ts │ │ │ ├── error.tsx │ │ │ ├── api.schedules.ts │ │ │ └── login.tsx │ │ ├── root.tsx │ │ ├── styles │ │ │ └── global.css │ │ └── entry.server.tsx │ ├── tsconfig.json │ ├── vite.config.ts │ ├── Dockerfile │ ├── package.json │ └── README_group_access.md │ ├── parameter_template.ts │ ├── functions │ └── ice-recovery │ │ └── package.json │ ├── package.json │ ├── tsconfig.json │ └── bin │ └── infraops-console.ts ├── NOTICE ├── docs └── images │ ├── job.png │ ├── prerequirsite_en.png │ ├── prerequirsite_ja.png │ ├── keypair_command_en.png │ ├── keypair_command_ja.png │ ├── repository_url_en.png │ ├── repository_url_ja.png │ ├── webapp-java.drawio.png │ ├── webapp-react.drawio.png │ └── infraops-console.drawio.png ├── CODE_OF_CONDUCT.md ├── .gitignore ├── LICENSE └── CONTRIBUTING.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /usecases/webapp-java/batch/src/sample/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /usecases/webapp-react/batch/src/sample/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /usecases/webapp-react/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /usecases/webapp-java/batch/requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2-binary 2 | boto3 -------------------------------------------------------------------------------- /usecases/webapp-react/batch/requirements.txt: -------------------------------------------------------------------------------- 1 | psycopg2-binary 2 | boto3 -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/sample.env: -------------------------------------------------------------------------------- 1 | REACT_APP_ENDPOINT_URL="https://{your-domain}/apigw/" -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/.dockerignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | node_modules 4 | .DS_Store 5 | .env -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | build 3 | !postcss.config.js 4 | !tailwind.config.js -------------------------------------------------------------------------------- /usecases/webapp-java/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /usecases/webapp-react/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 100 5 | } 6 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Template of Closed Network System Works on AWS 2 | Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /docs/images/job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/docs/images/job.png -------------------------------------------------------------------------------- /usecases/webapp-java/docker/nginx/static-content/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

It works!

4 | 5 | -------------------------------------------------------------------------------- /docs/images/prerequirsite_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/docs/images/prerequirsite_en.png -------------------------------------------------------------------------------- /docs/images/prerequirsite_ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/docs/images/prerequirsite_ja.png -------------------------------------------------------------------------------- /docs/images/keypair_command_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/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/HEAD/docs/images/keypair_command_ja.png -------------------------------------------------------------------------------- /docs/images/repository_url_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/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/HEAD/docs/images/repository_url_ja.png -------------------------------------------------------------------------------- /docs/images/webapp-java.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/docs/images/webapp-java.drawio.png -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "@tailwindcss/postcss": {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /docs/images/webapp-react.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/docs/images/webapp-react.drawio.png -------------------------------------------------------------------------------- /usecases/webapp-java/ssl/openssl_sign_inca.cnf: -------------------------------------------------------------------------------- 1 | [ v3_ca ] 2 | basicConstraints = CA:true, pathlen:0 3 | keyUsage = cRLSign, keyCertSign 4 | nsCertType = sslCA, emailCA 5 | -------------------------------------------------------------------------------- /usecases/webapp-react/ssl/openssl_sign_inca.cnf: -------------------------------------------------------------------------------- 1 | [ v3_ca ] 2 | basicConstraints = CA:true, pathlen:0 3 | keyUsage = cRLSign, keyCertSign 4 | nsCertType = sslCA, emailCA 5 | -------------------------------------------------------------------------------- /docs/images/infraops-console.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/docs/images/infraops-console.drawio.png -------------------------------------------------------------------------------- /usecases/webapp-java/docs/images/job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-java/docs/images/job.png -------------------------------------------------------------------------------- /usecases/webapp-java/docs/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-java/docs/images/screenshot.png -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-react/webapp/public/favicon.ico -------------------------------------------------------------------------------- /usecases/infraops-console/parameter_template.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | deployEnv: 'dev', 3 | sourceVpcId: 'vpc-xxxxxxxxxxxxxxxxx', 4 | appRunnerVpcEndpointId: 'vpce-xxxxxxxxxxxxxxxxx' 5 | }; 6 | -------------------------------------------------------------------------------- /usecases/webapp-java/docs/images/repository_url_ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-java/docs/images/repository_url_ja.png -------------------------------------------------------------------------------- /usecases/webapp-java/docs/images/architecture.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-java/docs/images/architecture.drawio.png -------------------------------------------------------------------------------- /usecases/webapp-java/docs/images/keypair_command_ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-java/docs/images/keypair_command_ja.png -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/docs/images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-react/webapp/docs/images/screenshot.png -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO sampleapp_table(name, job0001_flag, job0002_flag, job0003_flag, job0004_flag, job0005_flag) VALUES ('test record 1',true,true,true,true,true); 2 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/template-for-closed-network-system-workloads-on-aws/HEAD/usecases/webapp-java/webapp/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /usecases/webapp-java/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /usecases/webapp-react/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /usecases/webapp-java/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | 10 | config 11 | !ssl/openssl_sign_inca.cnf 12 | ssl 13 | parameter.ts -------------------------------------------------------------------------------- /usecases/webapp-react/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | 10 | config 11 | ssl/* 12 | !ssl/openssl_sign_inca.cnf 13 | parameter.ts -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface ImportMetaEnv { 4 | readonly VITE_ENDPOINT_URL: string 5 | } 6 | 7 | interface ImportMeta { 8 | readonly env: ImportMetaEnv 9 | } 10 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | 3 | module.exports = { 4 | content: [ 5 | "./app/**/*.{js,jsx,ts,tsx}", 6 | ], 7 | theme: { 8 | extend: {}, 9 | }, 10 | plugins: [], 11 | } 12 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url 'https://repo.spring.io/milestone' } 4 | maven { url 'https://repo.spring.io/snapshot' } 5 | gradlePluginPortal() 6 | } 7 | } 8 | rootProject.name = 'webapp-java' 9 | -------------------------------------------------------------------------------- /usecases/webapp-java/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 | } -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/types/record.ts: -------------------------------------------------------------------------------- 1 | export type Record = { 2 | id: number; 3 | name: string; 4 | job0001_flag: boolean; 5 | job0002_flag: boolean; 6 | job0003_flag: boolean; 7 | job0004_flag: boolean; 8 | job0005_flag: boolean; 9 | }; 10 | -------------------------------------------------------------------------------- /usecases/webapp-java/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 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/controller/form/SampleAppFormList.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.controller.form; 2 | 3 | import java.util.List; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class SampleAppFormList { 8 | private List sampleAppFormList; 9 | } 10 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.url=jdbc:postgresql://${DB_ENDPOINT}:5432/postgres?sslmode=verify-full&sslrootcert=/tmp/root.pem 2 | spring.datasource.username=${DB_USERNAME} 3 | spring.datasource.password=${DB_PASSWORD} 4 | spring.sql.init.mode=always 5 | logging.level.org.springframework.web=INFO -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/entry.client.tsx: -------------------------------------------------------------------------------- 1 | import { RemixBrowser } from "@remix-run/react"; 2 | import { startTransition, StrictMode } from "react"; 3 | import { hydrateRoot } from "react-dom/client"; 4 | 5 | startTransition(() => { 6 | hydrateRoot( 7 | document, 8 | 9 | 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | 21 | !vite-env.d.ts -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Sample React App", 3 | "name": "Sample App", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /.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 | parameter.ts 18 | 19 | # usecases 20 | !ssl/openssl_sign_inca.cnf 21 | ssl/* 22 | certificate_arn.json 23 | dist 24 | 25 | .tool-versions -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ecr/README.md: -------------------------------------------------------------------------------- 1 | # Ecr Construct 2 | 3 | ## Purpose 4 | 5 | Create ECR repository. 6 | 7 | ## Required resources 8 | 9 | None 10 | 11 | ## Required parameters (props) 12 | 13 | None 14 | 15 | ## Properties 16 | 17 | | Name | Type | Description | 18 | | ------------------- | :-------------: | ----------: | 19 | | containerRepository | ecr.Repository; | | 20 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/ecr/README.md: -------------------------------------------------------------------------------- 1 | # Ecr Construct 2 | 3 | ## Purpose 4 | 5 | Create ECR repository. 6 | 7 | ## Required resources 8 | 9 | None 10 | 11 | ## Required parameters (props) 12 | 13 | None 14 | 15 | ## Properties 16 | 17 | | Name | Type | Description | 18 | | ------------------- | :-------------: | ----------: | 19 | | containerRepository | ecr.Repository; | | 20 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/buildspec.yaml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | phases: 3 | install: 4 | runtime-versions: 5 | nodejs: 22 6 | commands: 7 | - npm ci 8 | pre_build: 9 | commands: 10 | - echo "VITE_ENDPOINT_URL=https://app.${DOMAIN_NAME}/apigw/" > .env 11 | - cat .env 12 | build: 13 | commands: 14 | - npm run build 15 | artifacts: 16 | base-directory: build 17 | files: 18 | - '**/*' 19 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 4 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | } 8 | 9 | code { 10 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; 11 | } 12 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/domain/dto/SampleAppListDto.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.domain.dto; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | import org.springframework.stereotype.Component; 6 | import lombok.Data; 7 | 8 | @Data 9 | @Component 10 | public class SampleAppListDto implements Serializable { 11 | 12 | private List sampleAppList; 13 | } 14 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/utils/error.server.ts: -------------------------------------------------------------------------------- 1 | export interface AppError { 2 | message: string; 3 | details?: string; 4 | code?: string; 5 | } 6 | 7 | export const createAppError = ( 8 | message: string, 9 | error: any, 10 | code?: string 11 | ): AppError => { 12 | return { 13 | message, 14 | details: error instanceof Error ? error.message : String(error), 15 | code: code || error.Code || error.code 16 | }; 17 | }; 18 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/repository/SampleAppRepository.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.repository; 2 | 3 | import org.springframework.data.repository.CrudRepository; 4 | import org.springframework.stereotype.Repository; 5 | import com.example.sampleapp.webapp.repository.model.*;; 6 | 7 | @Repository 8 | public interface SampleAppRepository extends CrudRepository { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/WebappApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WebappApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WebappApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import tsconfigPaths from 'vite-tsconfig-paths' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), tsconfigPaths()], 8 | server: { 9 | port: 3000, 10 | open: true 11 | }, 12 | build: { 13 | outDir: 'build', 14 | sourcemap: true 15 | }, 16 | resolve: { 17 | alias: { 18 | '@': '/src' 19 | } 20 | } 21 | }) 22 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/ServletInitializer.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 5 | 6 | public class ServletInitializer extends SpringBootServletInitializer { 7 | 8 | @Override 9 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { 10 | return application.sources(WebappApplication.class); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS sampleapp_table; 2 | CREATE TABLE IF NOT EXISTS sampleapp_table 3 | ( 4 | id serial NOT NULL, 5 | name text COLLATE pg_catalog."default" NOT NULL, 6 | job0001_flag boolean NOT NULL DEFAULT false, 7 | job0002_flag boolean NOT NULL DEFAULT false, 8 | job0003_flag boolean NOT NULL DEFAULT false, 9 | job0004_flag boolean NOT NULL DEFAULT false, 10 | job0005_flag boolean NOT NULL DEFAULT false, 11 | CONSTRAINT sample_app_pkey PRIMARY KEY (id) 12 | ); 13 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/codepipeline/README.md: -------------------------------------------------------------------------------- 1 | # CodePipelineWebappReact Construct 2 | 3 | ## Purpose 4 | 5 | - Create CodePipeline to deploy to S3 6 | - Include CodeCommit and CodeBuild 7 | 8 | ## Required resources 9 | 10 | - CodeCommit repository 11 | - S3 bucket to deploy 12 | 13 | ## Required parameters (props) 14 | 15 | - `codeCommitRepository` : CodeCOmmit Repository, Get source code from this repository 16 | - `s3bucket` : S3 bucket, deploy destination 17 | 18 | ## Properties 19 | 20 | None 21 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/s3/README.md: -------------------------------------------------------------------------------- 1 | # Bucket Construct 2 | 3 | ## Purpose 4 | 5 | Creates two s3 buckets(A bucket and B bucket). 6 | A bucket is to store data or something. 7 | B bucket is to store the logs that something access to A bucket. 8 | 9 | ## Required resources 10 | 11 | None 12 | 13 | ## Required parameters (props) 14 | 15 | None 16 | 17 | ## Optional parameters (props) 18 | 19 | None 20 | 21 | ## Properties 22 | 23 | | Name | Type | Description | 24 | | ------ | :-------: | ----------: | 25 | | bucket | s3.Bucket | | 26 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/s3/README.md: -------------------------------------------------------------------------------- 1 | # Bucket Construct 2 | 3 | ## Purpose 4 | 5 | Creates two s3 buckets(A bucket and B bucket). 6 | A bucket is to store data or something. 7 | B bucket is to store the logs that something access to A bucket. 8 | 9 | ## Required resources 10 | 11 | None 12 | 13 | ## Required parameters (props) 14 | 15 | None 16 | 17 | ## Optional parameters (props) 18 | 19 | None 20 | 21 | ## Properties 22 | 23 | | Name | Type | Description | 24 | | ------ | :-------: | ----------: | 25 | | bucket | s3.Bucket | | 26 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/modules/requests.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const API_ENDPOINT = import.meta.env.VITE_ENDPOINT_URL; 4 | 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 6 | export const get = async (resource: string, params?: { [key: string]: any }) => { 7 | return await axios.get(`${API_ENDPOINT}${resource}`, { params: params }); 8 | }; 9 | 10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 | export const post = async (resource: string, data: any) => { 12 | return axios.post(`${API_ENDPOINT}${resource}`, data); 13 | }; 14 | -------------------------------------------------------------------------------- /usecases/infraops-console/functions/ice-recovery/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ice-recovery", 3 | "version": "1.0.0", 4 | "description": "Lambda function for EC2 instance recovery during ICE events", 5 | "main": "index.ts", 6 | "dependencies": { 7 | "@aws-lambda-powertools/logger": "^1.13.0", 8 | "@aws-sdk/client-ec2": "^3.427.0", 9 | "aws-lambda": "^1.0.7" 10 | }, 11 | "scripts": { 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "devDependencies": { 15 | "@types/aws-lambda": "^8.10.119", 16 | "@types/node": "^20.10.4" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export function useDebounce(value: T, delay: number): T { 4 | const [debouncedValue, setDebouncedValue] = useState(value); 5 | 6 | useEffect(() => { 7 | // Set timer to update value after specified delay 8 | const timer = setTimeout(() => { 9 | setDebouncedValue(value); 10 | }, delay); 11 | 12 | // Cleanup function 13 | return () => { 14 | clearTimeout(timer); 15 | }; 16 | }, [value, delay]); 17 | 18 | return debouncedValue; 19 | } 20 | -------------------------------------------------------------------------------- /usecases/webapp-java/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" ] -------------------------------------------------------------------------------- /usecases/webapp-react/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" ] -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/kms/README.md: -------------------------------------------------------------------------------- 1 | # EncryptionKey Construct 2 | 3 | ## Purpose 4 | 5 | Create a AWS KMS key to encrypt Amazon Cloudwatch Logs LogGroup. 6 | 7 | ## Required resources 8 | 9 | None 10 | 11 | ## Required parameters (props) 12 | 13 | None 14 | 15 | ## Optional parameters (props) 16 | 17 | - `servicePrincipals` : To grant access from these principals 18 | 19 | ## Properties 20 | 21 | | Name | Type | Description | 22 | | ------------- | :-----: | --------------------------: | 23 | | encryptionKey | kms.Key | The key to encrypt LogGroup | 24 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/domain/dto/SampleAppDto.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.domain.dto; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import org.springframework.stereotype.Component; 6 | import lombok.Data; 7 | 8 | @Data 9 | @Component 10 | public class SampleAppDto implements Serializable { 11 | private Integer id; 12 | private String name; 13 | private Boolean job0001Flag; 14 | private Boolean job0002Flag; 15 | private Boolean job0003Flag; 16 | private Boolean job0004Flag; 17 | private Boolean job0005Flag; 18 | } 19 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/kms/README.md: -------------------------------------------------------------------------------- 1 | # EncryptionKey Construct 2 | 3 | ## Purpose 4 | 5 | Create a AWS KMS key to encrypt Amazon Cloudwatch Logs LogGroup. 6 | 7 | ## Required resources 8 | 9 | None 10 | 11 | ## Required parameters (props) 12 | 13 | None 14 | 15 | ## Optional parameters (props) 16 | 17 | - `servicePrincipals` : To grant access from these principals 18 | 19 | ## Properties 20 | 21 | | Name | Type | Description | 22 | | ------------- | :-----: | --------------------------: | 23 | | encryptionKey | kms.Key | The key to encrypt LogGroup | 24 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"], 3 | "compilerOptions": { 4 | "types": ["@remix-run/node", "vite/client"], 5 | "module": "ESNext", 6 | "lib": ["DOM", "DOM.Iterable", "ES2022"], 7 | "isolatedModules": true, 8 | "esModuleInterop": true, 9 | "jsx": "react-jsx", 10 | "moduleResolution": "Bundler", 11 | "resolveJsonModule": true, 12 | "target": "ES2022", 13 | "strict": true, 14 | "allowJs": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "skipLibCheck": true, 17 | "noEmit": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/README_ja.md: -------------------------------------------------------------------------------- 1 | # webapp-react 2 | 3 | ## 概要 4 | 5 | 本アプリケーションは、サーバーレスな infra 上で React アプリケーションの動作確認を行うためのサンプルアプリケーションになります。 6 | また、アプリケーション内で DB を操作することができ、バッチ処理で失敗させるレコードを選択することで、バッチ処理の動作確認を行うことができます。 7 | 8 | ## スクリーンショット 9 | 10 | 次の画像のような画面が表示されたら起動しています。 11 | 12 | ![参照画面](./docs/images/screenshot.png) 13 | 14 | ## ローカルでの動作確認 15 | 16 | ### 実行 17 | 18 | 次のコマンドを実行すると、localhost:3000 にアクセスして、アプリケーションの外観が確認できます。(データの操作を確認するには、AWS上にデプロイすることが必要となります。) 19 | 20 | ```sh 21 | npm start 22 | ``` 23 | 24 | ## ビルド 25 | 26 | 次のコマンドを実行すると、`build`にビルドしたファイルが生成されます。 27 | 28 | ```sh 29 | npm run build 30 | ``` 31 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/codepipeline/README.md: -------------------------------------------------------------------------------- 1 | # CodePipelineWebappJava Construct 2 | 3 | ## Purpose 4 | 5 | - Create CodePipeline to deploy to ECS/Fargate 6 | - Include CodeCommit and CodeBuild 7 | 8 | ## Required resources 9 | 10 | - CodeCommit repository 11 | - ECS TaskDefinition 12 | - ECS Service 13 | - ECS Cluster on Fargate. 14 | 15 | ## Required parameters (props) 16 | 17 | - `codeCommitRepository` : CodeCOmmit Repository, Get source code from this repository 18 | - `ecsService` : ECS Service for Fargate, deploy destination 19 | 20 | ## Properties 21 | 22 | None 23 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/utils/session.server.ts: -------------------------------------------------------------------------------- 1 | import { createCookieSessionStorage } from 'react-router' 2 | 3 | if (!process.env.SESSION_SECRET) { 4 | throw new Error('SESSION_SECRET is not set.') 5 | } 6 | 7 | export const sessionStorage = createCookieSessionStorage({ 8 | cookie: { 9 | name: '_auth', 10 | sameSite: 'lax', 11 | path: '/', 12 | httpOnly: true, 13 | secrets: [process.env.SESSION_SECRET], 14 | secure: process.env.NODE_ENV === 'production', 15 | maxAge: 60 * 60, // 1時間(IDトークン・アクセストークンの有効期限に合わせる) 16 | }, 17 | }) 18 | 19 | export const { getSession, commitSession, destroySession } = sessionStorage 20 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /usecases/webapp-java/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 | -------------------------------------------------------------------------------- /usecases/webapp-react/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 | -------------------------------------------------------------------------------- /usecases/webapp-java/test/webapp-java.test.ts: -------------------------------------------------------------------------------- 1 | // import * as cdk from 'aws-cdk-lib'; 2 | // import { Template } from 'aws-cdk-lib/assertions'; 3 | // import * as WebappJava from '../lib/webapp-java-stack'; 4 | 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/webapp-java-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new WebappJava.WebappJavaStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | 14 | // template.hasResourceProperties('AWS::SQS::Queue', { 15 | // VisibilityTimeout: 300 16 | // }); 17 | }); 18 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/controller/form/SampleAppForm.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.controller.form; 2 | 3 | import java.util.Date; 4 | import lombok.Data; 5 | import javax.validation.constraints.NotBlank; 6 | 7 | 8 | @Data 9 | public class SampleAppForm { 10 | @NotBlank 11 | private Integer id; 12 | @NotBlank 13 | private String name; 14 | @NotBlank 15 | private Boolean job0001Flag; 16 | @NotBlank 17 | private Boolean job0002Flag; 18 | @NotBlank 19 | private Boolean job0003Flag; 20 | @NotBlank 21 | private Boolean job0004Flag; 22 | @NotBlank 23 | private Boolean job0005Flag; 24 | } 25 | -------------------------------------------------------------------------------- /usecases/webapp-react/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 | "@types/pg": "^8.15.5" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/Dockerfile: -------------------------------------------------------------------------------- 1 | # For build 2 | FROM amazoncorretto:17-alpine AS build 3 | RUN apk update && apk add curl 4 | 5 | ENV HOME=/home/app 6 | RUN mkdir -p $HOME 7 | WORKDIR $HOME 8 | ADD . $HOME 9 | RUN curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem -o $HOME/root.pem 10 | RUN ./gradlew build 11 | 12 | # For app 13 | FROM amazoncorretto:17-alpine 14 | 15 | VOLUME /home/spring 16 | VOLUME /tmp 17 | 18 | RUN addgroup -S spring && adduser -S spring -G spring 19 | USER spring:spring 20 | 21 | COPY --from=build /home/app/build/libs/webapp-java-0.0.1-SNAPSHOT.jar sampleapp.jar 22 | COPY --from=build /home/app/root.pem /tmp/root.pem 23 | CMD ["java","-jar","/sampleapp.jar"] 24 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { vitePlugin as remix } from "@remix-run/dev"; 2 | import path from "node:path"; 3 | import { defineConfig } from "vite"; 4 | import tsconfigPaths from "vite-tsconfig-paths"; 5 | 6 | declare module "@remix-run/node" { 7 | interface Future { 8 | v3_singleFetch: true; 9 | } 10 | } 11 | 12 | export default defineConfig({ 13 | server: { 14 | port: 3000, 15 | }, 16 | plugins: [ 17 | remix({ 18 | ignoredRouteFiles: ["**/*.css"], 19 | future: { 20 | v3_singleFetch: true, 21 | }, 22 | }), 23 | tsconfigPaths() 24 | ], 25 | resolve: { 26 | alias: { 27 | "~": path.resolve(__dirname, "./app") 28 | } 29 | } 30 | }); -------------------------------------------------------------------------------- /usecases/webapp-react/parameter_template.ts: -------------------------------------------------------------------------------- 1 | interface Parameter { 2 | deployEnv: string; 3 | sharedVpcCidr: string; 4 | appVpcCidr: string; 5 | filePathOfSourceArtifact: string; 6 | windowsBastion: boolean; 7 | linuxBastion: boolean; 8 | domainName: string; 9 | notifyEmail: string; 10 | } 11 | 12 | const devParameter: Parameter = { 13 | deployEnv: "dev", 14 | sharedVpcCidr: '10.0.0.0/16', 15 | appVpcCidr: '10.1.0.0/16', 16 | filePathOfSourceArtifact: 'webapp-repository/assets/webapp.zip', 17 | windowsBastion: true, 18 | linuxBastion: true, 19 | domainName: "templateapp.local", 20 | notifyEmail: "johndoe+notify@example.com" 21 | } 22 | 23 | const parameter = devParameter; 24 | export default parameter; -------------------------------------------------------------------------------- /usecases/webapp-java/parameter_template.ts: -------------------------------------------------------------------------------- 1 | interface Parameter { 2 | deployEnv: string; 3 | sharedVpcCidr: string; 4 | appVpcCidr: string; 5 | filePathOfSourceArtifact: string; 6 | windowsBastion: boolean; 7 | linuxBastion: boolean; 8 | domainName: string; 9 | notifyEmail: string; 10 | } 11 | 12 | const devParameter: Parameter = { 13 | deployEnv: "dev", 14 | sharedVpcCidr: '10.0.0.0/16', 15 | appVpcCidr: '10.1.0.0/16', 16 | filePathOfSourceArtifact: 'webapp-repository/assets/webapp.zip', 17 | windowsBastion: true, 18 | linuxBastion: true, 19 | domainName: "templateapp.local", 20 | notifyEmail: "johndoe+notify@example.com" 21 | } 22 | 23 | const parameter = devParameter; 24 | export default parameter; 25 | -------------------------------------------------------------------------------- /usecases/infraops-console/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infraops-console", 3 | "version": "1.0.0", 4 | "bin": { 5 | "infraops-console": "bin/infraops-console.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^29.5.14", 15 | "@types/node": "22.7.9", 16 | "aws-cdk": "2.1022.0", 17 | "jest": "^29.7.0", 18 | "ts-jest": "^29.2.5", 19 | "typescript": "~5.6.3" 20 | }, 21 | "dependencies": { 22 | "@aws-cdk/aws-apprunner-alpha": "^2.212.0-alpha.0", 23 | "aws-cdk-lib": "^2.212.0", 24 | "cdk-nag": "^2.36.47", 25 | "constructs": "^10.0.0" 26 | }, 27 | "license": "MIT" 28 | } 29 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/RequirementBadge.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps } from 'react'; 2 | 3 | export type RequirementBadgeProps = ComponentProps<'span'> & { 4 | isOptional?: boolean; 5 | }; 6 | 7 | export const RequirementBadge = (props: RequirementBadgeProps) => { 8 | const { children, className, isOptional, ...rest } = props; 9 | 10 | return ( 11 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | export default RequirementBadge; -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | React App 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Label.tsx: -------------------------------------------------------------------------------- 1 | import type { ComponentProps } from 'react'; 2 | 3 | export type LabelSize = 'lg' | 'md' | 'sm'; 4 | 5 | export type LabelProps = ComponentProps<'label'> & { 6 | size?: LabelSize; 7 | }; 8 | 9 | export const Label = (props: LabelProps) => { 10 | const { children, className, size = 'md', ...rest } = props; 11 | 12 | return ( 13 | 24 | ); 25 | }; 26 | 27 | export default Label; -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ec2/README.md: -------------------------------------------------------------------------------- 1 | # Bastion Construct 2 | 3 | ## Purpose 4 | 5 | - Create Windows/Linux bastion instance 6 | 7 | ## Required resources 8 | 9 | - VPC and private isolated subnet 10 | 11 | ## Required parameters (props) 12 | 13 | - `os <"Linux"|"Windows">` : Select whether Linux or Windows 14 | - `vpc ` : Define destionation to create an instance. It required isolated subnet 15 | - `region ` : Region ID 16 | - `auroraSecurityGroupId ` : To create security group for bastion to allow access to RDS from bastion 17 | 18 | ## Optional parameters (props) 19 | 20 | - `instanceType ` : If you want to change instance type, please add it. Default is `t2-small` 21 | 22 | ## Properties 23 | 24 | - None 25 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/ec2/README.md: -------------------------------------------------------------------------------- 1 | # Bastion Construct 2 | 3 | ## Purpose 4 | 5 | - Create Windows/Linux bastion instance 6 | 7 | ## Required resources 8 | 9 | - VPC and private isolated subnet 10 | 11 | ## Required parameters (props) 12 | 13 | - `os <"Linux"|"Windows">` : Select whether Linux or Windows 14 | - `vpc ` : Define destionation to create an instance. It required isolated subnet 15 | - `region ` : Region ID 16 | - `auroraSecurityGroupId ` : To create security group for bastion to allow access to RDS from bastion 17 | 18 | ## Optional parameters (props) 19 | 20 | - `instanceType ` : If you want to change instance type, please add it. Default is `t2-small` 21 | 22 | ## Properties 23 | 24 | - None 25 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/_index.tsx: -------------------------------------------------------------------------------- 1 | import { redirect } from "@remix-run/node"; 2 | import type { LoaderFunctionArgs } from "@remix-run/node"; 3 | import { getSession } from "../utils/session.server"; 4 | 5 | export async function loader({ request }: LoaderFunctionArgs) { 6 | // Check the state of authentication 7 | const session = await getSession(request.headers.get('Cookie')); 8 | const user = session.get('user'); 9 | 10 | // User is authenticated, redirect to dashboard 11 | if (user) { 12 | return redirect("/dashboard"); 13 | } 14 | 15 | // User is not authenticated, redirect to login 16 | return redirect("/login"); 17 | } 18 | 19 | // This component is not actually rendered (because of the redirect) 20 | export default function Index() { 21 | return null; 22 | } 23 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/kms/key.ts: -------------------------------------------------------------------------------- 1 | import * as aws_kms from 'aws-cdk-lib/aws-kms'; 2 | import { ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 3 | import { Construct } from 'constructs'; 4 | 5 | export class EncryptionKey extends Construct { 6 | public readonly encryptionKey: aws_kms.Key; 7 | constructor( 8 | scope: Construct, 9 | id: string, 10 | props?: { 11 | servicePrincipals?: ServicePrincipal[]; 12 | } 13 | ) { 14 | super(scope, id); 15 | 16 | this.encryptionKey = new aws_kms.Key(this, id, { 17 | enableKeyRotation: true, 18 | }); 19 | 20 | if (props && props.servicePrincipals) { 21 | props.servicePrincipals.map((servicePrincipal) => { 22 | this.encryptionKey.grantEncryptDecrypt(servicePrincipal); 23 | }); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/network/README.md: -------------------------------------------------------------------------------- 1 | # Network Construct 2 | 3 | ## Purpose 4 | 5 | Creates a VPC enviroment with network logs enabled to Cloudwatch. 6 | 7 | ## Required resources 8 | 9 | None 10 | 11 | ## Required parameters (props) 12 | 13 | - `maxAzs` : Max availability zones 14 | - `cidr` : The CIDR address of the network 15 | - `cidrMask` : The mask for the of available IP addresses in each subnet 16 | 17 | ## Optional parameters (props) 18 | 19 | - `publicSubnet` : Create or not a public subnet 20 | - `isolatedSubnet` : Create or not a isolated subnet 21 | - `natSubnet` : Create or not a nat/private subnet 22 | 23 | ## Properties 24 | 25 | | Name | Type | Description | 26 | | ---- | :------: | ----------: | 27 | | vpc | ec2.IVpc | Exposed VPC | 28 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/kms/key.ts: -------------------------------------------------------------------------------- 1 | import * as aws_kms from 'aws-cdk-lib/aws-kms'; 2 | import { ServicePrincipal } from 'aws-cdk-lib/aws-iam'; 3 | import { Construct } from 'constructs'; 4 | 5 | export class EncryptionKey extends Construct { 6 | public readonly encryptionKey: aws_kms.Key; 7 | constructor( 8 | scope: Construct, 9 | id: string, 10 | props?: { 11 | servicePrincipals?: ServicePrincipal[]; 12 | } 13 | ) { 14 | super(scope, id); 15 | 16 | this.encryptionKey = new aws_kms.Key(this, id, { 17 | enableKeyRotation: true, 18 | }); 19 | 20 | if (props && props.servicePrincipals) { 21 | props.servicePrincipals.map((servicePrincipal) => { 22 | this.encryptionKey.grantEncryptDecrypt(servicePrincipal); 23 | }); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/network/README.md: -------------------------------------------------------------------------------- 1 | # Network Construct 2 | 3 | ## Purpose 4 | 5 | Creates a VPC enviroment with network logs enabled to Cloudwatch. 6 | 7 | ## Required resources 8 | 9 | None 10 | 11 | ## Required parameters (props) 12 | 13 | - `maxAzs` : Max availability zones 14 | - `cidr` : The CIDR address of the network 15 | - `cidrMask` : The mask for the of available IP addresses in each subnet 16 | 17 | ## Optional parameters (props) 18 | 19 | - `publicSubnet` : Create or not a public subnet 20 | - `isolatedSubnet` : Create or not a isolated subnet 21 | - `natSubnet` : Create or not a nat/private subnet 22 | 23 | ## Properties 24 | 25 | | Name | Type | Description | 26 | | ---- | :------: | ----------: | 27 | | vpc | ec2.IVpc | Exposed VPC | 28 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Slot.tsx: -------------------------------------------------------------------------------- 1 | import { Children, cloneElement, type HTMLAttributes, isValidElement, type ReactNode } from 'react'; 2 | 3 | type SlotProps = HTMLAttributes & { 4 | children?: ReactNode; 5 | }; 6 | 7 | export const Slot = (props: SlotProps) => { 8 | const { children, ...rest } = props; 9 | 10 | // https://react.dev/reference/react/isValidElement 11 | // https://react.dev/reference/react/cloneElement 12 | if (isValidElement(children)) { 13 | return cloneElement(children, { 14 | ...rest, 15 | ...children.props, 16 | className: `${rest.className ?? ''} ${children.props.className ?? ''}`, 17 | }); 18 | } 19 | 20 | if (Children.count(children) > 1) { 21 | Children.only(null); 22 | } 23 | 24 | return null; 25 | }; 26 | 27 | export default Slot; -------------------------------------------------------------------------------- /usecases/webapp-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-for-closed-network-system-workloads-on-aws", 3 | "version": "2.0.0", 4 | "bin": { 5 | "webapp-react": "bin/webapp-react.js" 6 | }, 7 | "scripts": { 8 | "create:certificate": "./scripts/create_certificate.sh", 9 | "build": "tsc", 10 | "watch": "tsc -w", 11 | "test": "jest", 12 | "cdk": "cdk" 13 | }, 14 | "devDependencies": { 15 | "@types/jest": "^29.5.14", 16 | "@types/node": "22.7.9", 17 | "aws-cdk": "2.1029.4", 18 | "jest": "^29.7.0", 19 | "ts-jest": "^29.2.5", 20 | "ts-node": "^10.9.2", 21 | "typescript": "~5.6.3" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.206.0", 25 | "cdk-ecr-deployment": "^4.0.3", 26 | "cdk-nag": "^2.36.47", 27 | "constructs": "^10.0.0" 28 | }, 29 | "license": "MIT" 30 | } 31 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import Dashboard from './components/Dashboard'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); 8 | 9 | class App extends Component { 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | ); 16 | } 17 | } 18 | // / 19 | // /#/sampleapp/form 20 | 21 | root.render(); 22 | 23 | // If you want to start measuring performance in your app, pass a function 24 | // to log results (for example: reportWebVitals(console.log)) 25 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 26 | reportWebVitals(); 27 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/aurora/README_dbinitlambda.md: -------------------------------------------------------------------------------- 1 | # DBinitLambda Construct 2 | 3 | ## Purpose 4 | 5 | - Lambda for initialize DB 6 | 7 | ## Required resources 8 | 9 | - RDS 10 | - RDS proxy 11 | 12 | ## Required parameters (props) 13 | 14 | - `vpc ` : Define the vpc including RDS 15 | - `sgForLambda ` : Security Group for Lambda 16 | - `auroraSecretName ` : Secret Name including RDS information 17 | - `auroraSecretArn ` : Secret Arn including RDS information 18 | - `auroraSecretEncryptionKeyArn ` : KMS Key Arn which encrypt secret including RDS information 19 | - `rdsProxyEndpoint ` : Endpoint url of RDS proxy endpoint 20 | - `rdsProxyArn ` : ARN of RDS proxy endpoint 21 | 22 | ## Optional parameters (props) 23 | 24 | None 25 | 26 | ## Properties 27 | 28 | None 29 | -------------------------------------------------------------------------------- /usecases/webapp-java/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-for-closed-network-system-workloads-on-aws", 3 | "version": "2.0.0", 4 | "bin": { 5 | "webapp-java": "bin/webapp-java.js" 6 | }, 7 | "scripts": { 8 | "create:certificate": "./scripts/create_certificate.sh", 9 | "build": "tsc", 10 | "watch": "tsc -w", 11 | "test": "jest", 12 | "cdk": "cdk" 13 | }, 14 | "devDependencies": { 15 | "@types/jest": "^29.5.14", 16 | "@types/node": "22.7.9", 17 | "aws-cdk": "2.1022.0", 18 | "jest": "^29.7.0", 19 | "ts-jest": "^29.2.5", 20 | "typescript": "~5.6.3" 21 | }, 22 | "dependencies": { 23 | "@aws-cdk/aws-apprunner-alpha": "^2.212.0-alpha.0", 24 | "aws-cdk-lib": "^2.212.0", 25 | "cdk-ecr-deployment": "^4.0.3", 26 | "cdk-nag": "^2.36.47", 27 | "constructs": "^10.0.0" 28 | }, 29 | "license": "MIT" 30 | } 31 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/README.md: -------------------------------------------------------------------------------- 1 | # webapp-react 2 | 3 | ## Overview 4 | 5 | This application is a sample application for checking the operation of React applications on serverless infra. 6 | Also, you can operate the DB within the application, and you can check the operation of batch processing by selecting records that will fail in batch processing. 7 | 8 | ## screenshot 9 | 10 | The following image will be displayed. 11 | 12 | ![Screenshot](./docs/images/screenshot.png) 13 | 14 | ## Run on local 15 | 16 | ### Execute 17 | 18 | You can check what the application looks like by running the following command and going to localhost:3000. (To check data operation, deployment on AWS is required.) 19 | 20 | ```sh 21 | npm start 22 | ``` 23 | 24 | ## build 25 | 26 | When the following command is executed, a file generated into `build` folder. 27 | 28 | ```sh 29 | npm run build 30 | ``` 31 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 node:22-alpine AS build 2 | 3 | # ビルド 4 | WORKDIR /app 5 | 6 | ENV TINI_VERSION v0.19.0 7 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini 8 | RUN chmod +x /tini 9 | 10 | COPY . . 11 | RUN npm install -g npm@latest 12 | RUN npm ci 13 | RUN npm run build 14 | 15 | # 本番環境 16 | FROM --platform=linux/amd64 gcr.io/distroless/nodejs22-debian12 17 | WORKDIR /app 18 | ENV NODE_ENV=production 19 | 20 | # 必要なファイルのみをコピー 21 | COPY --from=build --chown=nonroot:nonroot /tini /tini 22 | COPY --from=build /app/build ./build 23 | COPY --from=build /app/package.json ./package.json 24 | COPY --from=build /app/node_modules ./node_modules 25 | 26 | # アプリケーションの起動 27 | USER nonroot 28 | EXPOSE 3000 29 | ENTRYPOINT [ "/tini", "--", "/nodejs/bin/node" ] 30 | CMD ["./node_modules/.bin/remix-serve", "/app/build/server/index.js"] -------------------------------------------------------------------------------- /usecases/webapp-java/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "lib": [ 7 | "es2022" 8 | ], 9 | "declaration": true, 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noImplicitThis": true, 14 | "alwaysStrict": true, 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": false, 19 | "inlineSourceMap": true, 20 | "inlineSources": true, 21 | "experimentalDecorators": true, 22 | "strictPropertyInitialization": false, 23 | "resolveJsonModule": true, 24 | "esModuleInterop": true, 25 | "typeRoots": [ 26 | "./node_modules/@types" 27 | ] 28 | }, 29 | "exclude": [ 30 | "node_modules", 31 | "cdk.out" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /usecases/webapp-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "lib": [ 7 | "es2022" 8 | ], 9 | "declaration": true, 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noImplicitThis": true, 14 | "alwaysStrict": true, 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": false, 19 | "inlineSourceMap": true, 20 | "inlineSources": true, 21 | "experimentalDecorators": true, 22 | "strictPropertyInitialization": false, 23 | "resolveJsonModule": true, 24 | "esModuleInterop": true, 25 | "typeRoots": [ 26 | "./node_modules/@types" 27 | ] 28 | }, 29 | "exclude": [ 30 | "node_modules", 31 | "cdk.out" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /usecases/webapp-java/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 | -------------------------------------------------------------------------------- /usecases/webapp-react/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 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | /* Additional options */ 24 | "allowJs": true, 25 | "esModuleInterop": true, 26 | "allowSyntheticDefaultImports": true, 27 | "forceConsistentCasingInFileNames": true, 28 | 29 | }, 30 | "include": ["src"], 31 | "references": [{ "path": "./tsconfig.node.json" }] 32 | } 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button } from './Button'; 2 | export { default as DatabaseList } from './DatabaseList'; 3 | export { default as ErrorAlert } from './ErrorAlert'; 4 | export { default as Input } from './Input'; 5 | export { default as Label } from './Label'; 6 | export { default as Modal } from './Modal'; 7 | export { default as RefreshButton } from './RefreshButton'; 8 | export { default as RequirementBadge } from './RequirementBadge'; 9 | export { default as Select } from './Select'; 10 | export { default as ServiceList } from './ServiceList'; 11 | export { default as Slot } from './Slot'; 12 | export { default as StatusBadge } from './StatusBadge'; 13 | export { default as UniversalLink } from './UniversalLink'; 14 | export { default as ScheduleForm } from './ScheduleForm'; 15 | export { 16 | default as Table, 17 | TableHead, 18 | TableBody, 19 | TableRow, 20 | TableHeaderCell, 21 | TableCell 22 | } from './Table'; -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/buildspec.yaml: -------------------------------------------------------------------------------- 1 | version: 0.2 2 | 3 | phases: 4 | install: 5 | runtime-versions: 6 | java: corretto17 7 | pre_build: 8 | commands: 9 | - AWS_ACCOUNT_ID=$(echo ${CODEBUILD_BUILD_ARN} | cut -f 5 -d :) 10 | - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com 11 | - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) 12 | - IMAGE_TAG=${COMMIT_HASH:=latest} 13 | build: 14 | commands: 15 | - docker build . -t $REPOSITORY_URI:latest 16 | - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG 17 | post_build: 18 | commands: 19 | - docker push $REPOSITORY_URI:latest 20 | - docker push $REPOSITORY_URI:$IMAGE_TAG 21 | - printf '[{"name":"%s","imageUri":"%s"}]' $ECS_APP_CONTAINER $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json 22 | 23 | artifacts: 24 | files: imagedefinitions.json 25 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/RefreshButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button } from '../components'; 3 | 4 | interface RefreshButtonProps { 5 | onRefresh: () => void; 6 | isSubmitting?: boolean; 7 | className?: string; 8 | } 9 | 10 | export const RefreshButton: React.FC = ({ 11 | onRefresh, 12 | isSubmitting = false, 13 | className = '' 14 | }) => { 15 | const [isRefreshing, setIsRefreshing] = useState(false); 16 | 17 | const handleRefresh = () => { 18 | setIsRefreshing(true); 19 | onRefresh(); 20 | 21 | setTimeout(() => { 22 | setIsRefreshing(false); 23 | }, 5000); 24 | }; 25 | 26 | return ( 27 | 37 | ); 38 | }; 39 | 40 | export default RefreshButton; 41 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug Backend", 6 | "type": "node-terminal", 7 | "request": "launch", 8 | "command": "npm run dev", 9 | "cwd": "${workspaceFolder}" 10 | }, 11 | { 12 | "name": "Debug Frontend", 13 | "type": "chrome", 14 | "request": "launch", 15 | "url": "http://localhost:5173/", 16 | "webRoot": "${workspaceFolder}/app", 17 | "sourceMaps": true 18 | }, 19 | { 20 | "name": "Debug Backend and Frontend", 21 | "type": "node-terminal", 22 | "request": "launch", 23 | "command": "npm run dev", 24 | "cwd": "${workspaceFolder}", 25 | "serverReadyAction": { 26 | "action": "startDebugging", 27 | "pattern": "Local: +https?://.+", 28 | "name": "Debug Frontend" 29 | } 30 | }, 31 | ] 32 | } -------------------------------------------------------------------------------- /usecases/webapp-react/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 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/serverless/README_lambda.md: -------------------------------------------------------------------------------- 1 | # DefaultLambda Construct 2 | 3 | ## Purpose 4 | 5 | Creates Lambda connecting RDS 6 | 7 | ## Required resources 8 | 9 | - VPC including RDS 10 | 11 | ## Required parameters (props) 12 | 13 | - `resourceId ` : Some unique name for Lambda function 14 | - `entry ` : path for function code 15 | - `secretName ` : Secret Name including RDS information 16 | - `secretArn ` : Secret Arn including RDS information 17 | - `secretEncryptionKeyArn ` : KMS Key Arn which encrypt secret including RDS information 18 | - `proxyEndpoint ` : Endpoint url of RDS proxy endpoint 19 | - `proxyArn ` : ARN of RDS proxy endpoint 20 | - `sgForLambda ` : Security Group for Lambda 21 | 22 | ## Optional parameters (props) 23 | 24 | None 25 | 26 | ## Properties 27 | 28 | | Name | Type | Description | 29 | | ------ | :------------------------------: | --------------: | 30 | | lambda | aws_lambda_nodejs.NodejsFunction | lambda Function | 31 | -------------------------------------------------------------------------------- /usecases/infraops-console/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2020", 7 | "dom" 8 | ], 9 | "declaration": true, 10 | "strict": true, 11 | "noImplicitAny": true, 12 | "strictNullChecks": true, 13 | "noImplicitThis": true, 14 | "alwaysStrict": true, 15 | "noUnusedLocals": false, 16 | "noUnusedParameters": false, 17 | "noImplicitReturns": true, 18 | "noFallthroughCasesInSwitch": false, 19 | "inlineSourceMap": true, 20 | "inlineSources": true, 21 | "experimentalDecorators": true, 22 | "strictPropertyInitialization": false, 23 | "moduleResolution": "bundler", 24 | "esModuleInterop": true, 25 | "allowSyntheticDefaultImports": true, 26 | "resolveJsonModule": true, 27 | "skipLibCheck": true, 28 | "forceConsistentCasingInFileNames": true, 29 | "typeRoots": [ 30 | "./node_modules/@types" 31 | ], 32 | "outDir": "dist" 33 | }, 34 | "exclude": [ 35 | "node_modules", 36 | "cdk.out", 37 | "dist" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/api.instance-types.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * API Route for EC2 Instance Type Information 3 | * 4 | * This file serves as a Remix API route that enables frontend components 5 | * to call backend functionality without page navigation. 6 | * 7 | * Provides endpoints for retrieving available EC2 instance types 8 | * and instance families for the current AWS region. 9 | * 10 | * - loader: Handles GET requests (instance type data fetching) 11 | * 12 | * Business logic is delegated to functions in the models/ec2.server.ts file. 13 | * This file only handles authentication checks and parameter passing. 14 | * 15 | * Frontend usage examples: 16 | * - useFetcher().load("/api/instance-types") 17 | */ 18 | 19 | import type { LoaderFunctionArgs } from '@remix-run/node'; 20 | import { getInstanceTypes } from '../models/ec2.server'; 21 | import { requireAuthentication } from '../utils/auth.server'; 22 | 23 | export async function loader({ request }: LoaderFunctionArgs) { 24 | // Authentication check 25 | await requireAuthentication(request); 26 | 27 | // Delegate business logic to models layer 28 | return await getInstanceTypes(request); 29 | } 30 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/logout.tsx: -------------------------------------------------------------------------------- 1 | import type { ActionFunctionArgs } from '@remix-run/node'; 2 | import { redirect } from '@remix-run/node'; 3 | import { sessionStorage } from '../utils/session.server'; 4 | 5 | export async function action({ request }: ActionFunctionArgs) { 6 | // Get the session 7 | const session = await sessionStorage.getSession(request.headers.get('Cookie')); 8 | 9 | try { 10 | // Destroy the session 11 | const cookie = await sessionStorage.destroySession(session); 12 | 13 | // Redirect to login page 14 | return redirect('/login', { 15 | headers: { 16 | 'Set-Cookie': cookie, 17 | }, 18 | }); 19 | } catch (error) { 20 | console.error('Error during logout:', error); 21 | 22 | // Redirect to login page even if error occurs 23 | return redirect('/login', { 24 | headers: { 25 | 'Set-Cookie': await sessionStorage.destroySession(session), 26 | }, 27 | }); 28 | } 29 | } 30 | 31 | export async function loader() { 32 | // Redirect to login page on GET request 33 | return redirect('/login'); 34 | } 35 | 36 | export default function Logout() { 37 | return null; 38 | } 39 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/repository/model/SampleApp.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.repository.model; 2 | 3 | import java.util.Date; 4 | import javax.persistence.Column; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.relational.core.mapping.Table; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | import lombok.RequiredArgsConstructor; 11 | import lombok.ToString; 12 | 13 | @RequiredArgsConstructor 14 | @Getter 15 | @Setter 16 | @EqualsAndHashCode(of = {"id"}) 17 | @ToString 18 | @Table(name = "sampleapp_table", schema = "public") 19 | public class SampleApp { 20 | @Id 21 | @Column(name = "id") 22 | private Integer id; 23 | @Column(name = "name") 24 | private String name; 25 | @Column(name = "job0001_flag") 26 | private Boolean job0001Flag; 27 | @Column(name = "job0002_flag") 28 | private Boolean job0002Flag; 29 | @Column(name = "job0003_flag") 30 | private Boolean job0003Flag; 31 | @Column(name = "job0004_flag") 32 | private Boolean job0004Flag; 33 | @Column(name = "job0005_flag") 34 | private Boolean job0005Flag; 35 | } 36 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ecs/README_SERVICE.md: -------------------------------------------------------------------------------- 1 | # EcsAppService Construct 2 | 3 | ## Purpose 4 | 5 | Create Service, TaskDefinition, and Container for ECS on Fargate. 6 | And register the service to TargetGroup of ALB to connect from it. 7 | 8 | ## Required resources 9 | 10 | - VPC that includes private isolated subnet 11 | - VPC Endpoint for ECR, CloudWatch Logs, and SecretsManager 12 | - RDS(Aurora or others) 13 | - ECR Repository 14 | 15 | ## Required parameters (props) 16 | 17 | - `auroraSecretName`: Aurora's secrets name(key) 18 | - `auroraSecurityGroupId`: To define TaskDefinition's SecurityGroup 19 | - `auroraSecretEncryptionKeyArn`: Encryption key ARN of aurora secret 20 | - `cluster`: Service and Tasks works on this cluster 21 | - `repository`: Container Registory to get container image 22 | - `targetGroup`: Destination to register ECS services 23 | 24 | ## Properties 25 | 26 | | Name | Type | Description | 27 | | ------------ | :---------------------: | ----------: | 28 | | ecsService | ecs.FargateService | | 29 | | ecsContainer | ecs.ContainerDefinition | | 30 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/domain-stack.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, Stack, aws_ec2, aws_route53_targets, aws_route53 } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import { Network } from './construct/network/network'; 4 | 5 | interface RecordItem { 6 | name: string; 7 | target: aws_route53.RecordTarget; 8 | } 9 | 10 | interface DomainStackProps extends StackProps { 11 | sharedVpc: aws_ec2.Vpc; 12 | tgw: aws_ec2.CfnTransitGateway; 13 | domainName: string; 14 | recordItems: RecordItem[]; 15 | resolverInboundEndpointIps: string[]; 16 | } 17 | 18 | export class DomainStack extends Stack { 19 | constructor(scope: Construct, id: string, props: DomainStackProps) { 20 | super(scope, id, props); 21 | 22 | 23 | // Create Private Hosted Zone 24 | const privateHostedZone = new aws_route53.PrivateHostedZone(this, 'PrivateHostedZone', { 25 | zoneName: props.domainName, 26 | vpc: props.sharedVpc, 27 | }); 28 | 29 | // Add A records 30 | props.recordItems.map(recordItem => { 31 | new aws_route53.ARecord(this, 'AlbARecord', { 32 | recordName: recordItem.name, 33 | target: recordItem.target, 34 | zone: privateHostedZone, 35 | }); 36 | }) 37 | } 38 | } -------------------------------------------------------------------------------- /usecases/webapp-react/lib/domain-stack.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, Stack, aws_ec2, aws_route53_targets, aws_route53 } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import { Network } from './construct/network/network'; 4 | 5 | interface RecordItem { 6 | name: string; 7 | target: aws_route53.RecordTarget; 8 | } 9 | 10 | interface DomainStackProps extends StackProps { 11 | sharedVpc: aws_ec2.Vpc; 12 | tgw: aws_ec2.CfnTransitGateway; 13 | domainName: string; 14 | recordItems: RecordItem[]; 15 | resolverInboundEndpointIps: string[]; 16 | } 17 | 18 | export class DomainStack extends Stack { 19 | constructor(scope: Construct, id: string, props: DomainStackProps) { 20 | super(scope, id, props); 21 | 22 | 23 | // Create Private Hosted Zone 24 | const privateHostedZone = new aws_route53.PrivateHostedZone(this, 'PrivateHostedZone', { 25 | zoneName: props.domainName, 26 | vpc: props.sharedVpc, 27 | }); 28 | 29 | // Add A records 30 | props.recordItems.map(recordItem => { 31 | new aws_route53.ARecord(this, 'AlbARecord', { 32 | recordName: recordItem.name, 33 | target: recordItem.target, 34 | zone: privateHostedZone, 35 | }); 36 | }) 37 | } 38 | } -------------------------------------------------------------------------------- /usecases/webapp-java/lib/cicd-stack.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, Stack, aws_ecr, aws_ecs } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | 4 | // Constructs 5 | import { Ecr } from './construct/ecr/ecr'; 6 | import { CodePipelineWebappJava } from './construct/codepipeline/codepipeline-webapp-java'; 7 | import path from 'path'; 8 | 9 | interface CicdStackProps extends StackProps { 10 | ecsService: aws_ecs.FargateService; 11 | containerName: string; 12 | filePathOfSourceArtifact: string; 13 | } 14 | 15 | export class CicdStack extends Stack { 16 | public readonly batchRepository: aws_ecr.Repository; 17 | constructor(scope: Construct, id: string, props: CicdStackProps) { 18 | super(scope, id, props); 19 | 20 | // Create ECR 21 | const webappContainerRepository = new Ecr(this, 'Webapp').containerRepository; 22 | this.batchRepository = new Ecr(this, 'Batch', path.join(__dirname, '../batch')).containerRepository; 23 | 24 | // Create Deploy Pipeline 25 | new CodePipelineWebappJava(this, `CodePipeline`, { 26 | bucketKey: props.filePathOfSourceArtifact, 27 | ecrRepository: webappContainerRepository, 28 | ecsService: props.ecsService, 29 | containerName: props.containerName, 30 | }); 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/README_ja.md: -------------------------------------------------------------------------------- 1 | # webapp-java 2 | 3 | [View this page in English](./README.md) 4 | 5 | ## 概要 6 | 7 | 本アプリケーションは、infra 上で Java アプリケーションの動作確認を行うためのサンプルアプリケーションになります。 8 | また、アプリケーション内で DB を操作することができ、バッチ処理で失敗させるレコードを選択することで、バッチ処理の動作確認を行うことができます。 9 | 10 | ## ローカルでの動作確認 11 | 12 | ### ローカルに DB を構築する 13 | 14 | 実行させたい環境に、PostgreSQL をインストール、もしくは Docker にて起動させてください。 15 | その際に、DB にアクセスするユーザ名とパスワードは控えておいてください。 16 | 17 | ### 環境変数の設定 18 | 19 | 3 つの環境変数を定義してください。DB の構築時に控えておいた、ユーザ名とパスワードを設定してください。 20 | 21 | ```sh 22 | $ export $DB_ENDPOINT=localhost 23 | $ export $DB_USERNAME={構築時に設定したユーザ名} 24 | $ export $DB_PASSWORD={構築時に設定したパスワード} 25 | ``` 26 | 27 | ### テーブルの初期化とサンプルデータの追加 28 | 29 | `src/resources/application.properties` に設定されている、`spring.sql.init.mode=always`によって、起動時に必ず`src/main/resources/data.sql`と`src/main/resources/schema.sql`が呼び出され、DB が初期化されます。 30 | 本アプリケーションはサンプルのため、このような設定をしていますが、本番利用の際には DB の初期化やマイグレーションの実施方法について別途ご検討ください。 31 | 32 | ### 実行 33 | 34 | 次のコマンドを実行すると、localhost:8080 にアクセスして、アプリケーションの動作が確認できます。 35 | 36 | ```sh 37 | ./gradlew bootRun 38 | ``` 39 | 40 | ### スクリーンショット 41 | 42 | 次の画像のような画面が表示されたら起動しています。 43 | 44 | ![参照画面](./docs/images/screenshot.png) 45 | 46 | ## ビルド 47 | 48 | 次のコマンドを実行すると、`build/libs`に jar ファイルが生成されます。 49 | 50 | ```sh 51 | ./gradlew build 52 | ``` 53 | -------------------------------------------------------------------------------- /usecases/infraops-console/bin/infraops-console.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | 3 | import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag'; 4 | import { Aspects } from 'aws-cdk-lib'; 5 | import { InfraopsConsoleStack } from '../lib/infraops-console-stack'; 6 | import parameter from '../parameter'; 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 | 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 { deployEnv, sourceVpcId, appRunnerVpcEndpointId } = parameter; 19 | 20 | // インフラオペレーションコンソールスタックの作成 21 | const infraopsConsoleStack = new InfraopsConsoleStack(app, `${deployEnv}InfraopsConsole`, { 22 | env, 23 | description: 'InfraopsConsoleStack will provision DynamoDB table and AppRunner service for infraops console (uksb-1tupboc54) (tag:infraops-console).', 24 | sourceVpcId, 25 | appRunnerVpcEndpointId 26 | }); 27 | 28 | /** 29 | * CDK NAG Suppressions 30 | */ 31 | NagSuppressions.addStackSuppressions(infraopsConsoleStack, [ 32 | { 33 | id: 'AwsSolutions-IAM5', 34 | reason: 'To use ManagedPolicy', 35 | }, 36 | ]); 37 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webapp-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@mui/material": "^5.14.6", 7 | "@testing-library/jest-dom": "^5.17.0", 8 | "@testing-library/react": "^13.4.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "@types/jest": "^27.5.2", 11 | "@types/node": "^20.0.0", 12 | "@types/react": "^18.2.21", 13 | "@types/react-dom": "^18.2.7", 14 | "axios": "^1.5.0", 15 | "react": "^18.2.0", 16 | "react-dom": "^18.2.0", 17 | "react-router-dom": "^6.15.0", 18 | "typescript": "^5.1.6", 19 | "web-vitals": "^2.1.4" 20 | }, 21 | "scripts": { 22 | "dev": "vite", 23 | "build": "tsc && vite build", 24 | "preview": "vite preview", 25 | "test": "echo \"No tests specified\"", 26 | "lint": "npx eslint '**/*.ts' '**/*.tsx' ./" 27 | }, 28 | "devDependencies": { 29 | "@types/react-router-hash-link": "^1.0.0", 30 | "@typescript-eslint/eslint-plugin": "^7.13.0", 31 | "@typescript-eslint/parser": "^7.13.0", 32 | "@vitejs/plugin-react": "^4.3.1", 33 | "eslint": "^8.57.0", 34 | "eslint-config-prettier": "^9.1.0", 35 | "eslint-plugin-react": "^7.34.2", 36 | "prettier": "^3.3.2", 37 | "vite": "^6.3.5", 38 | "vite-tsconfig-paths": "^5.1.4" 39 | }, 40 | "engines": { 41 | "node": ">=22.0.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/components/RecordForm.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Record } from '../types/record'; 3 | import { RecordFormRow } from './RecordFormRow'; 4 | //mui 5 | import Table from '@mui/material/Table'; 6 | import TableBody from '@mui/material/TableBody'; 7 | import TableCell from '@mui/material/TableCell'; 8 | import TableHead from '@mui/material/TableHead'; 9 | import TableRow from '@mui/material/TableRow'; 10 | 11 | export const RecordForm: React.FC<{ 12 | record: Record; 13 | setFlagHandler: (id: number, jobFlagKey: string, newFlagValue: boolean) => void; 14 | }> = ({ record, setFlagHandler }) => { 15 | return ( 16 | 17 | 18 | 19 | ID 20 | 名前 21 | ジョブの成否 22 | 23 | 24 | JOB0001 25 | JOB0002 26 | JOB0003 27 | JOB0004 28 | JOB0005 29 | 30 | 31 | 32 | 33 | 34 |
35 | ); 36 | }; 37 | 38 | export default RecordForm; 39 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/api.services.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API Route for ECS Service Management 3 | * 4 | * This file serves as a Remix API route that enables frontend components 5 | * to call backend functionality without page navigation. 6 | * 7 | * Provides endpoints for managing ECS service operations 8 | * such as updating desired task counts without page transitions. 9 | * 10 | * - action: Handles POST/PUT/DELETE requests (service manipulation) 11 | * 12 | * Business logic is delegated to functions in the models/ecs.server.ts file. 13 | * This file only handles authentication checks and parameter passing. 14 | * 15 | * Frontend usage examples: 16 | * - useFetcher().submit(formData, { method: "post", action: "/api/services" }) 17 | */ 18 | 19 | import { ActionFunctionArgs } from '@remix-run/node'; 20 | import { handleServiceAction } from '../models/ecs.server'; 21 | import { requireAuthentication } from '../utils/auth.server'; 22 | 23 | export async function action({ request }: ActionFunctionArgs) { 24 | // Authentication check 25 | await requireAuthentication(request); 26 | 27 | // Extract form data and convert to parameters object 28 | const formData = await request.formData(); 29 | const params = Object.fromEntries(formData); 30 | const action = params.action as string; 31 | 32 | // Delegate business logic to models layer 33 | return await handleServiceAction(action, params, request); 34 | } 35 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ecs/README_BASE.md: -------------------------------------------------------------------------------- 1 | # EcsAppBase Construct 2 | 3 | ## Purpose 4 | 5 | Create ECS Cluster on Fargate and ALB to access ECS Services on its cluster. 6 | (Optional) Create Private Link to access to ALB. 7 | 8 | ## Required resources 9 | 10 | - VPC that includes private isolated subnet 11 | 12 | ## Required parameters (props) 13 | 14 | - `enabledPrivateLink`: Whether using private link or not 15 | - `vpc`: Define the vpc including isolated subnets 16 | 17 | ## Optional parameters (props) 18 | 19 | - `privateLinkVpc`: It's required when `enabledPrivateLink` is true. 20 | - `testVpcCidr`: CIDR of VPC that Test stack's instance(Windows Server) works on 21 | 22 | ## Properties 23 | 24 | | Name | Type | Description | 25 | | ----------- | :--------------------------------------------: | --------------------------------------------: | 26 | | cluster | ecs.Cluster | Created ECS CLuster on Fargate | 27 | | targetGroup | elaticloadbalancingv2.ApplicationTargetGroup | It includes services and tasks on ECS/Fargate | 28 | | alb | elasticloadbalancingv2.ApplicationLoadBalancer | front of ECS Services/Tasks | 29 | | nlb | elasticloadbalancingv2.NetworkLoadBalancer | Front of alb to connect via PrivateLink | 30 | -------------------------------------------------------------------------------- /usecases/webapp-react/functions/lib/connect.ts: -------------------------------------------------------------------------------- 1 | const referSecrets = async (): Promise<{ 2 | "engine": string; 3 | "host": string; 4 | "username": string; 5 | "password": string; 6 | "dbname": string; 7 | "port": number; 8 | "masterarn": string; 9 | "dbInstanceIdentifier": string; 10 | "dbClusterIdentifier": string; 11 | }> => { 12 | const { SecretsManagerClient, GetSecretValueCommand } = await import( 13 | "@aws-sdk/client-secrets-manager" 14 | ); 15 | const secretsManager = new SecretsManagerClient({ 16 | region: process.env.AWS_REGION!, 17 | }); 18 | const response = await secretsManager.send( 19 | new GetSecretValueCommand({ 20 | SecretId: process.env.SECRET_NAME!, 21 | }) 22 | ); 23 | return JSON.parse(response.SecretString!); 24 | }; 25 | 26 | export default async function Connection() { 27 | const { Client } = await import("pg"); 28 | const secrets = await referSecrets(); 29 | const {host, username, port} = secrets; 30 | const { Signer } = await import("@aws-sdk/rds-signer"); 31 | const signer = new Signer({ 32 | region: process.env.AWS_REGION!, 33 | username: username, 34 | hostname: process.env.PROXY_ENDPOINT!, 35 | port: port, 36 | }); 37 | const token = await signer.getAuthToken(); 38 | // client settings 39 | const client = new Client({ 40 | host: process.env.PROXY_ENDPOINT!, 41 | user: username, 42 | password: token, //secrets.password, 43 | ssl: true, 44 | }); 45 | return client; 46 | } 47 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/utils/abac-filter.server.ts: -------------------------------------------------------------------------------- 1 | import { Service } from '@aws-sdk/client-ecs'; 2 | import { getVerifiedUserInfo } from './jwt-verify.server'; 3 | import { Instance } from '@aws-sdk/client-ec2'; 4 | import { DBCluster } from '@aws-sdk/client-rds'; 5 | 6 | interface UnifiedTag { 7 | key: string; 8 | value: string; 9 | } 10 | 11 | type TagExtractor = (resource: T) => UnifiedTag[]; 12 | 13 | export async function filterByGroupId( 14 | resources: T[], 15 | tagExtractor: TagExtractor, 16 | request: Request 17 | ): Promise { 18 | const {groupId, isAdmin} = await getVerifiedUserInfo(request); 19 | 20 | return isAdmin ? resources : resources.filter(resource => { 21 | const tags = tagExtractor(resource); 22 | const groupIdTag = tags.find(tag => tag.key === 'GroupId'); 23 | return groupIdTag?.value === groupId; 24 | }); 25 | } 26 | 27 | export const extractEC2Tags: TagExtractor = (instance: Instance) => { 28 | return (instance.Tags || []).map((tag: any) => ({ 29 | key: tag.Key, 30 | value: tag.Value 31 | })); 32 | }; 33 | 34 | export const extractRDSTags: TagExtractor = (cluster: DBCluster) => { 35 | return (cluster.TagList || []).map((tag: any) => ({ 36 | key: tag.Key, 37 | value: tag.Value 38 | })); 39 | }; 40 | 41 | export const extractECSTags: TagExtractor = (service: Service) => { 42 | return (service.tags || []).map((tag: any) => ({ 43 | key: tag.key, 44 | value: tag.value 45 | })); 46 | }; 47 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Input.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps, forwardRef } from 'react'; 2 | 3 | export type InputBlockSize = 'lg' | 'md' | 'sm'; 4 | 5 | export type InputProps = ComponentProps<'input'> & { 6 | isError?: boolean; 7 | blockSize?: InputBlockSize; 8 | }; 9 | 10 | export const Input = forwardRef((props, ref) => { 11 | const { className, readOnly, isError, blockSize = 'lg', ...rest } = props; 12 | 13 | return ( 14 | 31 | ); 32 | }); 33 | 34 | export default Input; -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/aurora/README.md: -------------------------------------------------------------------------------- 1 | # Aurora Construct 2 | 3 | ## Purpose 4 | 5 | - Create a credential for Aurora 6 | - Create Aurora Cluster or Aurora Serverless (v1) 7 | - Create RDS Proxy for Aurora Cluster 8 | 9 | ## Required resources 10 | 11 | - VPC and private isolated subnet 12 | 13 | ## Required parameters (props) 14 | 15 | - `enabledServerless ` : Select whether Aurora Serverless or not 16 | - `auroraEdition ` : Define Aurora Engine 17 | > ### Note 18 | > 19 | > Aurora Serverless v1 supports `PostgreSQL Ver 10.18` or` MySQL 5.6/5.7` 20 | - `vpc ` : Define the vpc including isolated subnets 21 | - `dbUserName ` : Database username for db credentials 22 | 23 | ## Optional parameters (props) 24 | 25 | - `enabledProxy` : Create RDS proxy and this proxy attached to Aurora clustrer if this props is `true` 26 | 27 | ## Properties 28 | 29 | | Name | Type | Description | 30 | | ------------------- | :----------------------------------: | -----------------------------------------------: | 31 | | aurora | DatabaseCluster or ServerlessCluster | An Aurora | 32 | | proxy | DatabaseProxy | A RDS proxy | 33 | | databaseCredentials | Credentials | A Secrets Manager for storing databse credential | 34 | | proxyRole | Role | | 35 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/resources/templates/sampleapplist.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | サンプルアプリ 5 | 6 | 7 | 8 | 9 | 10 |
11 | 変更する 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
ID名前ジョブの成否
JOB0001JOB0002JOB0003JOB0004JOB0005
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/aurora/README.md: -------------------------------------------------------------------------------- 1 | # Aurora Construct 2 | 3 | ## Purpose 4 | 5 | - Create a credential for Aurora 6 | - Create Aurora Cluster or Aurora Serverless (v1) 7 | - Create RDS Proxy for Aurora Cluster 8 | 9 | ## Required resources 10 | 11 | - VPC and private isolated subnet 12 | 13 | ## Required parameters (props) 14 | 15 | - `enabledServerless ` : Select whether Aurora Serverless or not 16 | - `auroraEdition ` : Define Aurora Engine 17 | > ### Note 18 | > 19 | > Aurora Serverless v1 supports `PostgreSQL Ver 10.18` or` MySQL 5.6/5.7` 20 | - `vpc ` : Define the vpc including isolated subnets 21 | - `dbUserName ` : Database username for db credentials 22 | 23 | ## Optional parameters (props) 24 | 25 | - `enabledProxy` : Create RDS proxy and this proxy attached to Aurora clustrer if this props is `true` 26 | 27 | ## Properties 28 | 29 | | Name | Type | Description | 30 | | ------------------- | :----------------------------------: | -----------------------------------------------: | 31 | | aurora | DatabaseCluster or ServerlessCluster | An Aurora | 32 | | proxy | DatabaseProxy | A RDS proxy | 33 | | databaseCredentials | Credentials | A Secrets Manager for storing databse credential | 34 | | proxyRole | Role | | 35 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Modal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from '.'; 3 | 4 | interface ModalProps { 5 | isOpen: boolean; 6 | onClose: () => void; 7 | title: string; 8 | children: React.ReactNode; 9 | footer?: React.ReactNode; 10 | } 11 | 12 | export const Modal: React.FC = ({ isOpen, onClose, title, children, footer }) => { 13 | if (!isOpen) return null; 14 | 15 | return ( 16 |
17 |
18 |
19 |

{title}

20 | 29 |
30 |
31 | {children} 32 |
33 | {footer && ( 34 |
35 | {footer} 36 |
37 | )} 38 |
39 |
40 | ); 41 | }; 42 | 43 | export default Modal; 44 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/error.tsx: -------------------------------------------------------------------------------- 1 | import { Form, useNavigation } from '@remix-run/react'; 2 | import { redirect } from '@remix-run/node'; 3 | import type { LoaderFunctionArgs } from '@remix-run/node'; 4 | import { getSession } from '../utils/session.server'; 5 | import { Button } from '../components'; 6 | 7 | export async function loader({ request }: LoaderFunctionArgs) { 8 | // 既にログインしている場合はダッシュボードにリダイレクト 9 | const session = await getSession(request.headers.get('Cookie')); 10 | const user = session.get('user'); 11 | if (user) return redirect('/dashboard'); 12 | 13 | // URLからエラーメッセージを取得 14 | const url = new URL(request.url); 15 | const error = url.searchParams.get('error'); 16 | const errorDescription = url.searchParams.get('error_description'); 17 | 18 | return { 19 | error: error || 'Authentication Error', 20 | errorDescription: errorDescription || 'An error occurred during authentication. Please try again.', 21 | }; 22 | } 23 | 24 | export default function AuthError() { 25 | const navigation = useNavigation(); 26 | const isSubmitting = navigation.state !== 'idle'; 27 | 28 | return ( 29 |
30 |

認証エラー

31 |

認証中にエラーが発生しました。もう一度お試しください。

32 | 33 |
34 |
35 | 43 |
44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/README.md: -------------------------------------------------------------------------------- 1 | # webapp-java 2 | 3 | [日本語で読む](./README_ja.md) 4 | 5 | ## Overview 6 | 7 | This is a sample application in Java to test your deployment. 8 | 9 | You can query the database within the application and you can check the operation of batch processing by selecting records that cause batch processing to fail. 10 | 11 | ## Run on local 12 | 13 | ### Requirements 14 | 15 | - PostgresSQL 16 | 17 | ### Configure environment's variables 18 | 19 | Please define 3 environment variables for the database connection: 20 | 21 | ```sh 22 | export $DB_ENDPOINT=localhost 23 | export $DB_USERNAME= 24 | export $DB_PASSWORD= 25 | ``` 26 | 27 | ### Initialize table and add sample data 28 | 29 | The configuration of `spring.sqll.init.mode=always` is set in `src/resources/application.properties` that will always call `src/main/resources/data.sql` and `src/main/resources/schema.sql` to initialize the database. 30 | 31 | These settings are for the purpose of this sample, please consider separately ways to initialize the database and perform the migration in production enviroments. 32 | 33 | ### Run the application 34 | 35 | You can check the application by running the following command: 36 | 37 | ```sh 38 | ./gradlew bootRun 39 | ``` 40 | 41 | Access in your browser `http://localhost:8080` 42 | 43 | ### Sample screenshots 44 | 45 | The application is running when you can see a list of results from the database: 46 | 47 | ![list page](./docs/images/screenshot.png) 48 | 49 | ## Building 50 | 51 | Executing the following command will generate a jar file in `build/libs`. 52 | 53 | ```sh 54 | ./gradlew build 55 | ``` 56 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/components/RecordList.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Record } from '../types/record'; 3 | 4 | //mui 5 | import Table from '@mui/material/Table'; 6 | import TableBody from '@mui/material/TableBody'; 7 | import TableCell from '@mui/material/TableCell'; 8 | import TableHead from '@mui/material/TableHead'; 9 | import TableRow from '@mui/material/TableRow'; 10 | 11 | export const RecordList: React.FC<{ record: Record }> = ({ record }) => { 12 | return ( 13 | 14 | 15 | 16 | ID 17 | 名前 18 | ジョブの成否 19 | 20 | 21 | JOB0001 22 | JOB0002 23 | JOB0003 24 | JOB0004 25 | JOB0005 26 | 27 | 28 | 29 | 30 | {record.id} 31 | {record.name} 32 | {record.job0001_flag ? '成功' : '失敗'} 33 | {record.job0002_flag ? '成功' : '失敗'} 34 | {record.job0003_flag ? '成功' : '失敗'} 35 | {record.job0004_flag ? '成功' : '失敗'} 36 | {record.job0005_flag ? '成功' : '失敗'} 37 | 38 | 39 |
40 | ); 41 | }; 42 | 43 | export default RecordList; 44 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/s3/bucket.ts: -------------------------------------------------------------------------------- 1 | import * as aws_s3 from 'aws-cdk-lib/aws-s3'; 2 | import { RemovalPolicy } from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import { NagSuppressions } from 'cdk-nag'; 5 | 6 | export class Bucket extends Construct { 7 | public readonly bucket: aws_s3.Bucket; 8 | constructor(scope: Construct, id: string, props: { 9 | versioned: boolean; 10 | bucketName?: string; 11 | }) { 12 | super(scope, id); 13 | const accessLogBucket = new aws_s3.Bucket(this, `${id}AccessLogBucket`, { 14 | removalPolicy: RemovalPolicy.DESTROY, 15 | blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, 16 | encryption: aws_s3.BucketEncryption.S3_MANAGED, 17 | enforceSSL: true, 18 | autoDeleteObjects: true, 19 | objectOwnership: aws_s3.ObjectOwnership.BUCKET_OWNER_PREFERRED, 20 | }); 21 | this.bucket = new aws_s3.Bucket(this, `${id}Bucket`, { 22 | bucketName: props.bucketName, 23 | removalPolicy: RemovalPolicy.DESTROY, 24 | blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, 25 | encryption: aws_s3.BucketEncryption.S3_MANAGED, 26 | enforceSSL: true, 27 | serverAccessLogsBucket: accessLogBucket, 28 | autoDeleteObjects: true, 29 | objectOwnership: aws_s3.ObjectOwnership.BUCKET_OWNER_PREFERRED, 30 | versioned: props.versioned 31 | }); 32 | 33 | // cdk-nag suppressions 34 | NagSuppressions.addResourceSuppressions(accessLogBucket, [ 35 | { 36 | id: 'AwsSolutions-S1', 37 | reason: 38 | "This bucket is for access logs of the bucket. So it doesn't need more access log bucket.", 39 | }, 40 | ]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/s3/bucket.ts: -------------------------------------------------------------------------------- 1 | import * as aws_s3 from 'aws-cdk-lib/aws-s3'; 2 | import { RemovalPolicy } from 'aws-cdk-lib'; 3 | import { Construct } from 'constructs'; 4 | import { NagSuppressions } from 'cdk-nag'; 5 | 6 | export class Bucket extends Construct { 7 | public readonly bucket: aws_s3.Bucket; 8 | constructor(scope: Construct, id: string, props: { 9 | versioned: boolean; 10 | bucketName?: string; 11 | }) { 12 | super(scope, id); 13 | const accessLogBucket = new aws_s3.Bucket(this, `${id}AccessLogBucket`, { 14 | removalPolicy: RemovalPolicy.DESTROY, 15 | blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, 16 | encryption: aws_s3.BucketEncryption.S3_MANAGED, 17 | enforceSSL: true, 18 | autoDeleteObjects: true, 19 | objectOwnership: aws_s3.ObjectOwnership.BUCKET_OWNER_PREFERRED, 20 | }); 21 | this.bucket = new aws_s3.Bucket(this, `${id}Bucket`, { 22 | bucketName: props.bucketName, 23 | removalPolicy: RemovalPolicy.DESTROY, 24 | blockPublicAccess: aws_s3.BlockPublicAccess.BLOCK_ALL, 25 | encryption: aws_s3.BucketEncryption.S3_MANAGED, 26 | enforceSSL: true, 27 | serverAccessLogsBucket: accessLogBucket, 28 | autoDeleteObjects: true, 29 | objectOwnership: aws_s3.ObjectOwnership.BUCKET_OWNER_PREFERRED, 30 | versioned: props.versioned 31 | }); 32 | 33 | // cdk-nag suppressions 34 | NagSuppressions.addResourceSuppressions(accessLogBucket, [ 35 | { 36 | id: 'AwsSolutions-S1', 37 | reason: 38 | "This bucket is for access logs of the bucket. So it doesn't need more access log bucket.", 39 | }, 40 | ]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/cicd-stack.ts: -------------------------------------------------------------------------------- 1 | import { CfnOutput, StackProps, Stack, aws_codecommit, aws_ec2, aws_ecr, aws_s3 } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | 4 | // Constructs 5 | import { Ecr } from './construct/ecr/ecr'; 6 | import { CodePipelineWebappReact } from './construct/codepipeline/codepipeline-webapp-react'; 7 | import path from 'path'; 8 | import { NagSuppressions } from 'cdk-nag'; 9 | 10 | interface CicdStackProps extends StackProps { 11 | s3Bucket: aws_s3.Bucket; 12 | domainName: string; 13 | } 14 | 15 | export class CicdStack extends Stack { 16 | public readonly batchRepository: aws_ecr.Repository; 17 | constructor(scope: Construct, id: string, props: CicdStackProps) { 18 | super(scope, id, props); 19 | 20 | // Create ECR 21 | this.batchRepository = new Ecr(this, 'Batch', path.join(__dirname, '../batch')).containerRepository; 22 | 23 | // Create Pipeline 24 | const codecommitRepository = new aws_codecommit.Repository(this, 'SourceRepository', { 25 | repositoryName: `${id.toLowerCase()}-webapp-source`, 26 | }); 27 | 28 | // Create Deploy Pipeline 29 | new CodePipelineWebappReact(this, `CodePipeline`, { 30 | codeCommitRepository: codecommitRepository, 31 | s3bucket: props.s3Bucket, 32 | domainName: props.domainName, 33 | }); 34 | 35 | // Output 36 | new CfnOutput(this, 'SourceRepositoryName', { 37 | exportName: 'WebappSourceRepositoryName', 38 | value: codecommitRepository.repositoryName, 39 | }); 40 | new CfnOutput(this, 'SourceRepositoryUrl', { 41 | exportName: 'WebappSourceRepositoryUrl', 42 | value: codecommitRepository.repositoryCloneUrlHttp, 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/components/RecordFormRow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Record } from '../types/record'; 3 | import TableCell from '@mui/material/TableCell'; 4 | import TableRow from '@mui/material/TableRow'; 5 | import Checkbox from '@mui/material/Checkbox'; 6 | 7 | export const RecordFormRow: React.FC<{ 8 | record: Record; 9 | setFlagHandler: (id: number, jobFlagKey: string, newFlagValue: boolean) => void; 10 | }> = ({ record, setFlagHandler }) => { 11 | return ( 12 | 13 | {record.id} 14 | {record.name} 15 | 16 | setFlagHandler(record.id, 'job0001_flag', !record.job0001_flag)} 19 | /> 20 | 21 | 22 | setFlagHandler(record.id, 'job0002_flag', !record.job0002_flag)} 25 | /> 26 | 27 | 28 | setFlagHandler(record.id, 'job0003_flag', !record.job0003_flag)} 31 | /> 32 | 33 | 34 | setFlagHandler(record.id, 'job0004_flag', !record.job0004_flag)} 37 | /> 38 | 39 | 40 | setFlagHandler(record.id, 'job0005_flag', !record.job0005_flag)} 43 | /> 44 | 45 | 46 | ); 47 | }; 48 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/network-stack.ts: -------------------------------------------------------------------------------- 1 | import { StackProps, Stack, aws_ec2, aws_route53resolver, Token } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import { Network } from './construct/network/network'; 4 | 5 | interface NetworkStackProps extends StackProps { 6 | sharedVpcCidr: string; 7 | vpcCidr: string; 8 | privateLinkVpcCidr?: string; 9 | tgw: aws_ec2.CfnTransitGateway; 10 | resolverInboundEndpointIps: string[]; 11 | } 12 | 13 | export class NetworkStack extends Stack { 14 | public readonly vpc: aws_ec2.Vpc; 15 | public readonly privateLinkVpc: aws_ec2.Vpc; 16 | public readonly sgIdForVpcEndpoint: string; 17 | constructor(scope: Construct, id: string, props: NetworkStackProps) { 18 | super(scope, id, props); 19 | 20 | // Create networking resources 21 | const network = new Network(this, `Vpc`, { 22 | cidr: props.vpcCidr, 23 | maxAzs: 2, 24 | tgw: props.tgw 25 | }); 26 | this.vpc = network.vpc; 27 | 28 | const dhcpOptions = new aws_ec2.CfnDHCPOptions(this, 'DHCPOptions', { 29 | domainName: `${Stack.of(this).region}.compute.internal`, 30 | domainNameServers: props.resolverInboundEndpointIps 31 | }); 32 | 33 | // Gateway Endpoint is required in each VPCs. 34 | network.vpc.addGatewayEndpoint('S3GatewayEndpoint', { 35 | service: aws_ec2.GatewayVpcEndpointAwsService.S3, 36 | }); 37 | 38 | new aws_ec2.CfnVPCDHCPOptionsAssociation(this, 'DHCPOptionsAssociation', { 39 | dhcpOptionsId: dhcpOptions.attrDhcpOptionsId, 40 | vpcId: this.vpc.vpcId 41 | }) 42 | 43 | // Add route from this AppVPC to SharedVpc via TransitGatewayAttachement 44 | network.addRouteToTgwAttachementSubnets(props.tgw.attrId, props.sharedVpcCidr); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/serverless/README_apigw.md: -------------------------------------------------------------------------------- 1 | # ApiGw Construct 2 | 3 | ## Purpose 4 | 5 | Creates API Gateway for Serverless Architecture 6 | 7 | ## Required resources 8 | 9 | - VPC including RDS 10 | 11 | ## Required parameters (props) 12 | 13 | - `vpc ` : Define the vpc including RDS 14 | - `auroraSecretName ` : Secret Name including RDS information 15 | - `auroraSecretArn ` : Secret Arn including RDS information 16 | - `auroraSecurityGroupId `: Security Group Id including RDS 17 | - `auroraSecretEncryptionKeyArn ` : KMS Key Arn which encrypt secret including RDS information 18 | - `auroraEdition `: edition of aurora database 19 | - `rdsProxyEndpoint ` : Endpoint url of RDS proxy endpoint 20 | - `rdsProxyArn ` : ARN of RDS proxy endpoint 21 | 22 | ## Optional parameters (props) 23 | 24 | None 25 | 26 | ## Properties 27 | 28 | | Name | Type | Description | 29 | | ------------------------ | :-----------------------------------------------: | ------------------------------------: | 30 | | vpcEndpointSecurityGroup | aws_ec2.SecurityGroup | sg for API Gateway vpc endpoint | 31 | | privateApiVpcEndpoint | aws_ec2.InterfaceVpcEndpoint | API Gateway vpc endpoint | 32 | | privateApi | aws_apigateway.LambdaRestApi | API Gateway | 33 | | sgForLambda | aws_ec2.SecurityGroup | sg for lambda which has to connect DB | 34 | | addResource | (resourceName: string) => aws_apigateway.Resource | function for adding resource to API | 35 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/utils/jwt-verify.server.ts: -------------------------------------------------------------------------------- 1 | import { CognitoJwtVerifier } from "aws-jwt-verify"; 2 | import { requireAuthentication } from './auth.server'; 3 | 4 | // Initialize Cognito JWT Verifier (Singleton pattern) 5 | const verifier = CognitoJwtVerifier.create({ 6 | userPoolId: process.env.USER_POOL_ID!, 7 | tokenUse: "id", 8 | clientId: process.env.CLIENT_ID!, 9 | }); 10 | 11 | // Verify ID token and get payload 12 | export async function verifyAndDecodeIdToken(idToken: string) { 13 | try { 14 | const payload = await verifier.verify(idToken); 15 | return payload; 16 | } catch (error) { 17 | console.error('JWT verification failed:', error); 18 | throw new Error('Invalid or expired token'); 19 | } 20 | } 21 | 22 | // Safely get GroupId from verified token 23 | export async function getVerifiedGroupId(request: Request): Promise { 24 | const idToken = (await requireAuthentication(request)).idToken; 25 | const payload = await verifyAndDecodeIdToken(idToken); 26 | 27 | // Get groupId from custom attributes 28 | const groupId = payload['custom:groupId'] as string; 29 | 30 | if (!groupId) { 31 | throw new Error('GroupId not found in verified token'); 32 | } 33 | 34 | return groupId; 35 | } 36 | 37 | // Get admin privileges from verified token as well 38 | export async function getVerifiedUserInfo(request: Request) { 39 | const idToken = (await requireAuthentication(request)).idToken; 40 | const payload = await verifyAndDecodeIdToken(idToken); 41 | 42 | const groups = payload['cognito:groups'] as string[] || []; 43 | 44 | return { 45 | email: payload.email as string, 46 | groupId: payload['custom:groupId'] as string, 47 | isAdmin: groups.includes('Admins'), 48 | groups: groups, 49 | // Add other attributes as needed 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infraops-console", 3 | "version": "1.0.0", 4 | "private": true, 5 | "sideEffects": false, 6 | "scripts": { 7 | "typecheck": "tsc", 8 | "dev": "remix vite:dev", 9 | "build": "remix vite:build", 10 | "start": "remix-serve ./build/server/index.js" 11 | }, 12 | "dependencies": { 13 | "@aws-sdk/client-cognito-identity-provider": "^3.873.0", 14 | "@aws-sdk/client-dynamodb": "^3.873.0", 15 | "@aws-sdk/client-ec2": "^3.873.0", 16 | "@aws-sdk/client-ecs": "^3.883.0", 17 | "@aws-sdk/client-rds": "^3.883.0", 18 | "@aws-sdk/client-scheduler": "^3.876.0", 19 | "@aws-sdk/client-secrets-manager": "^3.873.0", 20 | "@aws-sdk/credential-providers": "^3.895.0", 21 | "@aws-sdk/lib-dynamodb": "^3.873.0", 22 | "@headlessui/react": "^2.2.7", 23 | "@mjackson/headers": "^0.11.1", 24 | "@remix-run/node": "^2.17.0", 25 | "@remix-run/react": "^2.17.0", 26 | "@remix-run/serve": "^2.17.0", 27 | "aws-jwt-verify": "^5.1.0", 28 | "isbot": "^3.6.8", 29 | "react": "^18.2.0", 30 | "react-dom": "^18.2.0", 31 | "react-router": "^7.8.1", 32 | "remix-auth": "^4.2.0", 33 | "remix-auth-form": "^3.0.0", 34 | "zod": "^3.22.2" 35 | }, 36 | "devDependencies": { 37 | "@digital-go-jp/tailwind-theme-plugin": "^0.3.2", 38 | "@remix-run/dev": "^2.17.0", 39 | "@remix-run/eslint-config": "^2.17.0", 40 | "@tailwindcss/postcss": "^4.1.12", 41 | "@types/react": "^18.2.20", 42 | "@types/react-dom": "^18.2.7", 43 | "autoprefixer": "^10.4.21", 44 | "eslint": "^8.38.0", 45 | "postcss": "^8.5.6", 46 | "tailwindcss": "^4.1.12", 47 | "typescript": "^5.1.6", 48 | "vite": "^6.3.5", 49 | "vite-tsconfig-paths": "^5.1.4" 50 | }, 51 | "engines": { 52 | "node": ">=22.0.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | React App 25 | 26 | 27 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/UniversalLink.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps } from 'react'; 2 | import { Link } from '@remix-run/react'; 3 | 4 | export const universalLinkStyle = 'text-solid-gray-800 text-dns-16N-130 underline underline-offset-[calc(3/16em)] hover:decoration-[calc(3/16+1rem)] focus-visible:rounded-4 focus-visible:outline focus-visible:outline-4 focus-visible:outline-black focus-visible:outline-offset-4'; 5 | 6 | export type UniversalLinkProps = ComponentProps & { 7 | asChild?: boolean; 8 | className?: string; 9 | showIcon?: boolean; 10 | }; 11 | 12 | export const UniversalLink = (props: UniversalLinkProps) => { 13 | const { asChild, children, className, showIcon, ...rest } = props; 14 | 15 | if (asChild) { 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | } 22 | 23 | return ( 24 | 25 | {children} 26 | {props.target === '_blank' && showIcon && } 27 | 28 | ); 29 | }; 30 | 31 | export type UniversalLinkExternalLinkIconProps = ComponentProps<'svg'>; 32 | 33 | export const UniversalLinkExternalLinkIcon = (props: UniversalLinkExternalLinkIconProps) => { 34 | const { className, ...rest } = props; 35 | 36 | return ( 37 | 46 | 51 | 52 | ); 53 | }; 54 | 55 | export default UniversalLink; 56 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/serverless/README_serverless_app.md: -------------------------------------------------------------------------------- 1 | # ServerlessApp Construct 2 | 3 | ## Purpose 4 | 5 | Creates serverless application resources including API Gateway, Lambda functions, S3 bucket, and ALB target groups with improved architecture following Clean Architecture principles. 6 | 7 | ## Required resources 8 | 9 | - VPC including subnets and VPC endpoints 10 | - Database secret and proxy configuration 11 | - S3 Interface VPC Endpoint 12 | 13 | ## Required parameters (props) 14 | 15 | - `vpc ` : VPC for deploying resources 16 | - `domainName ` : Domain name for the application 17 | - `certificateArn ` : ARN of SSL certificate 18 | - `dbSecretName ` : Secret Name including RDS information 19 | - `dbSecretArn ` : Secret Arn including RDS information 20 | - `dbSecurityGroupId ` : Security Group ID for database 21 | - `dbSecretEncryptionKeyArn ` : KMS Key Arn which encrypt secret including RDS information 22 | - `dbProxyEndpoint ` : Endpoint url of RDS proxy endpoint 23 | - `dbProxyArn ` : ARN of RDS proxy endpoint 24 | - `spaS3InterfaceEndpoint ` : S3 Interface VPC Endpoint 25 | 26 | ## Optional parameters (props) 27 | 28 | None 29 | 30 | ## Properties 31 | 32 | | Name | Type | Description | 33 | | ------ | :------------------------------: | --------------: | 34 | | apiGw | ApiGw | API Gateway construct with enhanced addMethodWithLambdaIntegration | 35 | | webappS3bucket | aws_s3.Bucket | S3 bucket for web application hosting | 36 | | sgForLambda | aws_ec2.SecurityGroup | Security Group for Lambda functions | 37 | | apiGwTargetGroup | aws_elasticloadbalancingv2.ApplicationTargetGroup | Target group for API Gateway | 38 | | s3TargetGroup | aws_elasticloadbalancingv2.ApplicationTargetGroup | Target group for S3 VPC Endpoint | 39 | | apiDomain | aws_apigateway.DomainName | API Gateway domain configuration | 40 | 41 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/controller/SampleAppController.java: -------------------------------------------------------------------------------- 1 | 2 | package com.example.sampleapp.webapp.controller; 3 | 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.ModelAttribute; 8 | import org.springframework.web.bind.annotation.PostMapping; 9 | import org.springframework.ui.Model; 10 | import org.springframework.validation.BindingResult; 11 | import org.springframework.validation.annotation.Validated; 12 | import com.example.sampleapp.webapp.domain.dto.SampleAppListDto; 13 | import com.example.sampleapp.webapp.domain.SampleAppService; 14 | 15 | @Controller 16 | public class SampleAppController { 17 | private final SampleAppService service; 18 | public SampleAppService sampleAppService; 19 | 20 | public SampleAppController(SampleAppService service) { 21 | this.service = service; 22 | } 23 | 24 | @RequestMapping(value = { "/sampleapp/list", "/" }) 25 | @GetMapping 26 | public String sampleAppList(Model model) { 27 | 28 | SampleAppListDto sampleAppList = service.listAll(); 29 | model.addAttribute("sampleapplist", sampleAppList); 30 | return "sampleapplist"; 31 | } 32 | 33 | @RequestMapping("/sampleapp/form") 34 | @GetMapping 35 | public String sampleAppForm(Model model) { 36 | 37 | SampleAppListDto sampleAppList = service.listAll(); 38 | model.addAttribute("sampleapplist", sampleAppList); 39 | return "sampleappform"; 40 | } 41 | 42 | @RequestMapping("/sampleapp/form/update") 43 | @PostMapping 44 | public String sampleAppListUpdate(@Validated @ModelAttribute SampleAppListDto sampleappformlist, 45 | BindingResult result, Model model) { 46 | 47 | service.updateAll(sampleappformlist); 48 | model.addAttribute("sampleapplist", service.listAll()); 49 | return "forward:/"; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /usecases/webapp-react/webapp/src/components/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Record } from '../types/record'; 3 | import { RecordForm } from './RecordForm'; 4 | import { RecordList } from './RecordList'; 5 | import { get } from '../modules/requests'; 6 | import { post } from '../modules/requests'; 7 | import Button from '@mui/material/Button'; 8 | 9 | const resource = 'sample/'; 10 | 11 | export const Dashboard: React.FC = () => { 12 | const [sampleRecords, setSampleRecords] = useState({} as Record); 13 | const [formState, setFormState] = useState(false); 14 | 15 | const setJobFlag = (id: number, jobFlagKey: string, newFlagValue: boolean) => { 16 | setSampleRecords((prevRecord) => 17 | prevRecord.id === id ? { ...prevRecord, [jobFlagKey]: newFlagValue } : prevRecord, 18 | ); 19 | }; 20 | 21 | useEffect(() => { 22 | (async () => { 23 | const res = await get(resource); 24 | setSampleRecords(res.data as Record); 25 | })(); 26 | }, []); 27 | return ( 28 | 29 |

Hello From S3 through CodePipeline !

30 |
31 | {formState ? ( 32 | <> 33 | 42 | 43 | 44 | ) : ( 45 | <> 46 | 54 | 55 | 56 | )} 57 |
58 | ); 59 | }; 60 | 61 | export default Dashboard; 62 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/api.schedules.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * API Route for EC2 Instance Schedule Management 3 | * 4 | * This file serves as a Remix API route that enables frontend components 5 | * to call backend functionality without page navigation. 6 | * 7 | * Provides endpoints for managing EC2 instance start/stop schedules 8 | * using AWS EventBridge Scheduler without page transitions. 9 | * 10 | * - loader: Handles GET requests (schedule data fetching) 11 | * - action: Handles POST/PUT/DELETE requests (schedule manipulation) 12 | * 13 | * Business logic is delegated to functions in the models/scheduler.server.ts file. 14 | * This file only handles authentication checks and parameter passing. 15 | * 16 | * Frontend usage examples: 17 | * - useFetcher().load("/api/schedules?instanceId=xxx") 18 | * - useFetcher().submit(formData, { method: "post", action: "/api/schedules" }) 19 | */ 20 | 21 | import { type ActionFunctionArgs, type LoaderFunctionArgs } from '@remix-run/node'; 22 | import { getSchedulesByInstanceId, handleScheduleAction } from '../models/scheduler.server'; 23 | import { requireAuthentication } from '../utils/auth.server'; 24 | 25 | export async function loader({ request }: LoaderFunctionArgs) { 26 | // Authentication check 27 | await requireAuthentication(request); 28 | 29 | // Get instance ID from query parameters 30 | const url = new URL(request.url); 31 | const instanceId = url.searchParams.get('instanceId'); 32 | 33 | // Delegate business logic to models layer 34 | return await getSchedulesByInstanceId(instanceId, request); 35 | } 36 | 37 | export async function action({ request }: ActionFunctionArgs) { 38 | // Authentication check 39 | await requireAuthentication(request); 40 | 41 | // Extract form data and convert to parameters object 42 | const formData = await request.formData(); 43 | const params = Object.fromEntries(formData); 44 | const actionType = params.actionType as string; 45 | 46 | // Delegate business logic to models layer 47 | return await handleScheduleAction(actionType, params, request); 48 | } 49 | -------------------------------------------------------------------------------- /usecases/webapp-react/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 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '3.0.0-SNAPSHOT' 3 | id 'io.spring.dependency-management' version '1.0.13.RELEASE' 4 | id 'java' 5 | id 'eclipse' 6 | id "org.owasp.dependencycheck" version "7.4.4" 7 | } 8 | 9 | group = 'com.example.webapp' 10 | version = '0.0.1-SNAPSHOT' 11 | sourceCompatibility = '17' 12 | ext['tomcat.version'] = '10.1.4' 13 | 14 | repositories { 15 | mavenCentral() 16 | maven { url 'https://repo.spring.io/milestone' } 17 | maven { url 'https://repo.spring.io/snapshot' } 18 | } 19 | 20 | dependencies { 21 | implementation 'org.springframework.boot:spring-boot-starter-data-jdbc' 22 | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' 23 | implementation 'org.springframework.boot:spring-boot-starter-web' 24 | implementation 'org.springframework.boot:spring-boot-starter' 25 | implementation 'org.springframework.boot:spring-boot-starter-log4j2' 26 | implementation 'org.springframework.boot:spring-boot-starter-validation' 27 | implementation 'javax.validation:validation-api:2.0.0.Final' 28 | runtimeOnly 'org.postgresql:postgresql' 29 | compileOnly 'org.projectlombok:lombok' 30 | annotationProcessor 'org.projectlombok:lombok' 31 | implementation 'javax.persistence:javax.persistence-api:2.2' 32 | developmentOnly 'org.springframework.boot:spring-boot-devtools' 33 | compileOnly 'org.springframework.boot:spring-boot-starter-tomcat' 34 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 35 | 36 | modules { 37 | module('org.springframework.boot:spring-boot-starter-logging') { 38 | replacedBy 'org.springframework.boot:spring-boot-starter-log4j2' 39 | } 40 | } 41 | } 42 | 43 | tasks.named('test') { 44 | useJUnitPlatform() 45 | } 46 | 47 | dependencyCheck { 48 | autoUpdate = true 49 | analyzedTypes = ['jar', 'war', 'js'] 50 | cveValidForHours = 24 51 | format = 'HTML' 52 | outputDirectory = "$buildDir/owasp-reports" 53 | scanProjects = [] 54 | skipProjects = [] 55 | scanSet = [ 56 | 'src/main/resources', 57 | 'src/main/java', 58 | 'build/libs' 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ecs/README_JOB.md: -------------------------------------------------------------------------------- 1 | # EcsJob Construct 2 | 3 | ## Purpose 4 | 5 | Create Job that uses Task of ECS Cluster on Fargate. 6 | The Job has rerunability, state management, and error notification function. 7 | 8 | ## Required resources 9 | 10 | - VPC that includes private isolated subnet 11 | - VPC Endpoint for ECR, CloudWatch Logs, SNS, and SecretsManager 12 | - ECR Repository 13 | - DynamoDB table 14 | - SNS Topic 15 | 16 | ## Required parameters (props) 17 | 18 | - `auroraSecretEncryptionKeyArn` : Encryption key ARN of aurora secret 19 | - `cluster` : Cluster of ECS on Fargate 20 | - `image` : Container's image 21 | - `table` : Store job invokation status each days 22 | - `topic` : Sending error notification 23 | 24 | ## Optional parameters (props) 25 | 26 | - `taskDefinitionEnvironments` <{[key: string]: string}>: Environments variables for task 27 | - `taskDefinitionSecrets` <{[key: string]: ecs.Secret}>: Secrets environments variables for task 28 | - `taskInput` <{[key: string]: any}>: Environments variables for statemachine of stepfunctions 29 | 30 | ## Properties 31 | 32 | | Name | Type | Description | 33 | | ------------ | :----------------------------------------------: | -------------------------------------------------------------------------------------------------: | 34 | | statemachine | stepfunctions.StateMachine | | 35 | | job | stepfunctions_tasks.Step FunctionsStartExecution | A Step Functions Task to call StartExecution on child state machine. | 36 | | task | aws_stepfunctions_tasks.EcsRunTask | It's just task of stepfunctions. This task is defined as ECS's task that called from stepfunctions | 37 | | taskRole | aws_iam.Role | The role is to execute AWS APIs from task. | 38 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/ecs/README_JOB.md: -------------------------------------------------------------------------------- 1 | # EcsJob Construct 2 | 3 | ## Purpose 4 | 5 | Create Job that uses Task of ECS Cluster on Fargate. 6 | The Job has rerunability, state management, and error notification function. 7 | 8 | ## Required resources 9 | 10 | - VPC that includes private isolated subnet 11 | - VPC Endpoint for ECR, CloudWatch Logs, SNS, and SecretsManager 12 | - ECR Repository 13 | - DynamoDB table 14 | - SNS Topic 15 | 16 | ## Required parameters (props) 17 | 18 | - `auroraSecretEncryptionKeyArn` : Encryption key ARN of aurora secret 19 | - `cluster` : Cluster of ECS on Fargate 20 | - `image` : Container's image 21 | - `table` : Store job invokation status each days 22 | - `topic` : Sending error notification 23 | 24 | ## Optional parameters (props) 25 | 26 | - `taskDefinitionEnvironments` <{[key: string]: string}>: Environments variables for task 27 | - `taskDefinitionSecrets` <{[key: string]: ecs.Secret}>: Secrets environments variables for task 28 | - `taskInput` <{[key: string]: any}>: Environments variables for statemachine of stepfunctions 29 | 30 | ## Properties 31 | 32 | | Name | Type | Description | 33 | | ------------ | :----------------------------------------------: | -------------------------------------------------------------------------------------------------: | 34 | | statemachine | stepfunctions.StateMachine | | 35 | | job | stepfunctions_tasks.Step FunctionsStartExecution | A Step Functions Task to call StartExecution on child state machine. | 36 | | task | aws_stepfunctions_tasks.EcsRunTask | It's just task of stepfunctions. This task is defined as ECS's task that called from stepfunctions | 37 | | taskRole | aws_iam.Role | The role is to execute AWS APIs from task. | 38 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Select.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface SelectOption { 4 | value: string; 5 | label: string; 6 | } 7 | 8 | interface SelectProps extends React.SelectHTMLAttributes { 9 | label?: string; 10 | options: SelectOption[]; 11 | error?: string; 12 | helpText?: string; 13 | fullWidth?: boolean; 14 | } 15 | 16 | export const Select: React.FC = ({ 17 | label, 18 | options, 19 | error, 20 | helpText, 21 | fullWidth = true, 22 | className = '', 23 | id, 24 | ...props 25 | }) => { 26 | const selectId = id || `select-${Math.random().toString(36).substring(2, 9)}`; 27 | 28 | const selectStyle = error 29 | ? 'border-red-500 focus:border-red-500 focus:ring-red-500' 30 | : 'border-gray-300 focus:border-blue-500 focus:ring-blue-500'; 31 | 32 | const widthStyle = fullWidth ? 'w-full' : ''; 33 | 34 | const baseStyle = 'block rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-opacity-50 disabled:bg-gray-100 disabled:cursor-not-allowed px-3 h-10'; 35 | 36 | return ( 37 |
38 | {label && ( 39 | 42 | )} 43 | 56 | {error && ( 57 |

58 | {error} 59 |

60 | )} 61 | {helpText && !error && ( 62 |

63 | {helpText} 64 |

65 | )} 66 |
67 | ); 68 | }; 69 | 70 | export default Select; 71 | -------------------------------------------------------------------------------- /usecases/webapp-java/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) -------------------------------------------------------------------------------- /usecases/webapp-react/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) -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/root.tsx: -------------------------------------------------------------------------------- 1 | import type { LinksFunction, LoaderFunctionArgs } from "@remix-run/node"; 2 | import { 3 | Links, 4 | Meta, 5 | Outlet, 6 | Scripts, 7 | ScrollRestoration, 8 | useRouteError, 9 | isRouteErrorResponse, 10 | } from "@remix-run/react"; 11 | 12 | import globalStylesUrl from "./styles/global.css?url"; 13 | import { authenticator } from "./utils/auth.server"; 14 | 15 | export const links: LinksFunction = () => [ 16 | { rel: "stylesheet", href: globalStylesUrl }, 17 | ]; 18 | 19 | export async function loader({ request }: LoaderFunctionArgs) { 20 | try { 21 | const user = await authenticator.authenticate('TOTP', request); 22 | return { user } 23 | } catch (error) { 24 | return { user: null }; 25 | } 26 | } 27 | 28 | export default function App() { 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ); 44 | } 45 | 46 | export function ErrorBoundary() { 47 | const error = useRouteError(); 48 | 49 | return ( 50 | 51 | 52 | 53 | 54 | エラーが発生しました 55 | 56 | 57 | 58 | 59 |
60 |

エラーが発生しました

61 | {isRouteErrorResponse(error) ? ( 62 | <> 63 |

{error.status} {error.statusText}

64 |

{error.data}

65 | 66 | ) : error instanceof Error ? ( 67 | <> 68 |

{error.name}: {error.message}

69 |
{error.stack}
70 | 71 | ) : ( 72 |

不明なエラーが発生しました

73 | )} 74 |
75 | ホームに戻る 76 |
77 |
78 | 79 | 80 | 81 | ); 82 | } 83 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/aurora/dbinitlambda.ts: -------------------------------------------------------------------------------- 1 | import { aws_ec2, custom_resources, CustomResource } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import { NagSuppressions } from 'cdk-nag'; 4 | import { DefaultLambda } from '../serverless/lambda'; 5 | import * as path from 'path'; 6 | 7 | export class DbInitLambda extends Construct { 8 | constructor( 9 | scope: Construct, 10 | id: string, 11 | props: { 12 | vpc: aws_ec2.IVpc; 13 | sgForLambda: aws_ec2.SecurityGroup; 14 | dbSecretName: string; 15 | dbSecretArn: string; 16 | dbSecretEncryptionKeyArn: string; 17 | dbProxyEndpoint: string; 18 | dbProxyArn: string; 19 | } 20 | ) { 21 | super(scope, id); 22 | 23 | const initLambda = new DefaultLambda(this, 'dbInitLambda', { 24 | entry: path.join(__dirname, '../../../functions/init.ts'), 25 | vpc: props.vpc, 26 | db: { 27 | secretName: props.dbSecretName, 28 | secretArn: props.dbSecretArn, 29 | secretEncryptionKeyArn: props.dbSecretEncryptionKeyArn, 30 | proxyEndpoint: props.dbProxyEndpoint, 31 | proxyArn: props.dbProxyArn, 32 | }, 33 | sgForLambda: props.sgForLambda, 34 | }); 35 | 36 | const provider = new custom_resources.Provider(this, 'DBInitProvider', { 37 | onEventHandler: initLambda.lambda, 38 | }); 39 | 40 | new CustomResource(this, 'DBInitResource', { 41 | serviceToken: provider.serviceToken, 42 | properties: { 43 | time: Date.now().toString(), 44 | }, 45 | }); 46 | NagSuppressions.addResourceSuppressions( 47 | provider, 48 | [ 49 | { 50 | id: 'AwsSolutions-L1', 51 | reason: 'This is Custom Resource managed by AWS', 52 | }, 53 | ], 54 | true 55 | ); 56 | NagSuppressions.addResourceSuppressions( 57 | provider, 58 | [ 59 | { 60 | id: 'AwsSolutions-IAM4', 61 | reason: 'This is Custom Resource managed by AWS', 62 | }, 63 | ], 64 | true 65 | ); 66 | NagSuppressions.addResourceSuppressions( 67 | provider, 68 | [ 69 | { 70 | id: 'AwsSolutions-IAM5', 71 | reason: 'This is Custom Resource managed by AWS', 72 | }, 73 | ], 74 | true 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/ErrorAlert.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface ErrorAlertProps { 4 | isVisible: boolean; 5 | message: string | null; 6 | onClose: () => void; 7 | } 8 | 9 | export const ErrorAlert: React.FC = ({ 10 | isVisible, 11 | message, 12 | onClose 13 | }) => { 14 | if (!isVisible || !message) return null; 15 | 16 | return ( 17 |
18 |
19 |
20 |
21 | 27 | 32 | 33 |
34 |
35 |

36 | {message} 37 |

38 |
39 |
40 |
41 | 51 |
52 |
53 |
54 |
55 |
56 | ); 57 | }; 58 | 59 | export default ErrorAlert; 60 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/java/com/example/sampleapp/webapp/domain/SampleAppService.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleapp.webapp.domain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import com.example.sampleapp.webapp.repository.*; 9 | import com.example.sampleapp.webapp.repository.model.*; 10 | import com.example.sampleapp.webapp.domain.dto.SampleAppDto; 11 | import com.example.sampleapp.webapp.domain.dto.SampleAppListDto; 12 | 13 | @Service 14 | public class SampleAppService { 15 | @Autowired 16 | private SampleAppRepository repository; 17 | 18 | public SampleAppListDto listAll() { 19 | Iterable data = repository.findAll(); 20 | List list = new ArrayList(); 21 | SampleAppListDto ret = new SampleAppListDto(); 22 | 23 | for (SampleApp sampleApp : data) { 24 | SampleAppDto sampleAppDto = new SampleAppDto(); 25 | 26 | sampleAppDto.setId(sampleApp.getId()); 27 | sampleAppDto.setName(sampleApp.getName()); 28 | sampleAppDto.setJob0001Flag(sampleApp.getJob0001Flag()); 29 | sampleAppDto.setJob0002Flag(sampleApp.getJob0002Flag()); 30 | sampleAppDto.setJob0003Flag(sampleApp.getJob0003Flag()); 31 | sampleAppDto.setJob0004Flag(sampleApp.getJob0004Flag()); 32 | sampleAppDto.setJob0005Flag(sampleApp.getJob0005Flag()); 33 | 34 | list.add(sampleAppDto); 35 | } 36 | 37 | ret.setSampleAppList(list); 38 | return ret; 39 | } 40 | 41 | public void updateAll(SampleAppListDto sampleAppListDto) { 42 | List sampleAppList = new ArrayList(); 43 | 44 | for (SampleAppDto sampleAppDto : sampleAppListDto.getSampleAppList()) { 45 | SampleApp sampleApp = repository.findById(sampleAppDto.getId()).get(); 46 | sampleApp.setJob0001Flag(sampleAppDto.getJob0001Flag()); 47 | sampleApp.setJob0002Flag(sampleAppDto.getJob0002Flag()); 48 | sampleApp.setJob0003Flag(sampleAppDto.getJob0003Flag()); 49 | sampleApp.setJob0004Flag(sampleAppDto.getJob0004Flag()); 50 | sampleApp.setJob0005Flag(sampleAppDto.getJob0005Flag()); 51 | sampleAppList.add(sampleApp); 52 | } 53 | repository.saveAll(sampleAppList); 54 | } 55 | 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ecr/ecr.ts: -------------------------------------------------------------------------------- 1 | import { aws_ecr, aws_ecr_assets, CfnOutput, RemovalPolicy, Stack } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import * as ecrdeploy from 'cdk-ecr-deployment'; 4 | import { NagSuppressions } from 'cdk-nag'; 5 | 6 | export class Ecr extends Construct { 7 | public readonly containerRepository: aws_ecr.Repository; 8 | public readonly ecrDeployment?: ecrdeploy.ECRDeployment; 9 | constructor(scope: Construct, id: string, imagePath?: string) { 10 | super(scope, id); 11 | 12 | this.containerRepository = new aws_ecr.Repository(this, `${id}Repository`, { 13 | imageScanOnPush: true, 14 | encryption: aws_ecr.RepositoryEncryption.KMS, 15 | removalPolicy: RemovalPolicy.DESTROY, 16 | emptyOnDelete: true // For develop environment 17 | }); 18 | 19 | if (imagePath) { 20 | const dockerImageAsset = new aws_ecr_assets.DockerImageAsset( 21 | this, 22 | "DockerImageAsset", 23 | { 24 | directory: imagePath, 25 | platform: aws_ecr_assets.Platform.LINUX_AMD64, 26 | } 27 | ); 28 | 29 | this.ecrDeployment = new ecrdeploy.ECRDeployment(this, `${id}ImageDeployment`, { 30 | src: new ecrdeploy.DockerImageName(dockerImageAsset.imageUri), 31 | dest: new ecrdeploy.DockerImageName(this.containerRepository.repositoryUriForTag('latest')), 32 | }) 33 | } 34 | 35 | 36 | new CfnOutput(this, 'RepositoryName', { 37 | exportName: `${id}ContainerRepositoryName`, 38 | value: this.containerRepository.repositoryName, 39 | }); 40 | new CfnOutput(this, 'RepositoryUri', { 41 | exportName: `${id}ContainerRepositoryUri`, 42 | value: this.containerRepository.repositoryUri, 43 | }); 44 | new CfnOutput(this, 'EcrRegion', { 45 | exportName: `${id}Region`, 46 | value: this.containerRepository.env.region, 47 | }); 48 | 49 | // CDK Nag Suppressions 50 | if (imagePath && this.ecrDeployment) { 51 | NagSuppressions.addResourceSuppressionsByPath(Stack.of(this), `/${Stack.of(this).stackName}/Custom::CDKECRDeploymentbd07c930edb94112a20f03f096f53666512MiB/ServiceRole/Resource`, [ 52 | { 53 | id: 'AwsSolutions-IAM4', 54 | reason: 'The construct uses managed policies for ECR deployment functionality', 55 | appliesTo: ['Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'] 56 | } 57 | ]); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/ecr/ecr.ts: -------------------------------------------------------------------------------- 1 | import { aws_ecr, aws_ecr_assets, CfnOutput, RemovalPolicy, Stack } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import * as ecrdeploy from 'cdk-ecr-deployment'; 4 | import { NagSuppressions } from 'cdk-nag'; 5 | 6 | export class Ecr extends Construct { 7 | public readonly containerRepository: aws_ecr.Repository; 8 | public readonly ecrDeployment?: ecrdeploy.ECRDeployment; 9 | constructor(scope: Construct, id: string, imagePath?: string) { 10 | super(scope, id); 11 | 12 | this.containerRepository = new aws_ecr.Repository(this, `${id}Repository`, { 13 | imageScanOnPush: true, 14 | encryption: aws_ecr.RepositoryEncryption.KMS, 15 | removalPolicy: RemovalPolicy.DESTROY, 16 | emptyOnDelete: true // For develop environment 17 | }); 18 | 19 | if (imagePath) { 20 | const dockerImageAsset = new aws_ecr_assets.DockerImageAsset( 21 | this, 22 | "DockerImageAsset", 23 | { 24 | directory: imagePath, 25 | platform: aws_ecr_assets.Platform.LINUX_AMD64, 26 | } 27 | ); 28 | 29 | this.ecrDeployment = new ecrdeploy.ECRDeployment(this, `${id}ImageDeployment`, { 30 | src: new ecrdeploy.DockerImageName(dockerImageAsset.imageUri), 31 | dest: new ecrdeploy.DockerImageName(this.containerRepository.repositoryUriForTag('latest')), 32 | }) 33 | } 34 | 35 | 36 | new CfnOutput(this, 'RepositoryName', { 37 | exportName: `${id}ContainerRepositoryName`, 38 | value: this.containerRepository.repositoryName, 39 | }); 40 | new CfnOutput(this, 'RepositoryUri', { 41 | exportName: `${id}ContainerRepositoryUri`, 42 | value: this.containerRepository.repositoryUri, 43 | }); 44 | new CfnOutput(this, 'EcrRegion', { 45 | exportName: `${id}Region`, 46 | value: this.containerRepository.env.region, 47 | }); 48 | 49 | // CDK Nag Suppressions 50 | if (imagePath && this.ecrDeployment) { 51 | NagSuppressions.addResourceSuppressionsByPath(Stack.of(this), `/${Stack.of(this).stackName}/Custom::CDKECRDeploymentbd07c930edb94112a20f03f096f53666512MiB/ServiceRole/Resource`, [ 52 | { 53 | id: 'AwsSolutions-IAM4', 54 | reason: 'The construct uses managed policies for ECR deployment functionality', 55 | appliesTo: ['Policy::arn::iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'] 56 | } 57 | ]); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/README_group_access.md: -------------------------------------------------------------------------------- 1 | # グループベースのアクセス制御機能 2 | 3 | このドキュメントでは、EC2インスタンス管理ツールに実装されたグループベースのアクセス制御機能について説明します。 4 | 5 | ## 概要 6 | 7 | グループベースのアクセス制御機能により、以下のことが可能になります: 8 | 9 | 1. 各ユーザーに特定のグループID(`groupId`)を割り当てる 10 | 2. EC2インスタンスに特定のグループID(`GroupId`タグ)を割り当てる 11 | 3. ユーザーは自分のグループIDに一致するインスタンスのみを表示・操作できる 12 | 4. 管理者(admin)はすべてのインスタンスを表示・操作できる 13 | 14 | ## 実装内容 15 | 16 | ### 1. ユーザーモデルの拡張 17 | 18 | ユーザーモデルに`groupId`フィールドを追加しました。 19 | 20 | ```typescript 21 | export type User = { 22 | email: string; 23 | role: 'admin' | 'user'; 24 | groupId: string | null; // ユーザーが所属するグループのID、adminはnull可 25 | createdAt: string; 26 | }; 27 | ``` 28 | 29 | - `admin`ロールのユーザーは`groupId`が`null`の場合、すべてのグループのインスタンスにアクセスできます 30 | - `user`ロールのユーザーは自分の`groupId`に一致するインスタンスのみアクセスできます 31 | 32 | ### 2. ユーザー管理画面の拡張 33 | 34 | ユーザー管理画面に以下の機能を追加しました: 35 | 36 | - ユーザー追加時にグループIDを設定できるようになりました 37 | - ユーザー一覧にグループID列を追加しました 38 | - 一般ユーザーにはグループIDが必須になりました 39 | 40 | ### 3. インスタンス一覧・操作の制限 41 | 42 | - 管理者はすべてのインスタンスを表示・操作できます 43 | - 一般ユーザーは自分のグループIDに一致するインスタンスのみ表示・操作できます 44 | - 管理者のみインスタンスのグループID情報を表示できます 45 | 46 | ## セットアップ手順 47 | 48 | ### 1. 既存のユーザーデータの移行 49 | 50 | 既存のユーザーデータに`groupId`フィールドを追加するには、以下のスクリプトを実行します: 51 | 52 | ```bash 53 | cd usecases/webapp-java/infraops-console/scripts 54 | chmod +x migrate-users.sh 55 | ./migrate-users.sh 56 | ``` 57 | 58 | このスクリプトは以下の処理を行います: 59 | - 管理者ユーザーの`groupId`を`null`に設定 60 | - 一般ユーザーの`groupId`を`default-group`に設定 61 | 62 | ### 2. EC2インスタンスへのグループIDタグの追加 63 | 64 | EC2インスタンスに`GroupId`タグを追加するには、以下のスクリプトを実行します: 65 | 66 | ```bash 67 | cd usecases/webapp-java/infraops-console/scripts 68 | chmod +x add-group-tags.sh 69 | ./add-group-tags.sh 70 | ``` 71 | 72 | このスクリプトは以下の処理を行います: 73 | - `aws:cloudformation:stack-name`タグが`devSharedNetwork`のインスタンスを検索 74 | - 各インスタンスに`GroupId`タグを追加(デフォルト値は`default-group`) 75 | - 既に`GroupId`タグがあるインスタンスはスキップ 76 | 77 | ### 3. 新規ユーザーの作成 78 | 79 | 新規ユーザーを作成する場合は、管理者がユーザー管理画面から作成します。一般ユーザーを作成する場合は、グループIDの入力が必須です。 80 | 81 | ## グループIDの管理 82 | 83 | ### インスタンスのグループID変更 84 | 85 | 特定のインスタンスのグループIDを変更するには、AWS Management ConsoleまたはAWS CLIを使用します: 86 | 87 | ```bash 88 | aws ec2 create-tags \ 89 | --resources i-1234567890abcdef0 \ 90 | --tags Key=GroupId,Value=your-group-id \ 91 | --profile closedtemplate \ 92 | --region us-east-1 93 | ``` 94 | 95 | ### ユーザーのグループID変更 96 | 97 | ユーザーのグループIDを変更するには、管理者がユーザー管理画面から該当ユーザーを削除し、新しいグループIDで再作成します。 98 | 99 | ## 注意事項 100 | 101 | 1. グループIDは任意の文字列を使用できますが、一貫性のある命名規則を使用することをお勧めします 102 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/styles/global.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | @plugin '@digital-go-jp/tailwind-theme-plugin'; 4 | 5 | /* デジタル庁デザインシステムのカスタムスタイル */ 6 | @layer base { 7 | :root { 8 | /* 既存の変数は残しておく(Tailwindのテーマで上書きされる可能性あり) */ 9 | --spacing-1: 0.25rem; 10 | --spacing-2: 0.5rem; 11 | --spacing-3: 1rem; 12 | --spacing-4: 1.5rem; 13 | --spacing-5: 3rem; 14 | 15 | --border-radius: 0.25rem; 16 | --box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); 17 | } 18 | 19 | *, *::before, *::after { 20 | box-sizing: border-box; 21 | } 22 | 23 | body { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | } 28 | 29 | @layer components { 30 | /* ログイン・認証関連 */ 31 | .login-container, .verify-container { 32 | @apply max-w-md mx-auto my-20 p-6 bg-white rounded shadow; 33 | } 34 | 35 | .login-container h1, .verify-container h1 { 36 | @apply mt-0 mb-6 text-center; 37 | } 38 | 39 | .form-group { 40 | @apply mb-4; 41 | } 42 | 43 | .form-group label { 44 | @apply block mb-1 font-medium; 45 | } 46 | 47 | .request-new-code { 48 | @apply mt-6 text-center; 49 | } 50 | 51 | /* ダッシュボード */ 52 | .dashboard-container { 53 | @apply p-4; 54 | } 55 | 56 | .dashboard-header { 57 | @apply flex justify-between items-center mb-6 pb-4 border-b; 58 | } 59 | 60 | .user-info { 61 | @apply flex items-center gap-4; 62 | } 63 | 64 | .nav-links { 65 | @apply flex gap-2; 66 | } 67 | 68 | .nav-link { 69 | @apply px-2 py-1 rounded bg-gray-200; 70 | } 71 | 72 | /* テーブル */ 73 | table { 74 | @apply w-full mb-4 border-collapse; 75 | } 76 | 77 | th, td { 78 | @apply p-2 align-top border-t text-left; 79 | } 80 | 81 | thead th { 82 | @apply align-bottom border-b-2 bg-gray-100; 83 | } 84 | 85 | tbody tr:hover { 86 | @apply bg-gray-100; 87 | } 88 | 89 | /* インスタンスの状態表示 */ 90 | .status-running { 91 | @apply text-green-600; 92 | } 93 | 94 | .status-stopped { 95 | @apply text-gray-600; 96 | } 97 | 98 | .status-pending, .status-stopping { 99 | @apply text-yellow-600; 100 | } 101 | 102 | /* ユーザー管理 */ 103 | .users-container { 104 | @apply p-4; 105 | } 106 | 107 | .add-user-form { 108 | @apply max-w-xl mb-8 p-4 bg-white rounded shadow; 109 | } 110 | 111 | .delete-button { 112 | @apply bg-red-600 hover:bg-red-700; 113 | } 114 | 115 | /* エラー表示 */ 116 | .error { 117 | @apply text-red-600 mt-2; 118 | } 119 | 120 | /* コンテナ */ 121 | .container { 122 | @apply w-full max-w-6xl mx-auto px-4; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /usecases/webapp-react/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 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/webapp-stack.ts: -------------------------------------------------------------------------------- 1 | import { aws_ec2, aws_ecs, aws_iam, aws_rds, StackProps, Stack, aws_secretsmanager, aws_kms, aws_elasticloadbalancingv2 } from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | import { EcsAppBase } from './construct/ecs/ecs-app-base'; 4 | import { EcsAppService } from './construct/ecs/ecs-app-service'; 5 | 6 | interface WebappStackProps extends StackProps { 7 | dbCluster: aws_rds.DatabaseCluster | aws_rds.ServerlessCluster; 8 | dbSecretName: string; 9 | dbSecretEncryptionKeyArn: string; 10 | vpc: aws_ec2.Vpc; 11 | sharedVpc: aws_ec2.Vpc; 12 | tgw: aws_ec2.CfnTransitGateway; 13 | domainName: string; 14 | certificateArn: string; 15 | } 16 | 17 | export class WebappStack extends Stack { 18 | public readonly ecsService: aws_ecs.FargateService; 19 | public readonly containerName: string; 20 | public readonly alb: aws_elasticloadbalancingv2.ApplicationLoadBalancer; 21 | constructor(scope: Construct, id: string, props: WebappStackProps) { 22 | super(scope, id, props); 23 | 24 | const auroraSg = props.dbCluster.connections.securityGroups[0]; 25 | 26 | // Create ECS 27 | const ecsBase = new EcsAppBase(this, `WebappBase`, { 28 | vpc: props.vpc, 29 | domainName: props.domainName, 30 | certificateArn: props.certificateArn, 31 | }); 32 | this.alb = ecsBase.alb; 33 | // Allow bastions to access to an web application 34 | ecsBase.albSg.addIngressRule(aws_ec2.Peer.ipv4(props.sharedVpc.vpcCidrBlock), aws_ec2.Port.HTTPS); 35 | 36 | const ecsAppService = new EcsAppService(this, `WebappService`, { 37 | cluster: ecsBase.cluster, 38 | targetGroup: ecsBase.targetGroup, 39 | httpsTargetGroup: ecsBase.httpsTargetGroup, 40 | }); 41 | this.ecsService = ecsAppService.ecsService; 42 | this.containerName = ecsAppService.ecsContainer.containerName; 43 | 44 | // To avoid cyclic dependency 45 | const dbSecret = aws_secretsmanager.Secret.fromSecretNameV2(this, 'DbSecret', props.dbSecretName) 46 | const dbSecretEncryptionKey = aws_kms.Key.fromKeyArn(this, 'DbSecretEncryptionKey', props.dbSecretEncryptionKeyArn); 47 | dbSecret.grantRead(ecsAppService.executionRole); 48 | ecsAppService.ecsContainer.addSecret("DB_ENDPOINT", aws_ecs.Secret.fromSecretsManager(dbSecret, 'host')); 49 | ecsAppService.ecsContainer.addSecret("DB_USERNAME", aws_ecs.Secret.fromSecretsManager(dbSecret, 'username')); 50 | ecsAppService.ecsContainer.addSecret("DB_PASSWORD", aws_ecs.Secret.fromSecretsManager(dbSecret, 'password')); 51 | dbSecretEncryptionKey.grantEncryptDecrypt(ecsAppService.executionRole); 52 | ecsAppService.ecsService.connections.allowTo(auroraSg, aws_ec2.Port.tcp(5432)); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/storage-stack.ts: -------------------------------------------------------------------------------- 1 | import { CfnOutput, StackProps, Stack, aws_ec2, aws_rds, aws_ssm } from 'aws-cdk-lib'; 2 | import { DatabaseClusterEngine, AuroraPostgresEngineVersion } from 'aws-cdk-lib/aws-rds'; 3 | import { Construct } from 'constructs'; 4 | import { Aurora } from './construct/aurora/aurora'; 5 | 6 | interface StorageStackProps extends StackProps { 7 | vpc: aws_ec2.Vpc; 8 | sharedNetworkStackName: string; // 共有ネットワークスタックの名前 9 | windowsBastion: boolean; // Windowsバスティオンの有無 10 | linuxBastion: boolean; // Linuxバスティオンの有無 11 | } 12 | 13 | export class StorageStack extends Stack { 14 | public readonly dbCluster: aws_rds.DatabaseCluster | aws_rds.ServerlessCluster; 15 | public readonly dbEncryptionKeyArn: string; 16 | 17 | constructor(scope: Construct, id: string, props: StorageStackProps) { 18 | super(scope, id, props); 19 | 20 | // Create Aurora 21 | const aurora = new Aurora(this, 'Aurora', { 22 | enabledServerless: false, 23 | enabledProxy: false, // If you want to use Lambda Proxy. 24 | auroraEdition: DatabaseClusterEngine.auroraPostgres({ 25 | version: AuroraPostgresEngineVersion.VER_16_4, 26 | }), 27 | vpc: props.vpc, 28 | dbUserName: 'postgres', 29 | }); 30 | this.dbCluster = aurora.aurora; 31 | this.dbEncryptionKeyArn = aurora.databaseCredentials.encryptionKey!.keyArn; 32 | 33 | // SSMパラメータからバスティオンIPを取得してセキュリティグループルールを設定 34 | if (props.windowsBastion) { 35 | try { 36 | const windowsBastionIp = aws_ssm.StringParameter.valueForStringParameter( 37 | this, 38 | `/${props.sharedNetworkStackName}/WindowsBastionIp` 39 | ); 40 | 41 | this.dbCluster.connections.allowDefaultPortFrom( 42 | aws_ec2.Peer.ipv4(`${windowsBastionIp}/32`), 43 | 'Allow access from Windows Bastion' 44 | ); 45 | } catch (error) { 46 | console.warn('Windows Bastion IP parameter not found. Skipping security group rule.'); 47 | } 48 | } 49 | 50 | if (props.linuxBastion) { 51 | try { 52 | const linuxBastionIp = aws_ssm.StringParameter.valueForStringParameter( 53 | this, 54 | `/${props.sharedNetworkStackName}/LinuxBastionIp` 55 | ); 56 | 57 | this.dbCluster.connections.allowDefaultPortFrom( 58 | aws_ec2.Peer.ipv4(`${linuxBastionIp}/32`), 59 | 'Allow access from Linux Bastion' 60 | ); 61 | } catch (error) { 62 | console.warn('Linux Bastion IP parameter not found. Skipping security group rule.'); 63 | } 64 | } 65 | 66 | new CfnOutput(this, 'AuroraEdition', { 67 | exportName: 'AuroraEdition', 68 | value: 'postgresql', 69 | }); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/storage-stack.ts: -------------------------------------------------------------------------------- 1 | import { CfnOutput, StackProps, Stack, aws_ec2, aws_rds, aws_ssm } from 'aws-cdk-lib'; 2 | import { DatabaseClusterEngine, AuroraPostgresEngineVersion } from 'aws-cdk-lib/aws-rds'; 3 | import { Construct } from 'constructs'; 4 | import { Aurora } from './construct/aurora/aurora'; 5 | 6 | interface StorageStackProps extends StackProps { 7 | vpc: aws_ec2.Vpc; 8 | sharedNetworkStackName: string; // 共有ネットワークスタックの名前 9 | windowsBastion: boolean; // Windowsバスティオンの有無 10 | linuxBastion: boolean; // Linuxバスティオンの有無 11 | } 12 | 13 | export class StorageStack extends Stack { 14 | public readonly dbCluster: aws_rds.DatabaseCluster | aws_rds.ServerlessCluster; 15 | public readonly dbEncryptionKeyArn: string; 16 | public readonly dbProxy: aws_rds.DatabaseProxy; 17 | 18 | constructor(scope: Construct, id: string, props: StorageStackProps) { 19 | super(scope, id, props); 20 | 21 | // Create Aurora 22 | const aurora = new Aurora(this, 'Aurora', { 23 | enabledServerless: false, 24 | enabledProxy: true, 25 | auroraEdition: DatabaseClusterEngine.auroraPostgres({ 26 | version: AuroraPostgresEngineVersion.VER_16_4, 27 | }), 28 | vpc: props.vpc, 29 | dbUserName: 'postgres', 30 | }); 31 | this.dbCluster = aurora.aurora; 32 | this.dbEncryptionKeyArn = aurora.databaseCredentials.encryptionKey!.keyArn; 33 | this.dbProxy = aurora.proxy; 34 | 35 | // SSMパラメータからバスティオンIPを取得してセキュリティグループルールを設定 36 | if (props.windowsBastion) { 37 | try { 38 | const windowsBastionIp = aws_ssm.StringParameter.valueForStringParameter( 39 | this, 40 | `/${props.sharedNetworkStackName}/WindowsBastionIp` 41 | ); 42 | 43 | this.dbCluster.connections.allowDefaultPortFrom( 44 | aws_ec2.Peer.ipv4(`${windowsBastionIp}/32`), 45 | 'Allow access from Windows Bastion' 46 | ); 47 | } catch (error) { 48 | console.warn('Windows Bastion IP parameter not found. Skipping security group rule.'); 49 | } 50 | } 51 | 52 | if (props.linuxBastion) { 53 | try { 54 | const linuxBastionIp = aws_ssm.StringParameter.valueForStringParameter( 55 | this, 56 | `/${props.sharedNetworkStackName}/LinuxBastionIp` 57 | ); 58 | 59 | this.dbCluster.connections.allowDefaultPortFrom( 60 | aws_ec2.Peer.ipv4(`${linuxBastionIp}/32`), 61 | 'Allow access from Linux Bastion' 62 | ); 63 | } catch (error) { 64 | console.warn('Linux Bastion IP parameter not found. Skipping security group rule.'); 65 | } 66 | } 67 | 68 | new CfnOutput(this, 'AuroraEdition', { 69 | exportName: 'AuroraEdition', 70 | value: 'postgresql', 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Table.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface TableProps { 4 | children: React.ReactNode; 5 | className?: string; 6 | } 7 | 8 | export const Table: React.FC = ({ children, className = '' }) => { 9 | return ( 10 |
11 | 12 | {children} 13 |
14 |
15 | ); 16 | }; 17 | 18 | interface TableHeadProps { 19 | children: React.ReactNode; 20 | className?: string; 21 | } 22 | 23 | export const TableHead: React.FC = ({ children, className = '' }) => { 24 | return ( 25 | 26 | {children} 27 | 28 | ); 29 | }; 30 | 31 | interface TableBodyProps { 32 | children: React.ReactNode; 33 | className?: string; 34 | } 35 | 36 | export const TableBody: React.FC = ({ children, className = '' }) => { 37 | return ( 38 | 39 | {children} 40 | 41 | ); 42 | }; 43 | 44 | interface TableRowProps { 45 | children: React.ReactNode; 46 | className?: string; 47 | onClick?: () => void; 48 | } 49 | 50 | export const TableRow: React.FC = ({ children, className = '', onClick }) => { 51 | return ( 52 | 56 | {children} 57 | 58 | ); 59 | }; 60 | 61 | interface TableHeaderCellProps { 62 | children: React.ReactNode; 63 | className?: string; 64 | align?: 'left' | 'center' | 'right'; 65 | } 66 | 67 | export const TableHeaderCell: React.FC = ({ 68 | children, 69 | className = '', 70 | align = 'left' 71 | }) => { 72 | const alignClass = { 73 | left: 'text-left', 74 | center: 'text-center', 75 | right: 'text-right' 76 | }; 77 | 78 | return ( 79 | 83 | {children} 84 | 85 | ); 86 | }; 87 | 88 | interface TableCellProps { 89 | children: React.ReactNode; 90 | className?: string; 91 | align?: 'left' | 'center' | 'right'; 92 | colSpan?: number; 93 | } 94 | 95 | export const TableCell: React.FC = ({ 96 | children, 97 | className = '', 98 | align = 'left', 99 | colSpan 100 | }) => { 101 | const alignClass = { 102 | left: 'text-left', 103 | center: 'text-center', 104 | right: 'text-right' 105 | }; 106 | 107 | return ( 108 | 109 | {children} 110 | 111 | ); 112 | }; 113 | 114 | export default Table; 115 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/src/main/resources/templates/sampleappform.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | サンプルアプリ 5 | 6 | 7 | 8 | 9 | 10 |
15 |
16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 55 | 62 | 69 | 76 | 83 | 90 | 91 | 92 |
ID名前ジョブの成否
JOB0001JOB0002JOB0003JOB0004JOB0005
36 | 44 | 46 | 54 | 56 | 61 | 63 | 68 | 70 | 75 | 77 | 82 | 84 | 89 |
93 |
94 | 95 | 96 | -------------------------------------------------------------------------------- /usecases/webapp-java/lib/construct/ecs/ecs-app-base.ts: -------------------------------------------------------------------------------- 1 | import { 2 | aws_ec2, 3 | aws_ecs, 4 | aws_elasticloadbalancingv2, 5 | CfnOutput, 6 | RemovalPolicy, 7 | } from 'aws-cdk-lib'; 8 | import { Bucket } from '../s3/bucket'; 9 | import { Construct } from 'constructs'; 10 | 11 | export class EcsAppBase extends Construct { 12 | public readonly cluster: aws_ecs.Cluster; 13 | public readonly targetGroup: aws_elasticloadbalancingv2.ApplicationTargetGroup; 14 | public readonly httpsTargetGroup: aws_elasticloadbalancingv2.ApplicationTargetGroup; 15 | public readonly alb: aws_elasticloadbalancingv2.ApplicationLoadBalancer; 16 | public readonly albSg: aws_ec2.SecurityGroup; 17 | constructor( 18 | scope: Construct, 19 | id: string, 20 | props: { 21 | vpc: aws_ec2.IVpc; 22 | domainName: string; 23 | certificateArn: string; 24 | } 25 | ) { 26 | super(scope, id); 27 | 28 | // ECS Cluster 29 | this.cluster = new aws_ecs.Cluster(this, 'Cluster', { 30 | clusterName: `${id.split('-').slice(-1)[0]}Cluster`, 31 | vpc: props.vpc, 32 | containerInsightsV2: aws_ecs.ContainerInsights.ENABLED, 33 | enableFargateCapacityProviders: true, 34 | }); 35 | this.cluster.applyRemovalPolicy(RemovalPolicy.DESTROY); 36 | 37 | // Security Group for ALB 38 | const albSg = new aws_ec2.SecurityGroup(this, 'AlbSecurityGroup', { 39 | vpc: props.vpc, 40 | allowAllOutbound: true, 41 | }); 42 | this.albSg = albSg; 43 | 44 | // ALB 45 | this.alb = new aws_elasticloadbalancingv2.ApplicationLoadBalancer(this, 'Alb', { 46 | vpc: props.vpc, 47 | internetFacing: false, 48 | securityGroup: albSg, 49 | vpcSubnets: props.vpc.selectSubnets({ 50 | subnets: [...props.vpc.isolatedSubnets.filter(subnet => subnet.node.id.includes('workload'))], 51 | }), 52 | dropInvalidHeaderFields: true, 53 | }); 54 | const albLogBucket = new Bucket(this, 'AlbLogBucket', {versioned: false}); 55 | 56 | this.alb.logAccessLogs(albLogBucket.bucket, `${id}WebappAlbLog`); 57 | 58 | this.cluster.connections.allowFrom(this.alb, aws_ec2.Port.tcp(80)); 59 | 60 | const httpsListener = this.alb.addListener('WebappHttpsListener', { 61 | port: 443, 62 | protocol: aws_elasticloadbalancingv2.ApplicationProtocol.HTTPS, 63 | open: false, 64 | sslPolicy: aws_elasticloadbalancingv2.SslPolicy.TLS12, 65 | }); 66 | httpsListener.addCertificates(`${id}Certificate`, [ 67 | aws_elasticloadbalancingv2.ListenerCertificate.fromArn(props.certificateArn), 68 | ]); 69 | 70 | this.httpsTargetGroup = new aws_elasticloadbalancingv2.ApplicationTargetGroup( 71 | this, 72 | 'HttpsTarget', 73 | { 74 | targetType: aws_elasticloadbalancingv2.TargetType.IP, 75 | port: 8080, 76 | vpc: props.vpc, 77 | healthCheck: { path: '/', port: '8080' }, 78 | } 79 | ); 80 | httpsListener.addTargetGroups('HttpsTargetGroup', { 81 | targetGroups: [this.httpsTargetGroup], 82 | }); 83 | 84 | new CfnOutput(this, 'ClusterName', { 85 | exportName: `${id}ClusterName`, 86 | value: this.cluster.clusterName, 87 | }); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /usecases/webapp-java/webapp/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /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 | 38 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 39 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 40 | 41 | 42 | ## Finding contributions to work on 43 | 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. 44 | 45 | 46 | ## Code of Conduct 47 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 48 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 49 | opensource-codeofconduct@amazon.com with any additional questions or comments. 50 | 51 | 52 | ## Security issue notifications 53 | 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. 54 | 55 | 56 | ## Licensing 57 | 58 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 59 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/entry.server.tsx: -------------------------------------------------------------------------------- 1 | import { PassThrough } from "node:stream"; 2 | import { createReadableStreamFromReadable, type EntryContext } from "@remix-run/node"; 3 | import { RemixServer } from "@remix-run/react"; 4 | import isbot from "isbot"; 5 | import { renderToPipeableStream } from "react-dom/server"; 6 | 7 | const ABORT_DELAY = 5_000; 8 | 9 | export default function handleRequest( 10 | request: Request, 11 | responseStatusCode: number, 12 | responseHeaders: Headers, 13 | remixContext: EntryContext 14 | ) { 15 | return isbot(request.headers.get("user-agent")) 16 | ? handleBotRequest( 17 | request, 18 | responseStatusCode, 19 | responseHeaders, 20 | remixContext 21 | ) 22 | : handleBrowserRequest( 23 | request, 24 | responseStatusCode, 25 | responseHeaders, 26 | remixContext 27 | ); 28 | } 29 | 30 | function handleBotRequest( 31 | request: Request, 32 | responseStatusCode: number, 33 | responseHeaders: Headers, 34 | remixContext: EntryContext 35 | ) { 36 | return new Promise((resolve, reject) => { 37 | let didError = false; 38 | 39 | const { pipe, abort } = renderToPipeableStream( 40 | , 41 | { 42 | onAllReady() { 43 | const body = new PassThrough(); 44 | const stream = createReadableStreamFromReadable(body); 45 | 46 | responseHeaders.set("Content-Type", "text/html"); 47 | 48 | resolve( 49 | new Response(stream, { 50 | headers: responseHeaders, 51 | status: didError ? 500 : responseStatusCode, 52 | }) 53 | ); 54 | 55 | pipe(body); 56 | }, 57 | onShellError(error: unknown) { 58 | reject(error); 59 | }, 60 | onError(error: unknown) { 61 | didError = true; 62 | 63 | console.error(error); 64 | }, 65 | } 66 | ); 67 | 68 | setTimeout(abort, ABORT_DELAY); 69 | }); 70 | } 71 | 72 | function handleBrowserRequest( 73 | request: Request, 74 | responseStatusCode: number, 75 | responseHeaders: Headers, 76 | remixContext: EntryContext 77 | ) { 78 | return new Promise((resolve, reject) => { 79 | let didError = false; 80 | 81 | const { pipe, abort } = renderToPipeableStream( 82 | , 83 | { 84 | onShellReady() { 85 | const body = new PassThrough(); 86 | const stream = createReadableStreamFromReadable(body); 87 | 88 | responseHeaders.set("Content-Type", "text/html"); 89 | 90 | resolve( 91 | new Response(stream, { 92 | headers: responseHeaders, 93 | status: didError ? 500 : responseStatusCode, 94 | }) 95 | ); 96 | 97 | pipe(body); 98 | }, 99 | onShellError(error: unknown) { 100 | reject(error); 101 | }, 102 | onError(error: unknown) { 103 | didError = true; 104 | 105 | console.error(error); 106 | }, 107 | } 108 | ); 109 | 110 | setTimeout(abort, ABORT_DELAY); 111 | }); 112 | } 113 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/serverlessapp-stack.ts: -------------------------------------------------------------------------------- 1 | import { 2 | aws_ec2, 3 | aws_s3, 4 | StackProps, 5 | Stack, 6 | aws_elasticloadbalancingv2, 7 | } from 'aws-cdk-lib'; 8 | import { Construct } from 'constructs'; 9 | import { ServerlessApp } from './construct/serverless/serverless-app'; 10 | import { NagSuppressions } from 'cdk-nag'; 11 | import { DbInitLambda } from './construct/aurora/dbinitlambda'; 12 | import { ApplicationLoadBalancer } from './construct/network/alb'; 13 | 14 | interface ServerlessappStackProps extends StackProps { 15 | dbSecretName: string; 16 | dbSecretArn: string; 17 | dbSecurityGroupId: string; 18 | dbSecretEncryptionKeyArn: string; 19 | dbEdition: string; 20 | dbProxyEndpoint: string; 21 | dbProxyArn: string; 22 | vpc: aws_ec2.Vpc; 23 | windowsBastion: boolean; 24 | linuxBastion: boolean; 25 | domainName: string; 26 | certificateArn: string; 27 | spaS3InterfaceEndpoint: aws_ec2.InterfaceVpcEndpoint; 28 | privateApiVpcEndpoint: aws_ec2.InterfaceVpcEndpoint; 29 | sgForApiGwVpce: aws_ec2.SecurityGroup; 30 | alb: ApplicationLoadBalancer; 31 | sgForAlb: aws_ec2.SecurityGroup; 32 | } 33 | 34 | export class ServerlessappStack extends Stack { 35 | public readonly alb: aws_elasticloadbalancingv2.ApplicationLoadBalancer; 36 | public readonly spaHostingBucket: aws_s3.Bucket; 37 | constructor(scope: Construct, id: string, props: ServerlessappStackProps) { 38 | super(scope, id, props); 39 | 40 | // Import vpc 41 | const vpc = props.vpc 42 | 43 | // Security Group for Lambda 44 | const sgForDb = aws_ec2.SecurityGroup.fromSecurityGroupId( 45 | this, 46 | 'DbSecurityGroup', 47 | props.dbSecurityGroupId 48 | ); 49 | 50 | // Create ServerlessApp (Application Resources Layer) 51 | const serverlessApp = new ServerlessApp(this, `ServerlessApp`, { 52 | vpc: vpc, 53 | domainName: props.domainName, 54 | certificateArn: props.certificateArn, 55 | dbSecretName: props.dbSecretName, 56 | dbSecretArn: props.dbSecretArn, 57 | dbSecurityGroupId: props.dbSecurityGroupId, 58 | dbSecretEncryptionKeyArn: props.dbSecretEncryptionKeyArn, 59 | dbProxyEndpoint: props.dbProxyEndpoint, 60 | dbProxyArn: props.dbProxyArn, 61 | spaS3InterfaceEndpoint: props.spaS3InterfaceEndpoint, 62 | privateApiVpcEndpoint: props.privateApiVpcEndpoint, 63 | vpcEndpointSecurityGroup: props.sgForApiGwVpce 64 | }); 65 | this.spaHostingBucket = serverlessApp.webappS3bucket; 66 | this.alb = props.alb.alb; 67 | 68 | // === Integration & Orchestration Layer === 69 | 70 | // Security Group Rules 71 | if (props.dbEdition == 'mysql') { 72 | sgForDb.addIngressRule(serverlessApp.sgForLambda, aws_ec2.Port.tcp(3306)); 73 | } else { 74 | sgForDb.addIngressRule(serverlessApp.sgForLambda, aws_ec2.Port.tcp(5432)); 75 | } 76 | 77 | // Allow ALB to communicate with API Gateway VPC Endpoint 78 | serverlessApp.apiGw.vpcEndpointSecurityGroup.addIngressRule(props.sgForAlb, aws_ec2.Port.tcp(443)); 79 | 80 | new DbInitLambda(this, 'DBInitLambdaConstruct', { 81 | vpc: vpc, 82 | sgForLambda: serverlessApp.sgForLambda, 83 | dbSecretName: props.dbSecretName, 84 | dbSecretArn: props.dbSecretArn, 85 | dbSecretEncryptionKeyArn: props.dbSecretEncryptionKeyArn, 86 | dbProxyEndpoint: props.dbProxyEndpoint, 87 | dbProxyArn: props.dbProxyArn, 88 | }); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /usecases/webapp-react/lib/construct/codepipeline/codepipeline-webapp-react.ts: -------------------------------------------------------------------------------- 1 | import { 2 | aws_codebuild, 3 | aws_codecommit, 4 | aws_codepipeline, 5 | aws_codepipeline_actions, 6 | aws_iam, 7 | aws_kms, 8 | aws_logs, 9 | aws_s3, 10 | } from 'aws-cdk-lib'; 11 | import { NagSuppressions } from 'cdk-nag'; 12 | import { Construct } from 'constructs'; 13 | import { EncryptionKey } from '../kms/key'; 14 | 15 | export class CodePipelineWebappReact extends Construct { 16 | constructor( 17 | scope: Construct, 18 | id: string, 19 | props: { 20 | codeCommitRepository: aws_codecommit.IRepository; 21 | s3bucket: aws_s3.Bucket; 22 | domainName: string; 23 | } 24 | ) { 25 | super(scope, id); 26 | 27 | const pipeline = new aws_codepipeline.Pipeline(this, 'WebappPipeline', { 28 | crossAccountKeys: true, 29 | enableKeyRotation: true, 30 | }); 31 | 32 | // Source stage 33 | const sourceOutput = new aws_codepipeline.Artifact('SourceArtifact'); 34 | const sourceAction = new aws_codepipeline_actions.CodeCommitSourceAction({ 35 | actionName: 'GetSourceCodeFromCodeCommit', 36 | repository: props.codeCommitRepository, 37 | branch: 'develop', 38 | output: sourceOutput, 39 | trigger: aws_codepipeline_actions.CodeCommitTrigger.POLL, 40 | }); 41 | 42 | pipeline.addStage({ 43 | stageName: 'Source', 44 | actions: [sourceAction], 45 | }); 46 | 47 | // Build stage 48 | const buildLogGroup = new aws_logs.LogGroup(this, 'BuildLogGroup', { 49 | encryptionKey: new EncryptionKey(this, 'BuildLogGroupEncryptionKey', { 50 | servicePrincipals: [new aws_iam.ServicePrincipal('logs.amazonaws.com')], 51 | }).encryptionKey, 52 | }); 53 | 54 | const buildActionProject = new aws_codebuild.PipelineProject(this, 'BuildProject', { 55 | buildSpec: aws_codebuild.BuildSpec.fromSourceFilename('buildspec.yaml'), 56 | encryptionKey: new aws_kms.Key(this, 'BuildActionProjectKey', { enableKeyRotation: true }), 57 | logging: { 58 | cloudWatch: { 59 | enabled: true, 60 | logGroup: buildLogGroup, 61 | }, 62 | }, 63 | environment: { 64 | privileged: true, 65 | buildImage: aws_codebuild.LinuxBuildImage.AMAZON_LINUX_2023_5, 66 | }, 67 | environmentVariables: { 68 | DOMAIN_NAME: { 69 | value: props.domainName, 70 | }, 71 | }, 72 | }); 73 | 74 | const buildOutput = new aws_codepipeline.Artifact(); 75 | const buildAction = new aws_codepipeline_actions.CodeBuildAction({ 76 | actionName: 'BuildReactOnCodeBuild', 77 | project: buildActionProject, 78 | input: sourceOutput, 79 | outputs: [buildOutput], 80 | }); 81 | pipeline.addStage({ 82 | stageName: 'Build', 83 | actions: [buildAction], 84 | }); 85 | 86 | // Deploy stage 87 | const deployAction = new aws_codepipeline_actions.S3DeployAction({ 88 | actionName: 'DeployBuildFileToS3', 89 | input: buildOutput, 90 | bucket: props.s3bucket, 91 | }); 92 | 93 | pipeline.addStage({ 94 | stageName: 'Deploy', 95 | actions: [deployAction], 96 | }); 97 | 98 | // cdk-nag suppressions 99 | NagSuppressions.addResourceSuppressions(buildActionProject, [ 100 | { 101 | id: 'AwsSolutions-CB3', 102 | reason: 'To build docker image on CodeBuild host.', 103 | }, 104 | ]); 105 | NagSuppressions.addResourceSuppressions(pipeline.artifactBucket, [ 106 | { 107 | id: 'AwsSolutions-S1', 108 | reason: "This bucket doesn't store sensitive data", 109 | }, 110 | ]); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/routes/login.tsx: -------------------------------------------------------------------------------- 1 | import { Form, useActionData, useNavigation } from '@remix-run/react'; 2 | import { redirect } from '@remix-run/node'; 3 | import type { ActionFunctionArgs, LoaderFunctionArgs } from '@remix-run/node'; 4 | import { authenticator } from '../utils/auth.server'; 5 | import { getSession } from '../utils/session.server'; 6 | import { Button, Input, Label, RequirementBadge, UniversalLink } from '../components'; 7 | 8 | export async function loader({ request }: LoaderFunctionArgs) { 9 | const session = await getSession(request.headers.get('Cookie')); 10 | const idToken = session.get('idToken'); 11 | if (idToken) return redirect('/dashboard'); 12 | 13 | return null; 14 | } 15 | 16 | export async function action({ request }: ActionFunctionArgs) { 17 | try { 18 | console.log('login action'); 19 | // Use form authentication 20 | await authenticator.authenticate('form', request); 21 | 22 | } catch (error: any) { 23 | 24 | // Return error response 25 | if (error instanceof Response) { 26 | return error; 27 | } 28 | 29 | console.log('error', error); 30 | 31 | let errorMessage = 'ログインに失敗しました。メールアドレスとパスワードを確認してください。'; 32 | 33 | if (error.message) { 34 | errorMessage = error.message; 35 | } 36 | 37 | return { 38 | error: errorMessage, 39 | }; 40 | } 41 | } 42 | 43 | export default function Login() { 44 | console.log('login'); 45 | const actionData = useActionData(); 46 | const navigation = useNavigation(); 47 | const isSubmitting = (navigation.state === 'submitting' || navigation.state === 'loading') && 48 | navigation.formMethod === 'POST'; 49 | console.log('Login actionData:', actionData); 50 | console.log('Navigation state:', navigation.state, 'isSubmitting:', isSubmitting); 51 | 52 | return ( 53 |
54 |

管理ツール

55 |

ログイン情報を入力してください

56 | 57 |
58 | {actionData && 'error' in actionData && ( 59 |
{actionData.error}
60 | )} 61 | 62 |
63 | 69 | 79 | 80 | 86 | 97 |
98 | 99 | 107 | 108 |
109 | 110 | パスワードを忘れた場合 111 | 112 |
113 |
114 |
115 | ); 116 | } 117 | -------------------------------------------------------------------------------- /usecases/infraops-console/webapp/app/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { type ComponentProps, forwardRef } from 'react'; 2 | import { Slot } from '.'; 3 | 4 | export type ButtonVariant = 'solid-fill' | 'outline' | 'text'; 5 | export type ButtonSize = 'lg' | 'md' | 'sm' | 'xs'; 6 | 7 | export const buttonBaseStyle = ` 8 | underline-offset-[calc(3/16*1rem)] 9 | focus-visible:outline focus-visible:outline-4 focus-visible:outline-black focus-visible:outline-offset-[calc(2/16*1rem)] focus-visible:ring-[calc(2/16*1rem)] focus-visible:ring-yellow-300 10 | aria-disabled:pointer-events-none aria-disabled:forced-colors:border-[GrayText] aria-disabled:forced-colors:text-[GrayText] 11 | disabled:pointer-events-none disabled:forced-colors:border-[GrayText] disabled:forced-colors:text-[GrayText] 12 | `; 13 | 14 | export const buttonVariantStyle: { [key in ButtonVariant]: string } = { 15 | 'solid-fill': ` 16 | border-4 17 | border-double 18 | border-transparent 19 | bg-blue-900 20 | text-white 21 | hover:bg-blue-1000 22 | hover:underline 23 | active:bg-blue-1200 24 | active:underline 25 | aria-disabled:bg-solid-gray-300 26 | aria-disabled:text-solid-gray-50 27 | disabled:bg-solid-gray-300 28 | disabled:text-solid-gray-50 29 | `, 30 | outline: ` 31 | border 32 | border-current 33 | bg-white 34 | text-blue-900 35 | hover:bg-blue-200 36 | hover:text-blue-1000 37 | hover:underline 38 | active:bg-blue-300 39 | active:text-blue-1200 40 | active:underline 41 | aria-disabled:bg-white 42 | aria-disabled:text-solid-gray-300 43 | disabled:bg-white 44 | disabled:text-solid-gray-300 45 | `, 46 | text: ` 47 | text-blue-900 48 | underline 49 | hover:bg-blue-50 50 | hover:text-blue-1000 51 | hover:decoration-[calc(3/16*1rem)] 52 | active:bg-blue-100 53 | active:text-blue-1200 54 | focus-visible:bg-yellow-300 55 | aria-disabled:bg-transparent 56 | aria-disabled:focus-visible:bg-yellow-300 57 | aria-disabled:text-solid-gray-300 58 | disabled:bg-transparent 59 | disabled:focus-visible:bg-yellow-300 60 | disabled:text-solid-gray-300 61 | `, 62 | }; 63 | 64 | export const buttonSizeStyle: { [key in ButtonSize]: string } = { 65 | lg: 'min-w-[calc(136/16*1rem)] min-h-14 rounded-8 px-4 py-3 text-oln-16B-100', 66 | md: 'min-w-24 min-h-12 rounded-8 px-4 py-2 text-oln-16B-100', 67 | sm: 'relative min-w-20 min-h-9 rounded-6 px-3 py-0.5 text-oln-16B-100 after:absolute after:inset-x-0 after:-inset-y-full after:m-auto after:h-[44px]', 68 | xs: 'relative min-w-18 min-h-7 rounded-4 px-2 py-0.5 text-oln-14B-100 after:absolute after:inset-x-0 after:-inset-y-full after:m-auto after:h-[44px]', 69 | }; 70 | 71 | export type ButtonProps = { 72 | className?: string; 73 | variant?: ButtonVariant; 74 | size: ButtonSize; 75 | } & ( 76 | | ({ asChild?: false } & ComponentProps<'button'>) 77 | | { asChild: true; children: React.ReactNode } 78 | ); 79 | 80 | export const Button = forwardRef((props, ref) => { 81 | const { asChild, children, className, variant, size, ...rest } = props; 82 | 83 | const classNames = `${buttonBaseStyle} ${buttonSizeStyle[size]} ${ 84 | variant ? buttonVariantStyle[variant] : '' 85 | } ${className ?? ''}`; 86 | 87 | if (asChild) { 88 | return ( 89 | 90 | {children} 91 | 92 | ); 93 | } 94 | 95 | const handleDisabled = (e: React.MouseEvent) => { 96 | e.preventDefault(); 97 | }; 98 | 99 | return ( 100 | 108 | ); 109 | }); 110 | 111 | export default Button; 112 | -------------------------------------------------------------------------------- /usecases/webapp-java/scripts/create_certificate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # 使用方法の説明 5 | if [ $# -gt 1 ]; then 6 | echo "使用方法: $0 [AWSプロファイル]" 7 | echo "例: $0 myprofile" 8 | echo "注: AWSプロファイルを指定しない場合は、defaultプロファイルが使用されます。" 9 | exit 1 10 | fi 11 | 12 | # parameter.tsからステージ名とドメイン名を取得 13 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 14 | WORKING_DIR="${SCRIPT_DIR}/../" 15 | PARAMETER_FILE="${WORKING_DIR}/parameter.ts" 16 | 17 | # Node.jsを使用してparameter.tsから値を抽出 18 | STAGE_NAME=$(node -e " 19 | const fs = require('fs'); 20 | const content = fs.readFileSync('${PARAMETER_FILE}', 'utf8'); 21 | const deployEnvMatch = content.match(/deployEnv: ['\"]([^'\"]+)['\"],/); 22 | if (deployEnvMatch && deployEnvMatch[1]) { 23 | console.log(deployEnvMatch[1]); 24 | } 25 | ") 26 | 27 | DOMAIN_NAME=$(node -e " 28 | const fs = require('fs'); 29 | const content = fs.readFileSync('${PARAMETER_FILE}', 'utf8'); 30 | const domainNameMatch = content.match(/domainName: ['\"]([^'\"]+)['\"],/); 31 | if (domainNameMatch && domainNameMatch[1]) { 32 | console.log(domainNameMatch[1]); 33 | } 34 | ") 35 | 36 | # 値が取得できなかった場合はエラー 37 | if [ -z "$STAGE_NAME" ] || [ -z "$DOMAIN_NAME" ]; then 38 | echo "エラー: parameter.tsからステージ名またはドメイン名を取得できませんでした。" 39 | exit 1 40 | fi 41 | 42 | # AWSプロファイルは引数から取得(オプション) 43 | AWS_PROFILE=${1:-default} 44 | 45 | # ディレクトリの設定 46 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 47 | WORKING_DIR="${SCRIPT_DIR}/../" 48 | SSL_DIR="${WORKING_DIR}/ssl" 49 | 50 | # SSLディレクトリが存在しない場合は作成 51 | mkdir -p ${SSL_DIR} 52 | 53 | echo "ステージ名: ${STAGE_NAME}" 54 | echo "ドメイン名: ${DOMAIN_NAME}" 55 | echo "AWSプロファイル: ${AWS_PROFILE}" 56 | echo "作業ディレクトリ: ${WORKING_DIR}" 57 | echo "SSL証明書ディレクトリ: ${SSL_DIR}" 58 | 59 | # ルート証明書の秘密鍵を作成 60 | echo "ルート証明書の秘密鍵を作成中..." 61 | openssl genpkey -out ${SSL_DIR}/ca.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pass pass:template@pp1234 62 | 63 | # ルート証明書を作成 64 | echo "ルート証明書を作成中..." 65 | openssl req -new -x509 -key ${SSL_DIR}/ca.key -days 3650 -out ${SSL_DIR}/ca.pem -passin pass:template@pp1234 -subj "/C=JP/ST=Tokyo/O=Template Sample App/CN=Template Common Name" 66 | 67 | # 中間証明書の秘密鍵を作成 68 | echo "中間証明書の秘密鍵を作成中..." 69 | openssl genpkey -out ${SSL_DIR}/ica.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pass pass:template@pp1234 70 | 71 | # 中間証明書のCSRを作成 72 | echo "中間証明書のCSRを作成中..." 73 | openssl req -new -key ${SSL_DIR}/ica.key -sha256 -outform PEM -keyform PEM -out ${SSL_DIR}/ica.csr -subj "/C=JP/ST=Tokyo/O=Template Sample App/CN=Template Common Name" 74 | 75 | # 中間証明書を作成 76 | echo "中間証明書を作成中..." 77 | openssl x509 -extfile ${SSL_DIR}/openssl_sign_inca.cnf -req -in ${SSL_DIR}/ica.csr -sha256 -CA ${SSL_DIR}/ca.pem -CAkey ${SSL_DIR}/ca.key -set_serial 01 -extensions v3_ca -days 3650 -out ${SSL_DIR}/ica.pem 78 | 79 | # サーバー証明書の秘密鍵を作成 80 | echo "サーバー証明書の秘密鍵を作成中..." 81 | openssl genrsa 2048 > ${SSL_DIR}/server.key 82 | 83 | # サーバー証明書のCSRを作成 84 | echo "サーバー証明書のCSRを作成中..." 85 | openssl req -new -key ${SSL_DIR}/server.key -outform PEM -keyform PEM -sha256 -out ${SSL_DIR}/server.csr -subj "/C=JP/ST=Tokyo/O=Template Sample App/CN=*.${DOMAIN_NAME}" 86 | 87 | # サーバー証明書を作成 88 | echo "サーバー証明書を作成中..." 89 | openssl x509 -req -in ${SSL_DIR}/server.csr -sha256 -CA ${SSL_DIR}/ica.pem -CAkey ${SSL_DIR}/ica.key -set_serial 01 -days 3650 -out ${SSL_DIR}/server.pem 90 | 91 | # 証明書をACMにインポート 92 | echo "証明書をACMにインポート中..." 93 | PROFILE_OPTION="" 94 | if [ "${AWS_PROFILE}" != "default" ]; then 95 | PROFILE_OPTION="--profile ${AWS_PROFILE}" 96 | fi 97 | 98 | # 出力先ディレクトリを作成 99 | OUTPUT_DIR="${SCRIPT_DIR}/../config" 100 | mkdir -p ${OUTPUT_DIR} 101 | 102 | # 証明書をインポートしてARNをJSONファイルに保存 103 | aws acm import-certificate --certificate fileb://${SSL_DIR}/server.pem --certificate-chain fileb://${SSL_DIR}/ica.pem --private-key fileb://${SSL_DIR}/server.key ${PROFILE_OPTION} | tee ${OUTPUT_DIR}/certificate_arn.json 104 | 105 | echo "証明書のARN: $(cat ${OUTPUT_DIR}/certificate_arn.json | grep CertificateArn | cut -d'"' -f4)" 106 | echo "証明書の作成とインポートが完了しました。" 107 | echo "証明書ARNは ${OUTPUT_DIR}/certificate_arn.json に保存されました。" 108 | --------------------------------------------------------------------------------