UNPKG

@nswhp/golden-path

Version:
560 lines (513 loc) 18.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAwsCdkPipeline = exports.getAwsCdkBinCdkProjectTs = exports.getAwsCdkInfrastructureTs = exports.getAwsCdkConfigFactoryTs = exports.getAwsCdkStackTs = exports.getAwsCdkConstantsTs = exports.getAwsCdkTaggingTs = exports.getAwsCdkExampleTest = exports.getAwsCdkEslintIgnore = exports.getAwsCdkEslintConfig = exports.getAwsCdkGitIgnore = exports.getAwsCdkPrettier = exports.getAwsCdkConfig = exports.getAwsCdkJestConfig = exports.getAwsCdkReadme = exports.getAwsCdkTsconfig = exports.getAwsCdkPackageJson = void 0; const function_util_1 = require("../../utils/function.util"); const getAwsCdkPackageJson = (projectName) => `{ "name": "${projectName}", "version": "1.0.0", "description": "", "keywords": [], "homepage": "https://github.com/nsw-health-pathology/goldpath-aws-cdk-typescript#readme", "scripts": { "lint": "eslint -c .eslintrc.json", "build": "npm run tsc --", "test": "jest", "deploy": "cdk deploy", "diff": "cdk diff", "ci": "npm run lint && npm run test", "synth": "cdk synth", "license-check": "license-checker --summary --production --excludePrivatePackages --onlyAllow Apache-2.0;MIT;ISC;BSD;BSD-2-Clause;BSD-3-Clause;MIT*;CC0-1.0;CC-BY-3.0;CC-BY-4.0;BSD*;0BSD;Public Domain;AFLv2.1;" }, "devDependencies": { "@types/jest": "^29.2.5", "@types/node": "18.11.18", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", "aws-cdk": "^2.72.0", "eslint": "^8.37.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.2.0", "prettier": "^2.8.7", "jest": "^29.3.1", "license-checker": "^25.0.1", "ts-jest": "^29.0.3", "ts-node": "^10.0.0", "typescript": "^4.9.3" }, "dependencies": { "aws-cdk-lib": "^2.72.0", "constructs": "^10.1.301" } }`; exports.getAwsCdkPackageJson = getAwsCdkPackageJson; const getAwsCdkTsconfig = () => `{ "compilerOptions": { "alwaysStrict": true, "declaration": true, "experimentalDecorators": true, "inlineSourceMap": true, "inlineSources": true, "lib": [ "es2018" ], "module": "CommonJS", "noEmitOnError": true, "noFallthroughCasesInSwitch": true, "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true, "resolveJsonModule": true, "strict": true, "skipLibCheck": true, "strictNullChecks": true, "outDir": "./dist", "strictPropertyInitialization": true, "stripInternal": true, "target": "ES2018", "rootDir": ".", "typeRoots": [ "./node_modules/@types" ] }, "include": [ "**/*.ts" ], "exclude": [ "node_modules", "cdk.out" ] }`; exports.getAwsCdkTsconfig = getAwsCdkTsconfig; const getAwsCdkReadme = (projectName, tagging) => `# ${projectName} Generated by the @nswhp/golden-path https://www.npmjs.com/package/@nswhp/golden-path ## Build Process \`\`\` \$ npm run synth -- Stack-np \`\`\` \`\`\` \$ npm run diff -- Stack-np \`\`\` \`\`\` \$ npm run deploy -- Stack-np \`\`\` ## Deployment Create a pipeline in azure devops and reference the .pipelines/azure-pipeline.yml in this repo. Create variable groups in pipelines -> library -> variable groups for each environment you have eg ${projectName}-pd, ${projectName}-np ## Tags - Service Offering ${tagging.serviceOffering} - Support Team ${tagging.support} - Support Contact ${tagging.supportContact} - Reason ${tagging.reason} - Cost Center ${tagging.costCenter} `; exports.getAwsCdkReadme = getAwsCdkReadme; const getAwsCdkJestConfig = () => `module.exports = { roots: ['<rootDir>/test'], testMatch: ['**/*.test.ts'], transform: { '^.+\\.tsx?$': 'ts-jest' } }; `; exports.getAwsCdkJestConfig = getAwsCdkJestConfig; const getAwsCdkConfig = (projectName) => `{ "app": "npx ts-node --prefer-ts-exts bin/${projectName}.ts" } `; exports.getAwsCdkConfig = getAwsCdkConfig; const getAwsCdkPrettier = () => `{ "semi": true, "singleQuote": true, "trailingComma": "all", "printWidth": 100, "bracketSpacing": true, "useTabs": false } `; exports.getAwsCdkPrettier = getAwsCdkPrettier; const getAwsCdkGitIgnore = () => `*.js !jest.config.js *.d.ts node_modules # CDK asset staging directory .cdk.staging cdk.out `; exports.getAwsCdkGitIgnore = getAwsCdkGitIgnore; const getAwsCdkEslintConfig = () => `{ "parser": "@typescript-eslint/parser", "plugins": [ "@typescript-eslint", "prettier" ], "extends": [ "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended" ], "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "ignorePatterns": ["**/*.d.ts", "**/*.js"], "rules": { "require-jsdoc": [ "warn", { "require": { "FunctionDeclaration": true, "MethodDefinition": false, "ClassDeclaration": false, "ArrowFunctionExpression": false, "FunctionExpression": false } } ], "object-shorthand": [ "error", "never" ], "semi": [ "error", "always" ], "quotes": [ "error", "single", "avoid-escape" ], "comma-dangle": [ "error", "always-multiline" ], "curly": [ "error", "multi-line" ], "prefer-const": 1, "no-var": 1, "no-fallthrough": 1, "no-unused-vars": "off", "prettier/prettier": ["off", { "singleQuote": true }], "no-console": [ "warn", { "allow": [ "info", "debug", "error" ] } ], "@typescript-eslint/no-unused-vars": [ "warn", { "argsIgnorePattern": "^_" } ], "@typescript-eslint/camelcase": 0, "@typescript-eslint/no-use-before-define": 0, "@typescript-eslint/explicit-function-return-type": [ "error", { "allowTypedFunctionExpressions": true } ], "sort-imports": [ "error", { "ignoreCase": true, "ignoreDeclarationSort": false, "ignoreMemberSort": false, "memberSyntaxSortOrder": [ "none", "all", "multiple", "single" ] } ] }, "overrides": [] } `; exports.getAwsCdkEslintConfig = getAwsCdkEslintConfig; const getAwsCdkEslintIgnore = () => `# build artifacts dist/* coverage/* # custom definition files .cache node_modules **/*.d.ts `; exports.getAwsCdkEslintIgnore = getAwsCdkEslintIgnore; const getAwsCdkExampleTest = () => `test('Infrastructure Created', () => { console.info('Add Test Cases'); }); `; exports.getAwsCdkExampleTest = getAwsCdkExampleTest; const getAwsCdkTaggingTs = () => `import { EnvironmentConfig } from './config/config.factory'; import { NswhpEnvironment } from './constants'; import { Tag } from 'aws-cdk-lib'; export class TaggingService { private readonly costCenter: string; private readonly serviceOffering: string; private readonly reason: string; private readonly environment: NswhpEnvironment; private readonly support: string; private readonly supportContact: string; /** * Constructor to accept the common team parameters that all resources will have */ constructor(envConfig: EnvironmentConfig) { this.support = envConfig.support; this.supportContact = envConfig.supportContact; this.costCenter = envConfig.costCenter; this.environment = envConfig.environmentNameShort; this.serviceOffering = envConfig.serviceOffering; this.reason = envConfig.reason; } /** * Produce our standardised tagging for resource infrastructure in line with * https://confluence.pathology.health.nsw.gov.au/display/EA/Logical+Data+Centre+Reference+Architecture#LogicalDataCentreReferenceArchitecture-ResourceTaggingStandards */ static generate(resourceName: string): Tag[] { return [ new Tag('Name', resourceName), ]; } /** * Tags that are considered static for the team-environment. All cloud resources for this team * should receive these same tags. * @returns */ staticTags(): Tag[] { return [ new Tag('CostCenter', this.costCenter), new Tag('Environment', this.environment), new Tag('ServiceOffering', this.serviceOffering), new Tag('Support', this.support), new Tag('SupportContact', this.supportContact), new Tag('Reason', this.reason), ]; } } `; exports.getAwsCdkTaggingTs = getAwsCdkTaggingTs; const getAwsCdkConstantsTs = (accountDetails) => `export type NswhpEnvironment = 'develop' | 'test' | 'qa' | 'training' | 'np' | 'pd' | 'nonprod' | 'preprod' | 'prod'; export const C = { AWS_REGION: 'ap-southeast-2', SOLUTION: 'SOLUTION', AWS_ACCOUNTS: { NP: '${accountDetails .some(account => account.env.toLocaleLowerCase() === 'np') ? accountDetails.find(account => account.env.toLocaleLowerCase() === 'np').accountId : ''}', QA: '${accountDetails .some(account => account.env.toLocaleLowerCase() === 'qa') ? accountDetails.find(account => account.env.toLocaleLowerCase() === 'qa').accountId : ''}', PD: '${accountDetails .some(account => account.env.toLocaleLowerCase() === 'pd') ? accountDetails.find(account => account.env.toLocaleLowerCase() === 'pd').accountId : ''}', }, };`; exports.getAwsCdkConstantsTs = getAwsCdkConstantsTs; const getAwsCdkStackTs = (env) => `import * as cdk from 'aws-cdk-lib'; import { EnvironmentConfig, getEnvironment } from './config/config.factory'; import { Construct } from 'constructs'; import { Infrastructure } from './config/infrastructure'; export class ${(0, function_util_1.toTitleCase)(env)}Stack extends cdk.Stack { protected readonly config: EnvironmentConfig; constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); this.config = getEnvironment(props?.env?.account || 'N/A'); // Here we register the team infrastructure in the stack new Infrastructure(this, this.config); } } `; exports.getAwsCdkStackTs = getAwsCdkStackTs; const getAwsCdkConfigFactoryTs = (npDetails, qaDetails, pdDetails, tagging, projectName) => `import { C } from '../constants'; export interface EnvironmentConfig { accountNameQualified: string; accountNameShort: string; accountId: string; environmentNameLong: 'nonprod' | 'quality assurance' | 'prod' environmentNameShort: 'np' | 'qa' | 'pd'; costCenter: string; name: string; support: string; supportContact: string; reason: string; serviceOffering: string; } export const getEnvironment = (accountId: string): EnvironmentConfig => { switch (accountId) { case C.AWS_ACCOUNTS.NP: return { accountNameQualified: '${npDetails ? npDetails.accountNameQualified : ''}', accountNameShort: '${npDetails ? npDetails.accountNameShort : ''}', accountId: C.AWS_ACCOUNTS.NP, environmentNameLong: 'nonprod', environmentNameShort: 'np', costCenter: '${tagging.costCenter}', name: '${projectName}', support: '${tagging.support}', supportContact: '${tagging.supportContact}', reason: '${tagging.reason}', serviceOffering: '${tagging.serviceOffering}', }; case C.AWS_ACCOUNTS.QA: return { accountNameQualified: '${qaDetails ? qaDetails.accountNameQualified : ''}', accountNameShort: '${qaDetails ? qaDetails.accountNameShort : ''}', accountId: C.AWS_ACCOUNTS.QA, environmentNameLong: 'quality assurance', environmentNameShort: 'qa', costCenter: '${tagging.costCenter}', name: '${projectName}', support: '${tagging.support}', supportContact: '${tagging.supportContact}', reason: '${tagging.reason}', serviceOffering: '${tagging.serviceOffering}', }; case C.AWS_ACCOUNTS.PD: return { accountNameQualified: '${pdDetails ? pdDetails.accountNameQualified : ''}', accountNameShort: '${pdDetails ? pdDetails.accountNameShort : ''}', accountId: C.AWS_ACCOUNTS.PD, environmentNameLong: 'prod', environmentNameShort: 'pd', costCenter: '${tagging.costCenter}', name: '${projectName}', support: '${tagging.support}', supportContact: '${tagging.supportContact}', reason: '${tagging.reason}', serviceOffering: '${tagging.serviceOffering}', }; default: throw Error('Missing for accountId: ' + accountId); } }; `; exports.getAwsCdkConfigFactoryTs = getAwsCdkConfigFactoryTs; const getAwsCdkInfrastructureTs = () => `import { Construct } from 'constructs'; import { EnvironmentConfig } from './config.factory'; import { TaggingService } from '../tagging'; import { Tags } from 'aws-cdk-lib'; export class Infrastructure extends Construct { constructor(scope: Construct, envConfig: EnvironmentConfig) { super(scope, envConfig.name); // Tag all the resources new TaggingService(envConfig).staticTags().forEach(t => Tags.of(this).add(t.key, t.value)); } }`; exports.getAwsCdkInfrastructureTs = getAwsCdkInfrastructureTs; const getAwsCdkBinCdkProjectTs = (npDetails, qaDetails, pdDetails, projectName) => `#!/usr/bin/env node import 'source-map-support/register'; import * as cdk from 'aws-cdk-lib'; import { C } from '../lib/constants'; ${npDetails ? `import { NpStack } from '../lib/np-stack';` : ''} ${qaDetails ? `import { QaStack } from '../lib/qa-stack';` : ''} ${pdDetails ? `import { PdStack } from '../lib/pd-stack';` : ''} const app = new cdk.App(); ${npDetails ? `new NpStack(app, 'Stack-np', { stackName: '${projectName}-np', env: { account: C.AWS_ACCOUNTS.NP, region: C.AWS_REGION }, terminationProtection: true, });` : ''} ${qaDetails ? `new NpStack(app, 'Stack-qa', { stackName: '${projectName}-qa', env: { account: C.AWS_ACCOUNTS.QA, region: C.AWS_REGION }, terminationProtection: true, });` : ''} ${pdDetails ? `new PdStack(app, 'Stack-pd', { stackName: '${projectName}-pd', env: { account: C.AWS_ACCOUNTS.PD, region: C.AWS_REGION }, terminationProtection: true, });` : ''} `; exports.getAwsCdkBinCdkProjectTs = getAwsCdkBinCdkProjectTs; const getAwsCdkPipeline = (projectName) => `# Starter pipeline # Start with a minimal pipeline that you can customize to build and deploy your code. # Add steps that build, run tests, deploy, and more: # https://aka.ms/yaml trigger: - pd - qa - np variables: - ${"${{ if eq(variables['Build.SourceBranchName'], 'pd') }}"}: - group: ${projectName}-pd - ${"${{ if eq(variables['Build.SourceBranchName'], 'qa') }}"}: - group: ${projectName}-qa - ${"${{ if eq(variables['Build.SourceBranchName'], 'np') }}"}: - group: ${projectName}-np stages: - stage: CI pool: vmImage: ubuntu-latest jobs: - job: Test_Infrastructure displayName: Test Infrastructure steps: - checkout: self fetchDepth: 1 - task: NodeTool@0 displayName: Install Node inputs: versionSource: 'spec' versionSpec: '$(NODE_VERSION)' - task: Npm@1 displayName: Install Packages inputs: command: 'install' workingDir: '$(System.DefaultWorkingDirectory)' - task: Npm@1 displayName: Run CI inputs: command: 'custom' workingDir: '$(System.DefaultWorkingDirectory)' customCommand: 'run ci' - task: Npm@1 displayName: Run synth inputs: command: 'custom' workingDir: '$(System.DefaultWorkingDirectory)' customCommand: 'run synth -- Stack-$(Build.SourceBranchName)' - stage: CD jobs: - job: Deploy_Infrastructure displayName: Deploy infrastructure steps: - checkout: self fetchDepth: 1 - task: NodeTool@0 displayName: Install Node inputs: versionSource: 'spec' versionSpec: '$(NODE_VERSION)' - task: Npm@1 displayName: Install packages inputs: command: 'install' workingDir: '$(System.DefaultWorkingDirectory)' - task: AWSShellScript@1 displayName: Run cdk diff inputs: awsCredentials: '$(AWS_DEPLOYMENT_SERVICE_CONNECTION)' regionName: 'ap-southeast-2' scriptType: 'inline' inlineScript: 'npm run diff -- Stack-$(Build.SourceBranchName)' - task: AWSShellScript@1 displayName: Run cdk deploy inputs: awsCredentials: '$(AWS_DEPLOYMENT_SERVICE_CONNECTION)' regionName: 'ap-southeast-2' scriptType: 'inline' inlineScript: 'npm run deploy -- Stack-$(Build.SourceBranchName) --require-approval never' `; exports.getAwsCdkPipeline = getAwsCdkPipeline;