[
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n  \"image\": \"mcr.microsoft.com/vscode/devcontainers/base:ubuntu\",\n  \"features\": {\n    \"ghcr.io/devcontainers/features/node:1\": {},\n    \"ghcr.io/devcontainers/features/aws-cli:1\": {},\n    \"ghcr.io/devcontainers-contrib/features/aws-cdk:2\": {\n      \"version\": \"2.133.0\"\n    }\n  },\n  \"postCreateCommand\": \".devcontainer/postCreateCommand.sh\",\n  \"workspaceFolder\": \"/home/vscode/cdk-eks-blueprints-patterns\",\n  \"workspaceMount\": \"source=${localWorkspaceFolder},target=/home/vscode/cdk-eks-blueprints-patterns,type=bind\",\n  \"hostRequirements\": {\n    \"cpus\": 2\n  },\n  \"remoteEnv\": {\n    \"PATH\": \"${containerEnv:PATH}:/home/vscode/cdk-eks-blueprints-patterns\"\n  }\n}"
  },
  {
    "path": ".devcontainer/postCreateCommand.sh",
    "content": "#!/usr/bin/env bash\n\n# For Kubectl AMD64 / x86_64\n[ $(uname -m) = x86_64 ] && curl -sLO \"https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\"\n# For Kubectl ARM64\n[ $(uname -m) = aarch64 ] && curl -sLO \"https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/arm64/kubectl\"\nchmod +x ./kubectl\nsudo mv ./kubectl /usr/local/bin/kubectl\n\n# For Helm\ncurl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3\nchmod 700 get_helm.sh\necho \"Installing 'helm' utility ...\"\n./get_helm.sh\nrm -rf get_helm.sh\n\n# setup autocomplete for kubectl and alias k\nmkdir $HOME/.kube\necho \"source <(kubectl completion bash)\" >> $HOME/.bashrc\necho \"alias k=kubectl\" >> $HOME/.bashrc\necho \"complete -F __start_kubectl k\" >> $HOME/.bashrc\n"
  },
  {
    "path": ".eslintignore",
    "content": "node_modules\n\ndist\n\ncoverage\n\ncdk.out\n\n.eslintrc.js\n\njest.config.js\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n    root: true,\n    parser: '@typescript-eslint/parser',\n    plugins: [\n        '@typescript-eslint',\n    ],\n    extends: [\n        'eslint:recommended',\n        'plugin:@typescript-eslint/recommended'\n    ],\n    rules: {\n        \"@typescript-eslint/no-explicit-any\": \"off\",\n        \"@typescript-eslint/explicit-module-boundary-types\": \"off\",\n        \"@typescript-eslint/no-non-null-assertion\": \"off\",\n        \"@typescript-eslint/no-unused-vars\": [1, {\"argsIgnorePattern\": \"^_\"}],\n        indent: ['error', 4],\n        \"prefer-const\": \"off\",\n        \"semi\": ['error',\"always\"],\n    },\n};"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "*Issue #, if available:*\n\n*Description of changes:*\n\n\nBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.\n"
  },
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: Node.js CI\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n    branches: [ main ]\n\njobs:\n  build:\n    runs-on: macos-14\n\n    strategy:\n      matrix:\n        node-version: [22]\n\n    steps:\n    - uses: actions/checkout@v3\n\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v3\n      with:\n        node-version: ${{ matrix.node-version }}\n\n    - name: Cache node modules\n      uses: actions/cache@v5\n      env:\n        cache-name: cache-node-modules\n      with:\n        # npm cache files are stored in `~/.npm` on Linux/macOS\n        path: ~/.npm\n        key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}\n        restore-keys: |\n          ${{ runner.os }}-build-${{ env.cache-name }}-\n          ${{ runner.os }}-build-\n          ${{ runner.os }}-\n              \n    - name: Install Deps         \n      run: make deps\n    \n    - name: Run Linter        \n      run: make lint\n\n    - name: Build TSC\n      run: make build\n    \n    - name: Run CDK List\n      run: make list\n\n    - name: Run CDK Synth\n      run: make test-all\n"
  },
  {
    "path": ".github/workflows/docbuild.yml",
    "content": "name: ci\non:\n  push:\n    branches:\n      - master\n      - main\npermissions:\n  contents: write\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-python@v4\n        with:\n          python-version: 3.x\n      - run: pip install mkdocs-material\n      - run: pip install mkdocs-embed-external-markdown\n      - run: mkdocs gh-deploy --force\n"
  },
  {
    "path": ".github/workflows/e2e.yaml",
    "content": "name: \"E2E Pipeline for CDK Observability Accelerator\"\non:\n  issue_comment:\n    types: [created]\njobs:\n  checkPermissions:\n    runs-on: ubuntu-latest\n    if: github.event.issue.pull_request && contains(github.event.comment.body, '/do-e2e-test') # check the comment if it contains the keywords\n    steps:\n    - id: checkUserPermissions\n      uses: actions-cool/check-user-permission@main\n      with:    \n        require: 'admin'\n    outputs:\n      run_test: ${{ steps.checkUserPermissions.outputs.require-result }}\n  e2ePipeline:\n    runs-on: ubuntu-latest\n    if: github.event.issue.pull_request && contains(github.event.comment.body, '/do-e2e-test') && needs.checkPermissions.outputs.run_test == 'true'\n    needs:\n    - checkPermissions\n    permissions:\n      id-token: write   # This is required for requesting the JWT\n      contents: read \n    steps:\n    - name: Dump GitHub context\n      env:\n        GITHUB_CONTEXT: ${{ toJson(github) }}\n      run: |\n        echo \"$GITHUB_CONTEXT\"\n    - name: Get PR branch\n      uses: xt0rted/pull-request-comment-branch@v1\n      id: comment-branch \n    - name: Set latest commit status as pending\n      uses: myrotvorets/set-commit-status-action@master\n      with:\n        sha: ${{ steps.comment-branch.outputs.head_sha }}\n        token: ${{ secrets.CI_TOKEN }}\n        status: pending\n    - name: Configure AWS Credentials\n      uses: aws-actions/configure-aws-credentials@v4\n      with:\n        role-to-assume: arn:aws:iam::867286930927:role/BlueprintsCodeBuildRole\n        role-session-name: codebuildsession\n        aws-region: ${{ vars.AWS_REGION }}  \n    - name: Run CodeBuild\n      uses: aws-actions/aws-codebuild-run-build@v1\n      with:\n        project-name: cdk-pattern-test\n        # buildspec-override: path/to/buildspec.yaml or inline buildspec definition\n        # compute-type-override: compute-type\n        # environment-type-override: environment-type\n        # image-override: ecr-image-uri\n        env-vars-for-codebuild: |\n          PR_NUMBER,\n          COMMIT_ID,\n          PATTERN_NAME\n      env:\n        PR_NUMBER: ${{ github.event.issue.number }}\n        COMMIT_ID: ${{ steps.comment-branch.outputs.head_sha }}\n        PATTERN_NAME: ${{ github.event.comment.body }}\n    - name: Set latest commit status as ${{ job.status }}\n      uses: myrotvorets/set-commit-status-action@master\n      if: always()\n      with:\n        sha: ${{ steps.comment-branch.outputs.head_sha }}\n        token: ${{ secrets.CI_TOKEN }}\n        status: ${{ job.status }}\n\n"
  },
  {
    "path": ".github/workflows/linkcheck.json",
    "content": "{\n    \"timeout\": \"5s\",\n    \"retryOn429\": true,\n    \"retryCount\": 5,\n    \"fallbackRetryDelay\": \"30s\",\n    \"aliveStatusCodes\": [200, 206],\n    \"httpHeaders\": [\n      {\n        \"urls\": [\"https://help.github.com/\"],\n        \"headers\": {\n          \"Accept-Encoding\": \"zstd, br, gzip, deflate\"\n        }\n      }\n    ],\n    \"ignorePatterns\": [\n      {\n        \"pattern\": [\n            \"localhost\"\n        ]\n      },\n      {\n        \"pattern\": [\n            \"127.0.0.1\"\n        ]\n      }\n    ]\n}\n"
  },
  {
    "path": ".github/workflows/markdown-link-check.yaml",
    "content": "name: Check Markdown links\n\non:\n  push:\n    branches:\n      - main\n    paths:\n      - \"**/*.md\"\n\n  pull_request:\n    branches:\n      - main\n    paths:\n      - \"**/*.md\"\n\njobs:\n  markdown-link-check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: actions/setup-node@v3\n        with:\n          node-version: '20.x'\n      - name: install markdown-link-check\n        run: npm install -g markdown-link-check@3.10.2\n      - name: markdown-link-check version\n        run: npm list -g markdown-link-check\n      - name: Run markdown-link-check on MD files\n        run: find docs -name \"*.md\" | xargs -n 1 markdown-link-check -q -c .github/workflows/linkcheck.json\n"
  },
  {
    "path": ".github/workflows/stale_issue_pr.yaml",
    "content": "name: 'Stale issue & PR handler'\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '0 0 * * *'\n\njobs:\n  stale:\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n    steps:\n      - uses: actions/stale@main\n        id: stale\n        with:\n          ascending: true\n          close-issue-message: 'Issue closed due to inactivity.'\n          close-pr-message: 'Pull request closed due to inactivity.'\n          days-before-close: 60\n          days-before-stale: 90\n          stale-issue-label: stale\n          stale-pr-label: stale\n          # Not stale if have this labels\n          exempt-issue-labels: 'bug,enhancement,\"feature request\"'\n          exempt-pr-labels: 'bug,enhancement'\n          operations-per-run: 100\n          stale-issue-message: |\n            This issue has been automatically marked as stale because it has been open 60 days\n            with no activity. Remove stale label or comment or this issue will be closed in 10 days\n          stale-pr-message: |\n            This PR has been automatically marked as stale because it has been open 60 days\n            with no activity. Remove stale label or comment or this PR will be closed in 10 days\n"
  },
  {
    "path": ".gitignore",
    "content": "!jest.config.js\n*.d.ts\nnode_modules\n.vscode\n\n.classpath.txt\n.idea\n.settings\n.vscode\n*.iml\n\n# CDK asset staging directory\n.cdk.staging\ncdk.out\ncdk.json\ndist\n*.swp\ncdk.context.json\npackage-lock.json\nyarn.lock\n\n# mkdocs artifact\nsite\n# macOS extraneous file\n.DS_STORE\n\n# Python virtual env directory\n*.venv*\n\n*otel-collector-config-new.yml"
  },
  {
    "path": ".nvmrc",
    "content": "12.18.2"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "## Code of Conduct\nThis project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).\nFor more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact\nopensource-codeofconduct@amazon.com with any additional questions or comments.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing Guidelines\n\nThank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional\ndocumentation, we greatly value feedback and contributions from our community.\n\nPlease read through this document before submitting any issues or pull requests to ensure we have all the necessary\ninformation to effectively respond to your bug report or contribution.\n\n\n## Reporting Bugs/Feature Requests\n\nWe welcome you to use the GitHub issue tracker to report bugs or suggest features.\n\nWhen filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already\nreported the issue. Please try to include as much information as you can. Details like these are incredibly useful:\n\n* A reproducible test case or series of steps\n* The version of our code being used\n* Any modifications you've made relevant to the bug\n* Anything unusual about your environment or deployment\n\n\n## Contributing via Pull Requests\nContributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:\n\n1. You are working against the latest source on the *main* branch.\n2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.\n3. You open an issue to discuss any significant work - we would hate for your time to be wasted.\n\nTo send us a pull request, please:\n\n1. Fork the repository.\n2. 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.\n3. Ensure local tests pass.\n4. Ensure the following commands pass:\n\n```\nmake build\nmake lint # no errors / warnings, also you can use make lint-fix to fix issues\nmake pattern <your-pattern> list\nmake pattern <your-pattern> deploy\n```\n\nThe above should produce no errors. \n\n5. Commit to your fork using clear commit messages.\n6. Send us a pull request, answering any default questions in the pull request interface.\n7. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.\n\nGitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and\n[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).\n\n\n## Finding contributions to work on\nLooking 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.\n\n\n## Code of Conduct\nThis project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).\nFor more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact\nopensource-codeofconduct@amazon.com with any additional questions or comments.\n\n\n## Security issue notifications\nIf 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.\n\n\n## Licensing\n\nSee the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "#!/bin/bash\n\n# Libraries\nTSC := node node_modules/.bin/tsc\nESLINT := node node_modules/.bin/eslint\nCDK := node node_modules/.bin/cdk\npattern: pattern_name := $(firstword $(filter-out pattern, $(MAKECMDGOALS)))\npattern: pattern_command := $(subst pattern $(pattern_name), , $(MAKECMDGOALS))\n\npattern_files := $(notdir $(wildcard bin/*.ts))\nformatted_pattern_names := $(patsubst %.ts,%,$(pattern_files))\n\n# Dependecies\nHOMEBREW_LIBS :=  nvm typescript argocd\n\nlist:\n\t@$ echo \"To work with patterns use: \\n\\t$$ make pattern <pattern-name> <list | deploy | synth | destroy>\"\n\t@$ echo \"Example:\\n\\t$$ make pattern fargate deploy \\n\\nPatterns: \\n\"\n\t@$ $(foreach pattern, $(formatted_pattern_names),  echo \"\\t$(pattern)\";)\n\ndeps: bootstrap\n\tnpm install\n\nlint: \n\t$(ESLINT) . --ext .js,.jsx,.ts,.tsx\n\nlint-fix: \n\t$(ESLINT) . --ext .js,.jsx,.ts,.tsx --fix\n\nbuild:\n\trm -rf dist && $(TSC) --skipLibCheck\n\ncompile:\n\t$(TSC) --build --incremental \n\nmkdocs:\n\tmkdocs serve \n\npattern:\n\t@echo $(pattern_name) performing $(pattern_command)\n\t$(CDK) --app \"npx ts-node bin/$(pattern_name).ts\" $(if $(pattern_command),$(pattern_command), list)\n\t@:\n%:\n\t@:\n\ntest-all:\n\t@for pattern in $(formatted_pattern_names) ; do \\\n\t\techo \"Building pattern $$pattern\"; \\\n\t\t$(CDK) --app \"npx ts-node bin/$$pattern.ts\" list || exit 1 ;\\\n    done \n\nbootstrap:\n\t@for LIB in $(HOMEBREW_LIBS) ; do \\\n\t\tLIB=$$LIB make check-lib ; \\\n    done\n\ncheck-lib:\nifeq ($(shell brew ls --versions $(LIB)),)\n\t@echo Installing $(LIB) via Homebrew\n\t@brew install $(LIB)\nelse\n\t@echo $(LIB) is already installed, skipping.\nendif\n"
  },
  {
    "path": "README.md",
    "content": "# EKS Blueprints Patterns\n\nWelcome to the `EKS Blueprints Patterns` repository.\n\nThis repository contains a number of samples for how you can leverage the [Amazon EKS Blueprints](https://github.com/aws-quickstart/cdk-eks-blueprints). You can think of the patterns as \"codified\" reference architectures, which can be explained and executed as code in the customer environment.\n\n## Patterns\n\nThe individual patterns can be found in the `lib` directory. Most of the patterns are self-explanatory, for some more complex examples please use this guide and docs/patterns directory for more information.\n\n## Documentation\n\nPlease refer to the Amazon EKS Blueprints Patterns [documentation site](https://aws-samples.github.io/cdk-eks-blueprints-patterns/) for complete list of Amazon EKS Blueprints patterns documentation.\n\nPlease refer to the Amazon EKS Blueprints Quick Start [documentation site](https://aws-quickstart.github.io/cdk-eks-blueprints/) for complete project documentation.\n\n## Usage\n\nBefore proceeding, make sure [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) is installed on your machine.\n\nTo use the eks-blueprints and patterns module, you must have [Node.js](https://nodejs.org/en/) and [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) installed. You will also use `make` and `brew` to simplify build and other common actions.\n\n## Workstation Setup Options\n\n### DevContainer Setup\n\nUsers can choose this option, if you dont want to run this solution on a mac or ubuntu machine. Please use the dev container configuration in the `.devcontainer` folder with [devpod](devpod.sh) or any other dev container environment to create a development environment with dependencies such as Node, NPM, aws-cli, aws-cdk, kubectl, helm dependencies for your local development with `cdk-eks-blueprints-patterns` solution. \n\n### RHEL Setup\n\nFollow the below steps to setup and leverage `eks-blueprints` and `eks-blueprints-patterns` in your Amazon Linux/CentOS/RHEL Linux machine.\n\n1.  **Update the package list**\n\n    Update the package list to ensure you're installing the latest versions.\n\n    ```bash\n    sudo yum update\n    ```\n\n1.  **Install `make`**\n\n    ```bash\n    sudo yum install make\n    ```\n\n1.  **Install `brew`** by following instructions as detailed in [docs.brew.sh](https://docs.brew.sh/Homebrew-on-Linux)\n\n    ```bash\n     /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n    ```\n\n    Add Homebrew to your PATH\n\n    ```bash\n    test -d ~/.linuxbrew && eval \"$(~/.linuxbrew/bin/brew shellenv)\"\n    test -d /home/linuxbrew/.linuxbrew && eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n    test -r ~/.bash_profile && echo \"eval \\\"\\$($(brew --prefix)/bin/brew shellenv)\\\"\" >> ~/.bash_profile\n    echo \"eval \\\"\\$($(brew --prefix)/bin/brew shellenv)\\\"\" >> ~/.profile\n    ```\n\n    Verify brew installation\n\n    ```bash\n    brew -v\n    ```\n\n1.  **Install `Node.js` and `npm`**\n\n        Install Node.js v18 and npm using brew.\n\n        ```bash\n        brew install node@18\n        ```\n\n        Note: Node.js package includes npm\n\n        Set PATH for node@18\n\n        ```bash\n        test -r ~/.bash_profile && echo 'export PATH=\"/home/linuxbrew/.linuxbrew/opt/node@18/bin:$PATH\"' >> ~/.bash_profile\n        echo 'export PATH=\"/home/linuxbrew/.linuxbrew/opt/node@18/bin:$PATH\"' >> ~/.profile\n        export PATH=\"/home/linuxbrew/.linuxbrew/opt/node@18/bin:$PATH\"\n        ```\n\n    Post completing the above, continue from [Verify Node.js and npm Installation](#verify-nodejs-and-npm-installationbash)\n\n### Ubuntu Setup\n\nFollow the below steps to setup and leverage `eks-blueprints` and `eks-blueprints-patterns` in your Ubuntu Linux machine.\n\n1. **Update the package list**\n\n   Update the package list to ensure you're installing the latest versions.\n\n   ```bash\n   sudo apt update\n   ```\n\n1. **Install `make`**\n\n   ```bash\n   sudo apt install make\n   ```\n\n1. **Install `brew`** by following instructions as detailed in [docs.brew.sh](https://docs.brew.sh/Homebrew-on-Linux)\n\n   ```bash\n    /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"\n   ```\n\n   Add Homebrew to your PATH\n\n   ```bash\n   test -d ~/.linuxbrew && eval \"$(~/.linuxbrew/bin/brew shellenv)\"\n   test -d /home/linuxbrew/.linuxbrew && eval \"$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)\"\n   test -r ~/.bash_profile && echo \"eval \\\"\\$($(brew --prefix)/bin/brew shellenv)\\\"\" >> ~/.bash_profile\n   echo \"eval \\\"\\$($(brew --prefix)/bin/brew shellenv)\\\"\" >> ~/.profile\n   ```\n\n   Verify brew installation\n\n   ```bash\n   brew -v\n   ```\n\n1. **Install `Node.js` and `npm`**\n\n   Install Node.js v18 and npm using brew.\n\n   ```bash\n   brew install node@18\n   ```\n\n   Note: Node.js package includes npm\n\n   Set PATH for node@18\n\n   ```bash\n   test -r ~/.bash_profile && echo 'export PATH=\"/home/linuxbrew/.linuxbrew/opt/node@18/bin:$PATH\"' >> ~/.bash_profile\n   echo 'export PATH=\"/home/linuxbrew/.linuxbrew/opt/node@18/bin:$PATH\"' >> ~/.profile\n   export PATH=\"/home/linuxbrew/.linuxbrew/opt/node@18/bin:$PATH\"\n   ```\n\nPost completing the above, continue from [Verify Node.js and npm Installation](#verify-nodejs-and-npm-installation)\n\n### Mac Setup\n\nFollow the below steps to setup and leverage `eks-blueprints` and `eks-blueprints-patterns` in your local Mac laptop.\n\n1. **Install `make`, `node` and `npm` using brew**\n\n   ```bash\n   brew install make\n   brew install node@18\n   ```\n\n   Note: Node.js package includes npm\n\n   Set PATH for node@18\n\n   ```bash\n   echo 'export PATH=\"/opt/homebrew/opt/node@18/bin:$PATH\"' >> ~/.zshrc\n   export PATH=\"/opt/homebrew/opt/node@18/bin:$PATH\"\n   ```\n\n### Verify `Node.js` and `npm` Installation\n\n1. Check the installed version of Node.js:\n\n   ```bash\n   node -v\n   ```\n\n   The output should be `v18.x.x`.\n\n1. Check the installed version of npm\n\n   ```bash\n   npm -v\n   ```\n\n   The output should be a version greater than `9.x.x`.\n\n   If your npm version is not `9.x.x` or above, update npm with the following command:\n\n   ```bash\n   sudo npm install -g npm@latest\n   ```\n\n   Verify the installed version by running `npm -v`.\n\n### Repo setup\n\n1. Clone `cdk-eks-blueprints-patterns` repository\n\n   ```bash\n   git clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\n   cd cdk-eks-blueprints-patterns\n   ```\n\n   PS: If you are contributing to this repo, please make sure to fork the repo, add your changes and create a PR against it.\n\n1. Once you have cloned the repo, you can open it using your favourite IDE and run the below commands to install the dependencies and build the existing patterns.\n\n- Install project dependencies.\n\n  ```bash\n  make deps\n  ```\n\n- To view patterns that are available to be deployed, execute the following:\n\n  ```bash\n  npm i\n  make build\n  ```\n\n- To list the existing CDK EKS Blueprints patterns\n\n  ```bash\n  make list\n  ```\n\nNote: Some patterns have a hard dependency on AWS Secrets (for example GitHub access tokens). Initially you will see errors complaining about lack of the required secrets. It is normal. At the bottom, it will show the list of patterns which can be deployed, in case the pattern you are looking for is not available, it is due to the hard dependency which can be fixed by following the docs specific to those patterns.\n\n```bash\nTo work with patterns use:\n        $ make pattern <pattern-name> <list | deploy | synth | destroy>\nExample:\n        $ make pattern fargate deploy\n\nPatterns:\n\n        bottlerocket\n        data-at-rest\n        datadog\n        dynatrace-operator\n        ecr-image-scanning\n        emr\n        fargate\n        generative-ai-showcase\n        generic-cluster-provider\n        guardduty\n        jupyterhub\n        kasten\n        keptn-control-plane\n        konveyor\n        kubecost\n        kubeflow\n        kubeshark\n        multi-region\n        multi-team\n        newrelic\n        nginx\n        pipeline-multienv-gitops\n        pipeline-multienv-monitoring\n        pipeline\n        rafay\n        secure-ingress-cognito\n        snyk\n        starter\n        gmaestro\n        workloads-codecommit\n```\n\n- Bootstrap your CDK environment.\n\n  ```bash\n  npx cdk bootstrap\n  ```\n\n- You can then deploy a specific pattern with the following:\n\n  ```bash\n  make pattern multi-team deploy\n  ```\n\n# Developer Flow\n\n## Modifications\n\nAll files are compiled to the dist folder including `lib` and `bin` directories. For iterative development (e.g. if you make a change to any of the patterns) make sure to run compile:\n\n```bash\nmake compile\n```\n\nThe `compile` command is optimized to build only modified files and is fast.\n\n## New Patterns\n\nTo create a new pattern, please follow these steps:\n\n1. Under lib create a folder for your pattern, such as `<pattern-name>-construct`. If you plan to create a set of patterns that represent a particular subdomain, e.g. `security` or `hardening`, please create an issue to discuss it first. If approved, you will be able to create a folder with your subdomain name and group your pattern constructs under it.\n2. Blueprints generally don't require a specific class, however we use a convention of wrapping each pattern in a plain class like `<Pattern-Name>Construct`. This class is generally placed in `index.ts` under your pattern folder.\n3. Once the pattern implementation is ready, you need to include it in the list of the patterns by creating a file `bin/<pattern-name>.ts`. The implementation of this file is very light, and it is done to allow patterns to run independently.\n\nExample simple synchronous pattern:\n\n```typescript\nimport { configureApp } from \"../lib/common/construct-utils\";\nimport FargateConstruct from \"../lib/fargate-construct\";\n\nnew FargateConstruct(configureApp(), \"fargate\"); // configureApp() will create app and configure loggers and perform other prep steps\n```\n\n4. In some cases, patterns need to use async APIs. For example, they may rely on external secrets that you want to validate ahead of the pattern deployment.\n\nExample async pattern:\n\n```typescript\nimport { configureApp, errorHandler } from \"../lib/common/construct-utils\";\n\nconst app = configureApp();\n\nnew NginxIngressConstruct().buildAsync(app, \"nginx\").catch((e) => {\n  errorHandler(\n    app,\n    \"NGINX Ingress pattern is not setup. This maybe due to missing secrets for ArgoCD admin pwd.\",\n    e\n  );\n});\n```\n\n5. There are a few utility functions that can be used in the pattern implementation such as secret prevalidation. This function will fail if the corresponding secret is not defined, this preventing the pattern to deploy.\n\n```typescript\nawait prevalidateSecrets(\n  NginxIngressConstruct.name,\n  undefined,\n  SECRET_ARGO_ADMIN_PWD\n);\nawait prevalidateSecrets(\"my-pattern-name\", \"us-east-1\", \"my-secret-name\"); //\n```\n\n## Contributing\n\nSee [Contributing](CONTRIBUTING.md) guide for requirements on contribution.\n\n# Deploying Blueprints with External Dependency on AWS Resources\n\nThere are cases when the blueprints defined in the patterns have dependencies on existing AWS Resources such as Secrets defined in the account/region.\nFor such cases, you may see errors if such resources are not defined.\n\nFor [`PipelineMultiEnvGitops`](./lib/pipeline-multi-env-gitops/index.ts) please see instructions in this [README](./docs/patterns/pipeline-multi-env-gitops.md).\n\nFor `MultiRegionConstruct` the pattern relies on the following secrets defined:\n\n1. `github-ssh-key` - must contain GitHub SSH private key as a JSON structure containing fields `sshPrivateKey` and `url`. The secret is expected to be defined in `us-east-1` and replicated to `us-east-2` and `us-west-2` regions. For more information on SSH credentials setup see [ArgoCD Secrets Support](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/#secrets-support).\n   Example Structure:\n\n```\n{\n    \"sshPrivateKey\": \"-----BEGIN THIS IS NOT A REAL PRIVATE KEY-----\\nb3BlbnNzaC1rtdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn\\nNhAAAAAwEAAQAAAgEAy82zTTDStK+s0dnaYzE7vLSAcwsiHM8gN\\nhq2p5TfcjCcYUWetyu6e/xx5Rh+AwbVvDV5h9QyMw4NJobwuj5PBnhkc3QfwJAO5wOnl7R\\nGbehIleWWZLs9qq`DufViQsa0fDwP6JCrqD14aIozg6sJ0Oqi7vQkV+jR0ht/\\nuFO1ANXBn2ih0ZpXeHSbPDLeZQjlOBrbGytnCbdvLtfGEsV0WO2oIieWVXJj/zzpKuMmrr\\nebPsfwr36nLprOQV6IhDDo\\n-----END NOT A REAL PRIVATE KEY-----\\n\",\n\n    \"url\": \"git@github\"\n}\n```\n\nNote: You can notice explicit \\n characters in the sshPrivateKey.\n\n2. `argo-admin-secret` - must contain ArgoCD admin password in Plain Text. The secret is expected to be defined in `us-east-1` and replicated to `us-east-1` and `us-west-2` regions.\n\nFor ``Dynatrace One Agent`\n\n- `dynatrace-tokens` - must contain [API_URL](https://github.com/dynatrace-oss/dynatrace-ssp-addon#aws-secret-manager-secrets), [API_TOKEN](https://github.com/dynatrace-oss/dynatrace-eks-blueprints-addon#aws-secret-manager-secrets) and [PAAS_TOKEN](https://github.com/dynatrace-oss/dynatrace-eks-blueprints-addon#aws-secret-manager-secrets) in Plain Text. The secret is expected to be defined in the target region (either directly or through AWS Secrets Manager Replication).\n\nFor `keptn-control-plane` the pattern relies on the following secrets defined:\n\n- `keptn-secrets` - must contain API_TOKEN and BRIDGE_PASSWORD password in Plain Text. The secret is expected to be defined in `us-east-1` region.\n\nFor `newrelic` the pattern relies on the following secrets defined:\n\n- `newrelic-pixie-keys` - must contain New Relic (required) and Pixie keys (optional). The secret is expected to be defined in the target region (either directly or through AWS Secrets Manager Replication).\n\nFor more information on defining secrets for ArgoCD, please refer to [Blueprints Documentation](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/#secrets-support) as well as [known issues](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/#known-issues).\n\nFor `nginx` please see [NGINX Blueprint documentation](docs/patterns/nginx.md).\n\nFor `datadog` the pattern relies on the following secret defined:\n\n- `apiKeyAWSSecret` - must contain the Datadog API key in Plain Text named `datadog-api-key`. The secret is expected to be defined in the target region.\n\nFor `kubeflow` please see [Kubeflow documentation](docs/patterns/kubeflow.md).\n\nFor `secure-ingress-cognito` please see [Secure Ingress using Cognito Blueprint documentation](docs/patterns/secureingresscognito.md).\n\nFor `GmaestroConstruct` the pattern relies on the following secret defined:\n\n- `granulate-client-id` - must contain the client_id Plain Text. The secret is expected to be defined in the target region (either directly or through AWS Secrets Manager Replication).\n## Security\n\nSee [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.\n\n## License\n\nThis library is licensed under the MIT-0 License. See the LICENSE file.\n"
  },
  {
    "path": "bin/asg.ts",
    "content": "import 'source-map-support/register';\nimport * as cdk from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n\nimport { configureApp } from \"../lib/common/construct-utils\";\n\nconst app = configureApp();\n\nconst spotInterruptHandlerAddOn = new blueprints.addons.AwsNodeTerminationHandlerAddOn({\n    version: \"0.25.1\",\n    repository: 'oci://public.ecr.aws/aws-ec2/helm/aws-node-termination-handler'\n});\n\nconst clusterProvider = new blueprints.AsgClusterProvider({\n    version: cdk.aws_eks.KubernetesVersion.V1_30,\n    minSize: 1, maxSize: 1, spotPrice: \"0.10\",\n    machineImageType: cdk.aws_eks.MachineImageType.BOTTLEROCKET,\n    id: \"asg-spot\",\n    name: \"asg-spot\",\n    spotInterruptHandler: false\n});\n\nconst blueprint = blueprints.EksBlueprint.builder()\n    .region(\"us-west-2\")\n    .version(\"auto\")\n    .clusterProvider(clusterProvider)\n    .addOns(spotInterruptHandlerAddOn)\n    .build(app, 'asg-test');"
  },
  {
    "path": "bin/backstage.ts",
    "content": "import { BackstageConstruct } from '../lib/backstage-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew BackstageConstruct(app, 'backstage-stack');\n"
  },
  {
    "path": "bin/batch.ts",
    "content": "import BatchConstruct from '../lib/aws-batch-on-eks-construct';\nimport { batchTeam } from '../lib/teams/team-batch';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Single cluster with Batch on EKS deployed\n//-------------------------------------------\nnew BatchConstruct().build(app, 'batch', [batchTeam]);"
  },
  {
    "path": "bin/bottlerocket.ts",
    "content": "import BottleRocketConstruct from '../lib/bottlerocket-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Single cluster with Bottlerocket nodes.\n//-------------------------------------------\nnew BottleRocketConstruct().build(app, 'bottlerocket');"
  },
  {
    "path": "bin/crossplane-argocd-gitops.ts",
    "content": "#!/usr/bin/env node\nimport * as cdk from 'aws-cdk-lib';\nimport { errorHandler } from '../lib/common/construct-utils';\nimport MultiClusterPipelineConstruct from \"../lib/crossplane-argocd-gitops/multi-cluster-pipeline\";\n\nconst app = new cdk.App();\n\nnew MultiClusterPipelineConstruct().buildAsync(app,  \"crossplane-argocd-gitops\").catch((e) => {\n    errorHandler(app, \"Pipeline construct failed because of error: \", e);\n});\n"
  },
  {
    "path": "bin/custom-networking-ipv4.ts",
    "content": "import CustomNetworkingIPv4Construct from '../lib/custom-networking-ipv4-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\nnew CustomNetworkingIPv4Construct(app, 'custom-networking-ipv4');"
  },
  {
    "path": "bin/data-at-rest-encryption.ts",
    "content": "import EncryptionAtRestConstruct from \"../lib/security/data-at-rest-encryption\";\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\n\n//--------------------------------------------------------------------------\n// Security Patterns\n//--------------------------------------------------------------------------\n\nconst app = configureApp();\nnew EncryptionAtRestConstruct().buildAsync(app, \"data-at-rest-encryption\").catch((e) => {\n    errorHandler(app, \"EncryptionAtRestConstruct is not setup due to missing secrets for ArgoCD admin pwd\", e);\n});\n"
  },
  {
    "path": "bin/datadog.ts",
    "content": "import DatadogConstruct from '../lib/datadog-construct';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew DatadogConstruct().buildAsync(app, 'datadog').catch((error) => {\n    errorHandler(app, \"Datadog pattern is not setup due to missing secrets: \" + error);\n});"
  },
  {
    "path": "bin/dynatrace-operator.ts",
    "content": "import DynatraceOperatorConstruct from '../lib/dynatrace-construct';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew DynatraceOperatorConstruct().buildAsync(app, \"dynatrace-operator\").catch((e) => {\n    errorHandler(app, \"Dynatrace pattern is not setup due to missing secrets for dynatrace-tokens.\", e);\n});"
  },
  {
    "path": "bin/ecr-image-scanning.ts",
    "content": "\nimport { ImageScanningSetupStack } from \"../lib/security/image-vulnerability-scanning/image-scanning-setup\";\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport ImageScanningWorkloadConstruct from \"../lib/security/image-vulnerability-scanning\";\n\nconst app = configureApp();\n\nnew ImageScanningSetupStack(app, \"image-scanning-setup\");\n\nnew ImageScanningWorkloadConstruct().buildAsync(app, \"image-scanning-workload\").catch((e) => {\n    errorHandler(app, \"ImageScanningWorkloadConstruct is not setup due to missing secrets for ArgoCD admin pwd\", e);\n});"
  },
  {
    "path": "bin/eks-config-rules.ts",
    "content": "import { configureApp } from '../lib/common/construct-utils';\nimport { EksConfigRulesSetup } from '../lib/security/eks-config-rules';\nimport { EksConfigSetup } from '../lib/security/eks-config-rules/config-setup';\n\n\nconst app = configureApp();\n\nnew EksConfigSetup(app, 'eks-config-setup');\n\nnew EksConfigRulesSetup(app, 'eks-config-rules-setup');\n"
  },
  {
    "path": "bin/emr.ts",
    "content": "import EmrEksConstruct from '../lib/emr-eks';\nimport { dataTeam } from '../lib/teams/team-emr-on-eks';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew EmrEksConstruct().build(app, 'emrOnEks', [dataTeam]);"
  },
  {
    "path": "bin/fargate.ts",
    "content": "\nimport { configureApp } from '../lib/common/construct-utils';\nimport FargateConstruct from '../lib/fargate-construct';\n\nnew FargateConstruct(configureApp(), 'fargate');"
  },
  {
    "path": "bin/generative-ai-showcase.ts",
    "content": "import GenAIShowcase from \"../lib/generative-ai-showcase\";\nimport { configureApp } from \"../lib/common/construct-utils\";\n\nconst app = configureApp();\n\nnew GenAIShowcase(app, 'generative-ai-showcase');\n"
  },
  {
    "path": "bin/generic-cluster-provider.ts",
    "content": "import { configureApp } from '../lib/common/construct-utils';\nimport GenericClusterConstruct from '../lib/generic-cluster-construct';\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Single cluster with custom configuration.\n//-------------------------------------------\nnew GenericClusterConstruct().build(app, 'generic-cluster');"
  },
  {
    "path": "bin/gmaestro.ts",
    "content": "#!/usr/bin/env node\nimport GmaestroConstruct from '../lib/gmaestro-construct';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew GmaestroConstruct().buildAsync(app, 'gmaestro').catch((error) => {\n    errorHandler(app, \"Gmaestro is not setup due to missing secrets: \" + error);\n});\n"
  },
  {
    "path": "bin/gpu.ts",
    "content": "import { configureApp } from \"../lib/common/construct-utils\";\nimport GpuConstruct from \"../lib/gpu-construct\";\n\nconst app = configureApp();\n\nnew GpuConstruct().build(app, \"gpu\");\n"
  },
  {
    "path": "bin/graviton.ts",
    "content": "import { configureApp } from \"../lib/common/construct-utils\";\nimport GravitonConstruct from \"../lib/graviton-construct\";\n\nconst app = configureApp();\n\nnew GravitonConstruct().build(app, \"graviton\");\n"
  },
  {
    "path": "bin/guardduty.ts",
    "content": "import { GuardDutySetupStack } from \"../lib/security/guardduty-construct/guardduty-setup\";\nimport GuardDutyWorkloadConstruct from \"../lib/security/guardduty-construct\";\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew GuardDutySetupStack(app, \"guardduty-setup\");\n\nnew GuardDutyWorkloadConstruct().buildAsync(app, \"guardduty\").catch((e) => {\n    errorHandler(app, \"GuardDutyWorkloadConstruct is not setup due to missing secrets for ArgoCD admin pwd\", e);\n});\n"
  },
  {
    "path": "bin/import-cluster.ts",
    "content": "import { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport { ImportClusterConstruct } from '../lib/import-cluster';\n\n\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Multiple clusters, multiple regions.\n//-------------------------------------------\n\nnew ImportClusterConstruct().build(app).catch((error) => {\n    errorHandler(app, \"Import cluster construct failed to import cluster\", error);\n});"
  },
  {
    "path": "bin/instana-operator.ts",
    "content": "import { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport InstanaConstruct from '../lib/instana-construct';\n\nconst app = configureApp();\n\nnew InstanaConstruct().buildAsync(app, \"instana-operator\").catch((error) => {\n    errorHandler(app, \"Instana pattern is not setup due to missing secrets: \" + error);\n});"
  },
  {
    "path": "bin/ipv6.ts",
    "content": "import { configureApp } from \"../lib/common/construct-utils\";\nimport IpV6Construct from \"../lib/ipv6-construct\";\n\nconst app = configureApp();\n\nnew IpV6Construct().build(app, \"ipv6\");\n"
  },
  {
    "path": "bin/jupyterhub.ts",
    "content": "\nimport JupyterHubConstruct from '../lib/jupyterhub-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst account = process.env.CDK_DEFAULT_ACCOUNT!;\nconst region = process.env.CDK_DEFAULT_REGION!;\n\nconst app = configureApp();\n\nnew JupyterHubConstruct(app, 'jupyterhub', { env: { account, region } });"
  },
  {
    "path": "bin/karpenter.ts",
    "content": "import KarpenterConstruct from \"../lib/karpenter-construct\";\nimport { configureApp } from \"../lib/common/construct-utils\";\n\nconst app = configureApp();\n\nnew KarpenterConstruct(app, 'karpenter');\n"
  },
  {
    "path": "bin/kasten.ts",
    "content": "\nimport KastenK10Construct from '../lib/kasten-k10-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew KastenK10Construct(app, 'kasten');"
  },
  {
    "path": "bin/keptn-control-plane.ts",
    "content": "import KeptnControlPlaneConstruct from '../lib/keptn-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew KeptnControlPlaneConstruct(app, 'keptn');"
  },
  {
    "path": "bin/komodor.ts",
    "content": "// import KomodorConstruct from '../lib/komodor-construct';\n// import { configureApp } from '../lib/common/construct-utils';\n\n// const app = configureApp();\n\n// new KomodorConstruct(app, 'komodor');"
  },
  {
    "path": "bin/konveyor.ts",
    "content": "import { KonveyorConstruct } from \"../lib/konveyor-construct\";\nimport { configureApp } from \"../lib/common/construct-utils\";\n\nconst app = configureApp();\n\nnew KonveyorConstruct(app, 'konveyor-stack');"
  },
  {
    "path": "bin/kubecost.ts",
    "content": "import KubecostConstruct from '../lib/kubecost-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew KubecostConstruct(app, 'kubecost');"
  },
  {
    "path": "bin/kubeflow.ts",
    "content": "import KubeflowConstruct from '../lib/kubeflow-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew KubeflowConstruct(app, 'kubeflow');"
  },
  {
    "path": "bin/kubeshark.ts",
    "content": "import KubesharkConstruct from '../lib/kubeshark-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew KubesharkConstruct(app, 'kubeshark');"
  },
  {
    "path": "bin/multi-cluster-conformitron.ts",
    "content": "import { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport { PipelineMultiCluster } from '../lib/multi-cluster-construct/pipeline';\n\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Multiple clusters, multiple regions.\n//-------------------------------------------\n\nnew PipelineMultiCluster().buildAsync(app).catch((error) => {\n    errorHandler(app, \"Multi cluster pattern is not setup. It may be due to missing secrets: \", error);\n});"
  },
  {
    "path": "bin/multi-region.ts",
    "content": "import { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport MultiRegionConstruct from '../lib/multi-region-construct';\n\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Multiple clusters, multiple regions.\n//-------------------------------------------\n\nnew MultiRegionConstruct().buildAsync(app, 'multi-region').catch((error) => {\n    errorHandler(app, \"Multi region pattern is not setup. It may be due to missing secrets: \", error);\n});"
  },
  {
    "path": "bin/multi-team.ts",
    "content": "import { configureApp } from '../lib/common/construct-utils';\nimport MultiTeamConstruct from '../lib/multi-team-construct';\n\nconst app = configureApp();\n\n//-------------------------------------------\n// Single Cluster with multiple teams.\n//-------------------------------------------\n\nnew MultiTeamConstruct(app, 'multi-team');"
  },
  {
    "path": "bin/newrelic.ts",
    "content": "import NewRelicConstruct from '../lib/newrelic-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew NewRelicConstruct(app, 'newrelic-cluster');\n"
  },
  {
    "path": "bin/nginx.ts",
    "content": "#!/usr/bin/env node\nimport NginxIngressConstruct from '../lib/nginx-ingress-construct';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew NginxIngressConstruct().buildAsync(app, 'nginx').catch((e) => {\n    errorHandler(app, \"NGINX Ingress pattern is not setup. This maybe due to missing secrets for ArgoCD admin pwd.\", e);\n});\n"
  },
  {
    "path": "bin/paralus.ts",
    "content": "import ParalusConstruct from '../lib/paralus-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew ParalusConstruct(app, 'paralus');"
  },
  {
    "path": "bin/pipeline-multienv-gitops.ts",
    "content": "import { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport PipelineMultiEnvGitops, { populateWithContextDefaults } from '../lib/pipeline-multi-env-gitops';\nimport * as cdk from 'aws-cdk-lib';\n\n\n// CDK Default Environment - default account and region\nconst account = process.env.CDK_DEFAULT_ACCOUNT!;\nconst region = process.env.CDK_DEFAULT_REGION!;\nconst env: cdk.Environment = { account: account, region: region };\n\nconst app = configureApp();\n// These different CDK environments are meant to be used for multi-region/account usage, \n// where the pipeline, dev cluster, and prod cluster are deployed in seperate environments\nconst { devEnv, pipelineEnv, prodEnv }:\n    { devEnv: cdk.Environment; pipelineEnv: cdk.Environment; prodEnv: cdk.Environment; } =\n    populateWithContextDefaults(app, account, region);\n\n//--------------------------------------------------------------------------\n// Multiple clusters, multiple reginos ,multiple teams, GitOps bootstrapped.\n//--------------------------------------------------------------------------\nnew PipelineMultiEnvGitops()\n    .buildAsync(\n        app,\n        'pipeline-multi-env',\n        {\n            devTestEnv: devEnv,\n            pipelineEnv: pipelineEnv,\n            prodEnv: prodEnv,\n        },\n        { env }\n    )\n    .catch((e) => {\n        errorHandler(\n            app,\n            'Pipeline pattern is not setup due to missing secrets for GitHub access.',\n            e\n        );\n    });"
  },
  {
    "path": "bin/pipeline-multienv-monitoring.ts",
    "content": "\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport { PipelineMultiEnvMonitoring } from '../lib/multi-account-monitoring';\n\nconst app = configureApp();\n\n//--------------------------------------------------------------------------\n// Multiple clusters, multiple accounts, pipeline and Monitoring\n//--------------------------------------------------------------------------\nnew PipelineMultiEnvMonitoring()\n    .buildAsync(app)\n    .catch((e) => {\n        errorHandler(app, \"Multi Account Monitoring pattern is not setup due to missing secrets for GitHub \\\n        access and/or CDK Context. See Multi Account Monitoring in the readme for instructions\", e);\n    });"
  },
  {
    "path": "bin/pipeline.ts",
    "content": "import { configureApp, errorHandler } from '../lib/common/construct-utils';\nimport PipelineConstruct from '../lib/pipeline-stack';\nimport * as cdk from 'aws-cdk-lib';\n\n//-------------------------------------------\n// Multiple clusters with deployment pipeline.\n//-------------------------------------------\nconst account = process.env.CDK_DEFAULT_ACCOUNT!;\nconst region = process.env.CDK_DEFAULT_REGION!;\nconst env: cdk.Environment = { account: account, region: region };\nconst app = configureApp();\n\n\nnew PipelineConstruct().buildAsync(app, { env }).catch((e) => {\n    errorHandler(app, \"Pipeline pattern is not setup due to missing secrets for GitHub access.\", e);\n});\n"
  },
  {
    "path": "bin/rafay.ts",
    "content": "\nimport RafayConstruct from '../lib/rafay-construct';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew RafayConstruct().buildAsync(app, 'rafay-cluster').catch((error) => {\n    errorHandler(app, \"Rafay pattern is not setup due to missing secrets: \" + error);\n});"
  },
  {
    "path": "bin/secure-ingress-cognito.ts",
    "content": "import { SecureIngressCognito } from '../lib/secure-ingress-auth-cognito';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\n//--------------------------------------------------------------------------\n// Single Cluster, Secure Ingress Auth using cognito\n//--------------------------------------------------------------------------\n\nconst app = configureApp();\n\nnew SecureIngressCognito()\n    .buildAsync(app, 'secure-ingress')\n    .catch((e) => {\n        errorHandler(app, \"Secure Ingress Auth pattern is not setup due to missing secrets for ArgoCD admin pwd. \\\n            See Secure Ingress Auth in the readme for instructions\", e);\n    });\n"
  },
  {
    "path": "bin/securityhub.ts",
    "content": "import { configureApp } from '../lib/common/construct-utils';\nimport { SecurityHubStackSetup } from '../lib/security/securityhub-construct';\n\nconst app = configureApp();\nnew SecurityHubStackSetup(app, 'securityhub-setup');"
  },
  {
    "path": "bin/snyk.ts",
    "content": "import SnykConstruct from '../lib/snyk-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew SnykConstruct(app, 'snyk-monitor');"
  },
  {
    "path": "bin/starter.ts",
    "content": "#!/usr/bin/env node\nimport { configureApp } from '../lib/common/construct-utils';\nimport StarterConstruct from '../lib/starter-construct';\n\nconst app = configureApp();\n\nnew StarterConstruct().build(app, 'starter-construct');\n"
  },
  {
    "path": "bin/unionai.ts",
    "content": "import UnionDataplaneConstruct from '../lib/union-dataplane-construct';\nimport { configureApp, errorHandler } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew UnionDataplaneConstruct().buildAsync(app, 'union-ai-datplane').catch((e) => {\n    errorHandler(app, \"Union Dataplane Construct pattern is not setup due to missing secrets for Union client. See Union Dataplane Construct in the readme for instructions\", e);\n});\n"
  },
  {
    "path": "bin/windows.ts",
    "content": "import { configureApp } from \"../lib/common/construct-utils\";\nimport WindowsConstruct from \"../lib/windows-construct\";\n\nconst app = configureApp();\n\nnew WindowsConstruct().build(app, \"windows\");\n"
  },
  {
    "path": "bin/workloads-codecommit.ts",
    "content": "import WorkloadsCodeCommitConstruct from '../lib/workloads-codecommit-construct';\nimport { configureApp } from '../lib/common/construct-utils';\n\nconst app = configureApp();\n\nnew WorkloadsCodeCommitConstruct(app, 'workloads-codecommit');\n"
  },
  {
    "path": "ci/buildspec.yml",
    "content": "version: 0.2\nenv:\n  variables:\n    CONTEXT_LOCATION:\n    COMMIT_ID:\n    PR_NUMBER:\n    PATTERN_NAME:\nphases:\n  install:\n    runtime-versions:\n      nodejs: 18\n    commands:\n      - n 20.10.0\n      - |\n        if [ ! -z \"${COMMIT_ID}\" ]; then \n          git fetch origin pull/${PR_NUMBER}/head:pr\n          git checkout main\n          # git merge needs user details, but we don't push anything, so the contents are unimportant\n          git -c \"user.name=CI Bot\" -c \"user.email=dev@null\" merge --no-edit ${COMMIT_ID}\n        fi\n      - npm i\n      - make build\n  pre_build:\n    commands:\n      - | \n        [ -z \"$CONTEXT_LOCATION\" ] || aws s3 cp $CONTEXT_LOCATION .\n  build:\n    commands:\n      - export AWS_REGION=us-east-2 && make pattern \"${PATTERN_NAME#/do-e2e-test } --verbose --all --require-approval never --force\"\n#    finally:\n#      - make destroy-all\n"
  },
  {
    "path": "docs/index.md",
    "content": "--8<-- \"README.md\""
  },
  {
    "path": "docs/patterns/backstage.md",
    "content": "# Backstage on EKS\n\n## Objective\n\n[Backstage](https://backstage.io/) is an application that aims to facilitate introduction and maintenance of standards and best practices, across the organization, tying all infrastructure tooling, resources, owners, contributors, and administrators together in one place.\n\nThe base functionality is provided by the Core component, which is assembled together with Plugins into an Application. Plugins extend the Core with additional functionalities that can be open source, or proprietary to a company.\n\nThe objective of this pattern is to illustrate how to deploy a Backstage pre-built Docker image, using the [Amazon EKS Blueprints Backstage add-on](https://github.com/aws-quickstart/cdk-eks-blueprints/blob/main/docs/addons/backstage.md).\n\n## Architecture\n\n![Backstage Architecture](./images/backstage-diagram.png)\n\n## Approach\n\nThis blueprint will include the following:\n\n- A new Well-Architected VPC with both Public and Private subnets\n- A new Well-Architected EKS cluster in the region and account you specify\n- An Application Load Balancer (ALB), implementing the Backstage Ingress rules\n- An Amazon RDS for PostgreSQL instance\n- A certificate, assigned to the ALB\n- A Secret in AWS Secrets Manager, storing the database credentials, imported into the cluster via [ExternalsSecretsAddOn](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/external-secrets/)\n- Other popular add-ons\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine:\n\n- [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) (also ensure it is [configured](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html#getting-started-quickstart-new))\n- [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n- [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n- [tsc](https://www.typescriptlang.org/download)\n- [make](https://www.gnu.org/software/make/)\n- [Docker](https://docs.docker.com/get-docker/)\n\nLet’s start by setting the account and region environment variables:\n\n```sh\nACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)\nAWS_REGION=$(aws configure get region)\n```\n\nCreate the [Backstage application](https://backstage.io/docs/getting-started/create-an-app), command reported here for your convenience:\n\n```sh\nnpx @backstage/create-app@latest\n```\n\nBuild the corresponding [Docker image](https://backstage.io/docs/deployment/docker), commands reported here for your convenience:\n\n```sh\ncd ./backstage\nyarn install --frozen-lockfile\nyarn tsc\nyarn build:backend --config app-config.yaml\n```\n\nNote: if the above command throws an error caused by app-config.yaml not found, you can explicitly set the path to the file:\n\n```sh\nyarn build:backend --config $(pwd)/app-config.yaml\n```\nThen you can progress with the docker image build:\n\n```sh\ndocker image build . -f packages/backend/Dockerfile --tag backstage\n```\n\nNote: consider the platform you are building on, and the target platform the image will run on, you might want to use the [--platform option](https://docs.docker.com/engine/reference/commandline/buildx_build/), e.g.:\n\n```sh\ndocker buildx build ... --platform=...\n```\n\nNote: If you are running a version of Docker Engine version earlier than 23.0, you might need to enable BuildKit manually, like explained in the [Getting Started section](https://docs.docker.com/build/buildkit/#getting-started) of the BuildKit webpage.\n\n(Optional) to show examples on the UI, add to Docker file:\n\n```sh\nCOPY --chown=node:node examples /examples\n```\n\nCreate an Amazon Elastic Container Registry (ECR) repository, named _backstage_:\n\n```sh\naws ecr create-repository --repository-name backstage\n```\n\n```sh\nDOCKER_IMAGE_ID=... #see output of image id from above image creation\naws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com\ndocker tag $DOCKER_IMAGE_ID $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/backstage:latest\ndocker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/backstage:latest\n```\n\nSetup a Hosted Zone in Route 53, with your parent domain. The pattern will create a new subdomain with format _{backstage subdomain label}.{parent domain}_. The default value for _{backstage subdomain label}_ is _backstage_ (see parameters below).\n\n## Deployment\n\nClone the repository:\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nSet the pattern's parameters in the CDK context by overriding the _cdk.json_ file (edit _PARENT_DOMAIN_NAME_ as it fits):\n\n```sh\nPARENT_DOMAIN_NAME=example.com\nHOSTED_ZONE_ID=$(aws route53 list-hosted-zones-by-name --dns-name $PARENT_DOMAIN_NAME --query \"HostedZones[].Id\" --output text | xargs basename)\ncat << EOF > cdk.json\n{\n    \"app\": \"npx ts-node dist/lib/common/default-main.js\",\n    \"context\": {\n        \"backstage.image.registry.name\": \"${ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com\",\n        \"backstage.parent.domain.name\":\"${PARENT_DOMAIN_NAME}\",\n        \"backstage.hosted.zone.id\": \"${HOSTED_ZONE_ID}\"\n      }\n}\nEOF\n```\n\n(Optional) The full list of parameters you can set in the _context_ is:\n\n```\n    \"context\": {\n        \"backstage.namespace.name\": ...,\n        \"backstage.image.registry.name\": ...,\n        \"backstage.image.repository.name\": ...,\n        \"backstage.image.tag.name\": ...,\n        \"backstage.parent.domain.name\": ...,\n        \"backstage.subdomain.label\": ...,\n        \"backstage.hosted.zone.id\": ...,\n        \"backstage.certificate.resource.name\": ...,\n        \"backstage.database.resource.name\": ...,\n        \"backstage.database.instance.port\": ...,\n        \"backstage.database.secret.resource.name\": ...,\n        \"backstage.database.username\": ...,\n        \"backstage.database.secret.target.name\": ...,\n      }\n```\n\nYou can assign values to the above keys according to the following criteria (values are required where you don't see _default_ mentioned):\n\n- \"backstage.namespace.name\": Backstage's namespace, the default is \"backstage\"\n- \"backstage.image.registry.name\": the image registry for the Backstage Helm chart in Amazon ECR, a value similar to \"youraccount.dkr.ecr.yourregion.amazonaws.com\"\n- \"backstage.image.repository.name\": the image repository for the Backstage Helm chart, the default is \"backstage\"\n- \"backstage.image.tag.name\": the image tag, the default is \"latest\"\n- \"backstage.parent.domain.name\": the parent domain in your Hosted Zone\n- \"backstage.subdomain.label\": to be used as _{\"subdomain.label\"}.{\"parent.domain.name\"}_, the default is \"backstage\"\n- \"backstage.hosted.zone.id\": the Hosted zone ID (format: 20x chars/numbers)\n- \"backstage.certificate.resource.name\": resource name of the certificate, registered by the resource provider, the default is \"backstage-certificate\"\n- \"backstage.database.resource.name\": resource name of the database, registered by the resource provider, the default is \"backstage-database\"\n- \"backstage.database.instance.port\": the port the database will use, the default is 5432\n- \"backstage.database.secret.resource.name\": resource name of the database's Secret, registered by the resource provider, the default is \"backstage-database-credentials\"\n- \"backstage.database.username\": the username for the database's credentials, the default is \"postgres\"\n- \"backstage.database.secret.target.name\": the name to be used when creating the Secret, the default is \"backstage-database-secret\"\n\nIf you haven't done it before, [bootstrap your cdk account and region](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html).\n\nRun the following commands:\n\n```sh\nmake deps\nmake build\nmake pattern backstage deploy\n```\nWhen deployment completes, the output will be similar to the following:\n\n![Backstage deployment output](./images/backstage-console-output.png)\n\nNavigate to the URL indicated by the first line in the output (_backstage-blueprint.BackstagebaseURL ...), you should see the screen below:\n\n![Backstage console](./images/backstage-screen.png)\n\nTo see the deployed resources within the cluster, please run:\n\n```sh\nkubectl get pod,svc,secrets,ingress -A\n```\n\nA sample output is shown below:\n\n![Backstage kubectl output](./images/backstage-kubectl-output.png)\n\n## Next steps\n\nYou can go the [AWS Blog](https://aws.amazon.com/blogs/) to explore how to use Backstage e.g., [as an API Developer Portal for Amazon API Gateway](https://aws.amazon.com/blogs/opensource/how-traveloka-uses-backstage-as-an-api-developer-portal-for-amazon-api-gateway/) or [to provision infrastructure using AWS Proton](https://aws.amazon.com/blogs/containers/provisioning-infrastructure-using-the-aws-proton-open-source-backstage-plugin/). On the Backstage website you can also see other examples of [how to use and expand Backstage](https://backstage.io/demos/).\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern backstage destroy \n```\n"
  },
  {
    "path": "docs/patterns/batch.md",
    "content": "# AWS Batch on Amazon EKS Pattern\n\n## Objective\n\nAWS Batch helps you run batch computing workloads on AWS. Using Amazon EKS as the compute resource, you can now schedule and scale batch workloads into new or existing EKS cluster. As part of the deployment, AWS Batch doesn't create, administer, or perform lifecycle operations of the EKS cluster, but will only scale up and down the nodes maanged by AWS Batch and run pods on those nodes to complete batch jobs. \n\nThe objective of this pattern is to deploy AWS Batch on Amazon EKS using EKS Blueprints with the following features in place:\n- Batch addon implemented\n- Batch Team defined with a sample compute environment and job queue (as defined under `lib/teams/team-batch`) - This can be customized based on your needs\n- Fluent Bit addon implemented to monitor AWS Batch on Amazon EKS jobs using CloudWatch, with the proper permissions for sending logs"
  },
  {
    "path": "docs/patterns/crossplane-argocd-gitops.md",
    "content": "# GitOps based Multi-cluster add-on and Apps Management using Crossplane and ArgoCD\n\n## Objective\n\nThe objective of this pattern is to provide centralized management of Amazon EKS add-ons, Kubernetes Applications and Helm charts in workload clusters. This approach consists of a Management Cluster and multiple workload clusters. The Management Cluster is created with ArgoCD and Crossplane add-ons. The platform team creates Crossplane Manifest files for Amazon EKS add-ons/Kubernetes Applications/Helm charts and pushes them to the GitOps Repo. The ArgoCD Application Controller in the Management Cluster reconciles these Crossplane Manifests and deploy them into Management Cluster.  The Crossplane Controller in the Management Cluster deploys the Amazon EKS add-ons/Kubernetes Applications/Helm charts into the Workload Clusters.\n\nThis helps platform teams to simplify the process of deploying add-ons and Apps from a central Management Cluster. In this Solution, we use CDK to deploy AWS CodePipeline which monitors this platform repo and deploy the Management and Workload Clusters using CDK EKS Blueprints.\n\n## Architecture\n\n![crossplane-argocd-gitops](./images/crossplane-argocd-gitops.png)\n\n## Approach\n\nThis blueprint will include the following:\n\n* AWS CodePipeline which deploys the Management and Workload Clusters\n* A new Well-Architected EKS cluster `eks-mgmt-cluster` and two workload EKS Clusters `workload-amd-1-29-blueprint` and `workload-arm-1-29-blueprint` in the region and account you specify.\n* [Amazon VPC CNI add-on (VpcCni)](https://docs.aws.amazon.com/eks/latest/userguide/managing-vpc-cni.html) into your cluster to support native VPC networking for Amazon EKS.\n*  The Management Cluster is deployed with the following add-ons.\n      * Upbound Universal Crossplane Provider\n      * Upbound AWS Family Crossplane Provider\n      * Upbound AWS EKS Crossplane Provider\n      * Kubernetes Crossplane Provider\n      * Helm Crossplane Provider\n      * Secrets Store AddOn\n      * ArgoCD add-on\n* The ArgoCD add-on is bootstrapped with [GitOps](https://github.com/aws-samples/eks-blueprints-workloads) which contains Crossplane Manifest files to deploy EKS add-ons, Kubernetes Manifests and also Helm Charts.\n\n## GitOps Configuration\n\nFor GitOps, the blueprint bootstrap the ArgoCD add-on and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\n\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. [helm](https://helm.sh/docs/intro/install/)\n6. GitHub Access Token for this repo and AWS secret\n\n### Create AWS Secret Manager Secret\n\nCreate a plain-text Amazon secret to hold a fine-grained GitHub access token for this repo in the desired region, and\nset its name as a value to the GITHUB_SECRET environment variable. Default value is `cdk_blueprints_github_secret`.\n\n> **WARNING:** When switching the CDK between region, remember to replicate this secret!!!!\n\n```shell\nexport ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)\nexport AWS_REGION=\"us-west-2\"\nexport CDK_REPO_GITHUB_PAT_TOKEN=<set_token_here>\nexport CDK_REPO_AWS_SECRET_NAME=\"cdk_blueprints_github_secret\"\naws secretsmanager create-secret --region $AWS_REGION \\\n    --name $CDK_REPO_AWS_SECRET_NAME \\\n    --description \"GitHub Personal Access Token for CodePipeline to access GitHub account\" \\\n    --secret-string $CDK_REPO_GITHUB_PAT_TOKEN\n```\n\n## Deploy\n\n1. Clone the repository and install dependency packages. This repository contains CDK v2 code written in TypeScript.\n\n```\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\nnpm i\n```\n\n2. Execute the commands below to bootstrap the AWS environment\n\n```\ncdk bootstrap aws://$ACCOUNT_ID/$AWS_REGION\n```\n\n4. Run the following command from the root of this repository to deploy the pipeline stack:\n\n```\nmake clean\nmake build\nmake list\nmake pattern crossplane-argocd-gitops deploy\n```\n\n## Cluster Access\n\n### View the CodePipeline\n\n![codepipeline1](./images/codepipeline1.png)\n\n![codepipeline2](./images/codepipeline2.png)\n\n### Access the Management EKS cluster\n\nIn this section, let us create a kube-context for the Management cluster and ensure that the ArgoCD and Crossplane add-ons are deployed successfully.\n\n1. Run the below command to get the AWS command from CloudFormation Stack `eks-mgmt-cluster-stage-eks-mgmt-cluster-stage-blueprint` outputs\n\n\nThe example command looks like below.\n\n```shell\nexport CFNOutputKey=$(aws cloudformation describe-stacks \\\n    --stack-name eks-mgmt-cluster-stage-eks-mgmt-cluster-stage-blueprint \\\n    --query 'Stacks[].Outputs[].OutputKey' | jq -r '.[]|select(. | startswith(\"mgmtclusterstageblueprintConfigCommand\"))')\necho $CFNOutputKey\n\nexport mgmtclusterstageblueprintConfigCommand=$(aws cloudformation describe-stacks \\\n    --stack-name eks-mgmt-cluster-stage-eks-mgmt-cluster-stage-blueprint \\\n    --query 'Stacks[].Outputs[?OutputKey==`'$CFNOutputKey'`].OutputValue' \\\n     --output text)\necho  $mgmtclusterstageblueprintConfigCommand\n```\n\n2. Run below command to create the kube-context for the Management cluster.\n\n```shell\n$mgmtclusterstageblueprintConfigCommand\n```\n\nThe output will look like below.\n\n```shell\nUpdated context arn:aws:eks:us-west-2:ACCOUNT_ID:cluster/eks-eks-mgmt-cluster in /Users/<user_name>/.kube/config\n```\n\n3. Copy the context in the output above and set an environment variable \n\n```shell\nexport MANAGEMENT_CLUSTER_CONTEXT=\"arn:aws:eks:${AWS_REGION}:${ACCOUNT_ID}:cluster/eks-eks-mgmt-cluster\"\necho \"export  MANAGEMENT_CLUSTER_CONTEXT=${MANAGEMENT_CLUSTER_CONTEXT}\" >> ~/.bash_profile\n```\n\n4. Run below command to validate the access to the cluster\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get node\n```\n\nThe output will like below.\n\n```shell\nNAME                           STATUS   ROLES    AGE   VERSION\nip-10-0-137-3.ec2.internal     Ready    <none>   18h   v1.29.6-eks-1552ad0\nip-10-0-169-194.ec2.internal   Ready    <none>   18h   v1.29.6-eks-1552ad0\n```\n\n5. Run below command to get the list of Crossplane Providers deployed in the cluster\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get providers.pkg.crossplane.io\n```\n\nThe output will like below.\n\n```shell\nNAME                          INSTALLED   HEALTHY   PACKAGE                                                          AGE\nhelm-provider                 True        True      xpkg.upbound.io/crossplane-contrib/provider-helm:v0.19.0         18h\nkubernetes-provider           True        True      xpkg.upbound.io/crossplane-contrib/provider-kubernetes:v0.13.0   18h\nprovider-aws-eks              True        True      xpkg.upbound.io/upbound/provider-aws-eks:v1.1.0                  18h\nupbound-provider-family-aws   True        True      xpkg.upbound.io/upbound/provider-family-aws:v1.13.0 \n```\n6. Run below command to get the Crossplane Providers pods in the `upbound-system` Namespace.\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get pod -n upbound-system\n```\n\nThe output will like below.\n\n```shell\nNAME                                                        READY   STATUS    RESTARTS   AGE\ncrossplane-594b65bfdb-pgkxf                                 1/1     Running   0          6d8h\ncrossplane-rbac-manager-86c74cf5d-tjcw8                     1/1     Running   0          6d8h\nhelm-provider-4d90a08b9ede-7c874b858b-pp26d                 1/1     Running   0          47h\nkubernetes-provider-a3cbbe355fa7-55846cfbfb-6tpcl           1/1     Running   0          25h\nprovider-aws-eks-23042d28ed58-66d9db8476-jr6mb              1/1     Running   0          6d8h\nupbound-provider-family-aws-bac5d48bd353-64845bdcbc-4vpn6   1/1     Running   0          6d8h            8d\n```\n7. Run below command to get the ArgoCD pods deployed in the `argocd` Namespace.\n\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get pod -n argocd\n```\n\nThe output will like below.\n\n```shell\nNAME                                                              READY   STATUS    RESTARTS       AGE\nblueprints-addon-argocd-application-controller-0                  1/1     Running   0              24h\nblueprints-addon-argocd-applicationset-controller-7b78c7fc94ls9   1/1     Running   0              24h\nblueprints-addon-argocd-dex-server-6cf94ddc54-dfhv7               1/1     Running   0              24h\nblueprints-addon-argocd-notifications-controller-6f6b7d95cdd2tl   1/1     Running   0              24h\nblueprints-addon-argocd-redis-b8dbc7dc6-h4bs8                     1/1     Running   0              24h\nblueprints-addon-argocd-repo-server-fd57dc686-zkbsm               1/1     Running   0              4h15m\nblueprints-addon-argocd-server-84c8b597c9-98c95                   1/1     Running   0              24h\n```\n\n### Access to the Workload clusters using IAM role `eks-workload-connector-role` \n\nNote that we create and add an IAM role eks-workload-connector-role with system:masters RBAC access to both of the workload clusters i.e. workload-amd-1-29-blueprint and workload-arm-1-29-blueprint as part of the Stack creation.\n\nThe Upbound AWS EKS Provider Pod will use its IRSA role to assume the `eks-workload-connector-role` to gain access to the workload clusters. The `sts:AssumeRole` IAM permission is already added to the IRSA role during the Management cluster creation.\n\nWe will create two Crossplane objects of type `ClusterAuth` to create kube-context to access the Workload clusters using the IAM role `eks-workload-connector-role`\n\nWe will also create two Crossplane objects of type `Addon` to deploy Amazon EKS add-ons into the Workload clusters. To deploy add-ons, the AWS EKS Provider Pod needs `eks:*` IAM permissions, which are already added to `eks-workload-connector-role` during cluster creation.\n\nNote this IAM permissions can be made very granular to provide least privileged access to workload clusters.\n\n\n### Access the Workload EKS cluster `workload-amd-1-29-blueprint` \n\nIn this section, let us create a kube-context and verify access to the Workload cluster `workload-amd-1-29-blueprint`\n\n>Note that we have added an IAM role eks-workload-connector-role with system:masters RBAC access to both of the workload clusters i.e. workload-amd-1-29-blueprint and workload-arm-1-29-blueprint.\n\n1. Run the command to create the kube-context for the cluster.\n\n```shell\naws eks update-kubeconfig --name workload-amd-1-29-blueprint --region ${AWS_REGION} --role-arn \"arn:aws:iam::${ACCOUNT_ID}:role/eks-workload-connector-role\"\n```\n\n2. Copy the context in the output above and set an environment variable.\n\n```shell\nexport WORKLOAD_CLUSTER1_CONTEXT=\"arn:aws:eks:${AWS_REGION}:${ACCOUNT_ID}:cluster/workload-amd-1-29-blueprint\"\necho \"export  WORKLOAD_CLUSTER1_CONTEXT=${WORKLOAD_CLUSTER1_CONTEXT}\" >> ~/.bash_profile\n```\n3. Run below command to validate the access to the cluster.\n\n```shell\nkubectl --context $WORKLOAD_CLUSTER1_CONTEXT get node\n```\n\n### Access the Workload EKS cluster `workload-arm-1-29-blueprint` \n\nIn this section, let us create a kube-context and verify access to the Workload cluster `workload-arm-1-29-blueprint`\n\n>Note that we have added an IAM role eks-workload-connector-role with system:masters RBAC access to both of the workload clusters i.e. workload-amd-1-29-blueprint and workload-arm-1-29-blueprint.\n\n1. Run the command to create the kube-context for the cluster.\n\n```shell\naws eks update-kubeconfig --name workload-arm-1-29-blueprint --region ${AWS_REGION} --role-arn \"arn:aws:iam::${ACCOUNT_ID}:role/eks-workload-connector-role\"\n```\n2. Copy the context in the output above and set an environment variable.\n\n```shell\nexport WORKLOAD_CLUSTER2_CONTEXT=\"arn:aws:eks:${AWS_REGION}:${ACCOUNT_ID}:cluster/workload-arm-1-29-blueprint\"\necho \"export  WORKLOAD_CLUSTER2_CONTEXT=${WORKLOAD_CLUSTER1_CONTEXT}\" >> ~/.bash_profile\n```\n3. Run below command to validate the access to the cluster.\n\n```shell\nkubectl --context $WORKLOAD_CLUSTER1_CONTEXT get node\n```\n\n## Test \n\n### Install the ArgoCD CLI\n\n1. Install the ArgoCD CLI as per the [docs](https://argo-cd.readthedocs.io/en/stable/cli_installation/)\n\n2. Get the ArgoCD Admin password using below command.\n\n```shell\nkubectl --context $MANAGEMENT_CLUSTER_CONTEXT  -n argocd get secret argocd-initial-admin-secret -o jsonpath=\"{.data.password}\" | base64 -d; echo\n```\n\n3. Open a **New Terminal** and Run a local proxy server for the ArgoCD Server.\n\n```shell\nkubectl --context $MANAGEMENT_CLUSTER_CONTEXT port-forward svc/blueprints-addon-argocd-server -n argocd 8080:443\n```\n4. In the current Terminal run the ArgoCD login command.\n\n```shell\nargocd login localhost:8080 --username admin --password <admin_password>\n```\n\n5. Add Management EKS cluster to ArgoCD.\n\n```shell\nargocd cluster add $MANAGEMENT_CLUSTER_CONTEXT\n```\nThe output will look like below.\n\n```shell\nWARNING: This will create a service account `argocd-manager` on the cluster referenced by context `arn:aws:eks:us-west-2:ACCOUNT_ID:cluster/eks-mgmt-cluster` with full cluster level privileges. Do you want to continue [y/N]? y\nINFO[0004] ServiceAccount \"argocd-manager\" already exists in namespace \"kube-system\" \nINFO[0004] ClusterRole \"argocd-manager-role\" updated    \nINFO[0005] ClusterRoleBinding \"argocd-manager-role-binding\" updated \nCluster 'https://0F745A41ECA76297CBF070C032932033.sk1.us-west-2.eks.amazonaws.com' added\n```\n\n6. Run the below command to get the list of ArgoCD Applications.\n\n```shell\nargocd app list\n```\n\nThe output will look like below.\n\n```shell\nNAME                   CLUSTER                         NAMESPACE  PROJECT  STATUS  HEALTH   SYNCPOLICY  CONDITIONS  REPO                                                        PATH                      TARGET\nargocd/bootstrap-apps  https://kubernetes.default.svc  argocd     default  Synced  Healthy  Auto-Prune  <none>      https://github.com/aws-samples/eks-blueprints-workloads  ./crossplane-argocd-gitops/envs/dev  main\nargocd/team-spock        https://kubernetes.default.svc  argocd     default  Synced  Healthy  Auto-Prune  <none>      https://github.com/aws-samples/eks-blueprints-workloads  ./teams/team-spock/dev        main\n```\n\n\n### Validate EKS add-ons deployment in Workload Clusters\n\n1. Run the below command to get the list of `ProviderConfig` Crossplane CRD objects deployed in the Management cluster\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get providerconfigs.aws.upbound.io\n```\n\nThe output will look like below.\n\n```shell\nNAME                         AGE\ncommon-provider-config-aws   23h\n```\n\n2. Run the below command to get the list of `Addon` Objects deployed in the Management cluster. \n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get addons.eks.aws.upbound.io\n```\n\nThe output will look like below.\n\n```shell\nNAME                                    READY   SYNCED   EXTERNAL-NAME                               AGE\naddon-eks-pod-identity-agent-amd-1-29   True    True     workload-amd-1-29-blueprint:eks-pod-identity-agent   4h15m\naddon-eks-pod-identity-agent-arm-1-29   True    True     workload-arm-1-29-blueprint:eks-pod-identity-agent   4h15m\n```\n\n3. Go to the Workload EKS Clusters and Ensure that EKS add-on is deployed successfully.\n\n![workload-amd-1-29-blueprint EKS add-on](./images/amd-add-on.png)\n\n![workload-arm-1-29-blueprint EKS add-on](./images/arm-add-on.png)\n\n### Validate Kubernetes Manifests deployment in Workload clusters\n\n1. Run the below command to get the list of Crossplane Kubernetes `ProviderConfig` objects deployed in the Management cluster.\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get providerconfigs.kubernetes.crossplane.io\n```\n\nThe output will look like below.\n\n```shell\nNAME                                     AGE\nprovider-config-k8s-workload-amd-1-29-blueprint   4h31m\nprovider-config-k8s-workload-arm-1-29-blueprint   4h40m\n```\n\n2. Run the below command to get the list of Namespaces in the Workload cluster  `workload-amd-1-29-blueprint`\n\n```shell\nkubectl  --context $WORKLOAD_CLUSTER1_CONTEXT get ns\n```\n\nThe output will look like below.\n\n```shell\nNAME                                STATUS   AGE\ndefault                             Active   8d\nexternal-secrets                    Active   8d\nkube-node-lease                     Active   8d\nkube-public                         Active   8d\nkube-system                         Active   8d\ntest-namespace-workload-amd-1-29-blueprint   Active   4h9m\n```\n\n3. Run the below command to get the list of Namespaces in the Workload cluster  `workload-arm-1-29-blueprint`\n\n```shell\nkubectl  --context $WORKLOAD_CLUSTER2_CONTEXT get ns\n```\n\nThe output will look like below.\n\n```shell\nNAME                                STATUS   AGE\ndefault                             Active   8d\nexternal-secrets                    Active   8d\nkube-node-lease                     Active   8d\nkube-public                         Active   8d\nkube-system                         Active   8d\ntest-namespace-workload-arm-1-29-blueprint   Active   4h9m\n```\n\n### Validate Helm Chart deployment in Workload clusters\n\n1. Run the below command to get the list of Crossplane Helm Provider Objects deployed in the Management Cluster.\n\n```shell\nkubectl  --context $MANAGEMENT_CLUSTER_CONTEXT get providerconfigs.helm.crossplane.io\n```\n\nThe output will look like below.\n\n```shell\nNAME                                      AGE\nprovider-config-helm-workload-amd-1-29-blueprint   4h37m\nprovider-config-helm-workload-arm-1-29-blueprint   4h46m\n```\n\n2. Run the below command to get the list of helm charts in the Workload Cluster  `workload-amd-1-29-blueprint`\n\n```shell\nhelm  --kube-context $WORKLOAD_CLUSTER1_CONTEXT list -A\n```\n\nThe output will look like below.\n\n```shell\nNAME                                    NAMESPACE               REVISION        UPDATED                                  STATUS          CHART                   APP VERSION\nblueprints-addon-external-secrets       external-secrets        1               2024-05-07 05:25:31.465715836 +0000 UTC  deployed        external-secrets-0.9.9  v0.9.9     \ntest-helm-workload-amd-1-29-blueprint            default                 1               2024-05-15 06:39:17.325950143 +0000 UTC  deployed        nginx-17.0.1            1.26.0  \n```\n\n3. Run the below command to get the list of Helm Charts in the Workload cluster  `workload-arm-1-29-blueprint`\n\n```shell\nhelm  --kube-context $WORKLOAD_CLUSTER2_CONTEXT list -A\n```\n\nThe output will look like below.\n\n```shell\nNAME                                    NAMESPACE               REVISION        UPDATED                                  STATUS          CHART                   APP VERSION\nblueprints-addon-external-secrets       external-secrets        1               2024-05-07 05:26:52.028907405 +0000 UTC  deployed        external-secrets-0.9.9  v0.9.9     \ntest-helm-workload-arm-1-29-blueprint            default                 1               2024-05-15 06:39:17.222351682 +0000 UTC  deployed        nginx-17.0.1            1.26.0   \n```\n\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern crossplane-argocd-gitops destroy \n```\nThe above command deletes the AWS CodePipeline `crossplane-argocd-gitops`. However to complete the Cleanup, delete the following CloudFormation Stacks manually using AWS Console or AWS CLI using below commands.\n\n```sh\naws cloudformation delete-stack --stack-name workload-amd-1-29-workload-amd-1-29-blueprint\naws cloudformation delete-stack --stack-name workload-arm-1-29-workload-arm-1-29-blueprint\naws cloudformation delete-stack --stack-name mgmt-cluster-stage-mgmt-cluster-stage-blueprint\n```\n\n\n\n\n"
  },
  {
    "path": "docs/patterns/custom-networking-with-ipv4.md",
    "content": "# Custom Networking on EKS\nOn Amazon EKS clusters, the default Container Networking Interface(CNI) is implemented by the Amazon VPC CNI plugin. When VPC CNI is used in EKS clusters, by default the VPC CNI assigns pods an IP address that's selected from the primary subnet of the VPC. The primary subnet is the subnet CIDR that the primary Elastic Network Interface(ENI) is attached to; usually it's the subnet of the worker node/host in the EKS cluster. If the primary subnet CIDR is too small, the CNI may not be able to have enough IP addresses to assign to the pods running in the cluster. This is a common challenge for EKS IPv4 clusters.\n\nCustom Networking provides a solution to the IP exhaustion issue by assigning the Pod IPs from secondary VPC address spaces(CIDR). When custom networking is enabled in VPC CNI, it creates secondary ENIs in the subnet defined under a custom resource named ENIConfig that includes an alternate subnet CIDR range (carved from a secondary VPC CIDR). The VPC CNI assigns pods IP addresses from the CIDR range defined in the ENIConfig Custom Resource Definition(CRD).\n\nUsing the Custom Networking with IPv4 pattern, you should be able to stand up an EKS cluster with VPC CNI installed and configured with custom networking enabled.\n\n\nThis pattern deploys the following resources:\n\n- Creates EKS Cluster Control plane with a managed node group \n- Deploys supporting add-ons: VpcCni, CoreDns, KubeProxy, AWSLoadBalancerController\n- Enables Custom Networking configuration in VpcCni AddOn \n\n\n\n## Prerequisites:\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. [yq](https://github.com/mikefarah/yq/#install)\n6. `make`\n\nAmazon EKS add-ons are only available with Amazon EKS clusters running Kubernetes version 1.18 and later.\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\n### Check Versions\n\nMake sure that, following versions are installed.\nNode version is a current stable node version 18.x.\n\n```\nnode -v\nv18.12.1\n```\nNPM version must be 8.4 or above:\n\n```\nnpm -v\n8.19.2\n```\n\n### Clone the cdk-blueprints-patterns github repository\n\n```\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\n```\n\n### Install project dependencies\n\nOnce you have cloned the above repository, you can open it using your favourite IDE and run the below command to install the dependencies and build the existing patterns.\n\n`make deps`\n\n### To view patterns that are available to be deployed, execute the following:\n\n```\nnpm i\nmake build\n```\n\nTo list the existing CDK EKS Blueprints patterns, run\n\n`make list`\n\n### Bootstrap your CDK environment\n\n`npx cdk bootstrap`\n\nYou can now proceed with deployment of the `custom-networking-ipv4` pattern.\n\n### To deploy the custom-networking-ipv4 pattern, run \n\n`make pattern custom-networking-ipv4 deploy`\n\nOnce the deployment is successful, run `update-kubeconfig` command to update the kubeconfig file with required access. You should be able to get the command from CDK output message.\n\n```\naws eks update-kubeconfig --name custom-networking-ipv4-blueprint --region $AWS_REGION --role-arn arn:aws:iam::$AWS_ACCOUNT_ID:role/custom-networking-ipv4-bl-customnetworkingipv4blue-2SR7PW3UBLIH\n```\n\nYou can verify the resources created by executing\n\n```\nkubectl get node -o wide\n```\n\nOutput:\n\n```\nNAME                                        STATUS   ROLES    AGE   VERSION                INTERNAL-IP   EXTERNAL-IP     OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME\nip-10-0-18-208.us-east-2.compute.internal   Ready    <none>   70m   v1.24.11-eks-a59e1f0   10.0.18.208   18.116.23.237   Amazon Linux 2   5.10.173-154.642.amzn2.x86_64   containerd://1.6.19\nip-10-0-61-228.us-east-2.compute.internal   Ready    <none>   70m   v1.24.11-eks-a59e1f0   10.0\n```\n\n### Under the Hood\n\nThis pattern first creates secondary CIDRs and secondary subnets with specified range of CIDRs as shown below in resourceProvider. Then the VPC CNI addon sets up custom networking based on the parameters `awsVpcK8sCniCustomNetworkCfg`, `eniConfigLabelDef: \"topology.kubernetes.io/zone\"` for your Amazon EKS cluster workloads with secondary subnet ranges.\n\n* When the secondary CIDRs are passed to the VPC resource provider, the secondary subnets are created and registered under names `secondary-cidr-subnet-${order}` with the resource providers.\n* We enable CNI plugin with custom pod networking with below environment variables:\n    * `AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG` = `true`\n    * `ENI_CONFIG_LABEL_DEF` = `topology.kubernetes.io/zone`\n\nThis deploys an ENIConfig custom resource for pod subnets (one per availability zone).\n\n```\nimport 'source-map-support/register';\nimport * as cdk from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nconst app = new cdk.App();\nconst addOn = new blueprints.addons.VpcCniAddOn({\n  customNetworkingConfig: {\n      subnets: [\n          blueprints.getNamedResource(\"secondary-cidr-subnet-0\"),\n          blueprints.getNamedResource(\"secondary-cidr-subnet-1\"),\n          blueprints.getNamedResource(\"secondary-cidr-subnet-2\"),\n      ]   \n  },\n  awsVpcK8sCniCustomNetworkCfg: true,\n  eniConfigLabelDef: 'topology.kubernetes.io/zone'\n});\nconst blueprint = blueprints.EksBlueprint.builder()\n  .addOns(addOn)\n  .resourceProvider(blueprints.GlobalResources.Vpc, new blueprints.VpcProvider(undefined, {\n                primaryCidr: \"10.2.0.0/16\", \n                secondaryCidr: \"100.64.0.0/16\",\n                secondarySubnetCidrs: [\"100.64.0.0/24\",\"100.64.1.0/24\",\"100.64.2.0/24\"]\n            }))\n  .build(app, 'my-stack-name');\n  ```\n\nIn the diagram shown below, a secondary CIDR (100/64) is assigned to each private subnet that gets created in an availability zone. Worker nodes in the EKS cluster still gets an IP address from the Primary CIDRs(10.0) range whereas the pods get an IP address from the secondary CIDR range.\n\n![Custom-NW-IPv4](./images/custom-nw-mng.png)\n\nThis can be verified by issuing the following command\n\n```\nkubectl get eniconfig\n```\n\nOutput:\n\n```\nNAME         AGE\nus-east-2a   47m\nus-east-2b   47m\nus-east-2c   47m\n```\n\nAn ENIConfig custom resource is created in each AZ.  Number of secondary ENIs associated with the Worker node varies by instance type.\n\n![Custom-NW-MNG](./images/custom-nw-mng.png)\n\n## Additional Configuration Options\n\nVPC CNI AddOn provides some knobs to add additional advanced configuration on top of custom networking.\n\n### Prefix Delegation\n\nWhen using custom networking mode, since the node’s primary ENI is no longer used to assign Pod IP addresses, there is a decrease in the number of Pods that can run on a given EC2 instance type. To work around this limitation you can use prefix delegation with custom networking. This is an important capability because when you use custom networking, only Pods that are configured to use hostNetwork are “bound” to the host’s primary ENI. All other Pods are bound to secondary ENIs. However, with prefix delegation enabled, each secondary IP is replaced with a /28 prefix which negates the IP address loss when you use custom networking.\n\nBy default, Prefix Delegation is turned off in Vpc Cni. To check this, run the following command.\n\n```\nkubectl get ds aws-node -o yaml -n kube-system | yq '.spec.template.spec.containers[].env'\n```\n\nOutput:\n\n```\n[...]\n\n- name: ENABLE_PREFIX_DELEGATION\n\n  value: \"false\"\n\n[...]\n```\n\nConsider the maximum number of Pods for an m5.large instance with custom networking.\nWhen using custom networking, the maximum number of Pods you can run without prefix delegation enabled is 20.\n\nDownload and run max-pods-calculator.sh script to calculate the maximum number of pods:\n\n```\ncurl -o max-pods-calculator.sh https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/max-pods-calculator.sh\nchmod +x max-pods-calculator.sh\n/max-pods-calculator.sh \\\n    --instance-type m5.large \\\n    --cni-version 1.12.5-eksbuild.2 \\\n    --cni-custom-networking-enabled\n```\n\nOutput:\n\n```\n20\n```\n\nTo turn on `Prefix Delegation`, use the following command\n\n```\nkubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true\n```\n\nOutput:\n`110`\n\n![Custom-NW-Bar-Chart](./images/Custom-nw-bar-chart.png)\n\nThe reason we got max-pods is 110 instead of 290 is because the instance has a relatively low number of vCPUs. In addition the Kubernetes community recommends set max Pods no greater than 10 * number of cores, up to 110. Since Vpc Cni runs as a daemonset, you’d need to create new nodes for this to take effect.\n\nThe number of ENIs and IP addresses in a pool are configured through environment variables called `WARM_ENI_TARGET`, `WARM_IP_TARGET`, `MINIMUM_IP_TARGET`. For more details on these options, please refer to [EKS Best Practices Networking](https://aws.github.io/aws-eks-best-practices/networking/vpc-cni/#overview) Guide.\n\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n\n```sh\nmake pattern custom-networking-ipv4 destroy \n```\n\n"
  },
  {
    "path": "docs/patterns/generative-ai/showcase.md",
    "content": "# Using Gen AI to run a prompt showcase with Bedrock and Amazon EKS\n\n## Objective\n\n[Amazon Bedrock](https://aws.amazon.com/bedrock/) is a fully managed service for using foundation models. It allows you to access models from Amazon and third parties with a single set of APIs for both text generation and image generation.\n\n[LangChain](https://python.langchain.com/) provides convenient functions for interacting with Amazon Bedrock's models and related services like vector databases. LangChain offers Python and JavaScript libraries. For this workshop, we will use the Python version of LangChain.\n\n[Streamlit](https://streamlit.io/) allows us to quickly create web front ends for our Python code, without needing front-end development skills. Streamlit is great for creating proofs-of-concepts that can be presented to a wide audience of both technical and non-technical people.\n\nIn this pattern we will demonstrate a prompt showcase use case with Gen AI using Bedrock and Amazon EKS. This usecase will demonstrate a prompt showcase which uses different prompt templates such as Summarization, Sentiment and Recommendation with user input to generate a response using Generative AI. In this model we will running a containerized application on Amazon EKS which integrates with Bedrock to provide required user response.\n\n## Architecture\n\n![Showcase Architecture](../images/generativeai-showcase-architecture.jpg)\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine:\n\n- [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) (also ensure it is [configured](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html#getting-started-quickstart-new))\n- Bedrock is currently in preview. Please make sure your AWS account is enabled to use Bedrock\n- [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n- [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n- [tsc](https://www.typescriptlang.org/download)\n- [make](https://www.gnu.org/software/make/)\n- [Docker](https://docs.docker.com/get-docker/)\n\nLet’s start by setting the account and region environment variables:\n\n```sh\nACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)\nAWS_REGION=$(aws configure get region)\n```\n\nClone the repository:\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns/lib/generative-ai-showcase/python\n```\nCreate the ECR image repository and push the docker image to ECR for your showcase app:\n\n```sh\nIMAGE_NAME=bedrock-showcase\nIMAGE_TAG=v2\naws ecr create-repository --repository-name $IMAGE_NAME\naws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com\ndocker build -t $IMAGE_NAME .\ndocker tag bedrock-showcase:latest $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$IMAGE_TAG\ndocker push $ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/$IMAGE_NAME:$IMAGE_TAG\ncd ../../../../\n```\n\n## Deployment\n\nIf you haven't done it before, [bootstrap your cdk account and region](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html).\n\nSet the pattern's parameters in the CDK context by overriding the _cdk.json_ file:\n\n```sh\ncat << EOF > cdk.json\n{\n    \"app\": \"npx ts-node dist/lib/common/default-main.js\",\n    \"context\": {\n        \"bedrock.pattern.name\": \"showcase\",\n        \"bedrock.pattern.namespace\": \"bedrock\",\n        \"bedrock.pattern.image.name\": \"${ACCOUNT_ID}.dkr.ecr.$AWS_REGION.amazonaws.com/${IMAGE_NAME}\",\n        \"bedrock.pattern.image.tag\": \"${IMAGE_TAG}\"\n      }\n}\nEOF\n```\n\nRun the following commands:\n\n```sh\nmake deps\nmake build\nmake pattern generative-ai-showcase deploy\n```\nWhen deployment completes, the output will be similar to the following:\n\n```output\n ✅  generative-ai-showcase-blueprint\n\n✨  Deployment time: 1287.16s\n\nOutputs:\ngenerative-ai-showcase-blueprint.generativeaishowcaseblueprintClusterNameA8D25DA0 = generative-ai-showcase-blueprint\ngenerative-ai-showcase-blueprint.generativeaishowcaseblueprintConfigCommandC6A8442C = aws eks update-kubeconfig --name generative-ai-showcase-blueprint --region us-east-1 --role-arn arn:aws:iam::XXXXXXXXXX:role/generative-ai-showcase-bl-generativeaishowcaseblue-L18IUPGQ8M2I\ngenerative-ai-showcase-blueprint.generativeaishowcaseblueprintGetTokenCommand5AE22878 = aws eks get-token --cluster-name generative-ai-showcase-blueprint --region us-east-1 --role-arn arn:aws:iam::XXXXXXXXXX:role/generative-ai-showcase-bl-generativeaishowcaseblue-L18IUPGQ8M2I\nStack ARN:\narn:aws:cloudformation:us-east-1:XXXXXXXXXX:stack/generative-ai-showcase-blueprint/cd2c4d90-5317-11ee-9c8d-0e69cfd9ba55\n\n✨  Total time: 1290.99s\n```\n\nTo see the deployed resources within the cluster, please run:\n\n```sh\nkubectl get pod,svc,secrets,ingress -A\n```\n\nA sample output is shown below:\n\n```output\nNAME                                          READY   STATUS    RESTARTS   AGE\npod/bedrock-showcase-model-586b558b46-bkwql   1/1     Running   0          60s\n\nNAME                                     TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE\nservice/bedrock-showcase-model-service   NodePort   172.20.12.47   <none>        80:30451/TCP   10m\n\nNAME                                                       CLASS   HOSTS   ADDRESS                                                                 PORTS   AGE\ningress.networking.k8s.io/bedrock-showcase-model-ingress   alb     *       k8s-bedrock-bedrocks-63d6186d4e-765982776.us-east-1.elb.amazonaws.com   80      10m\n```\n\nNext, Navigate to the URL show under Ingress to see the below screen to interact with Generative AI showcase application by selecting different promptsand inputs and see the result :\n\n![Showcase application](../images/generativeai-showcase-demo-output.jpg)\n\n## Next steps\n\nYou can go [AWS Blogs](https://aws.amazon.com/blogs/) to learn about [New Tools for Building with Generative AI on AWS](https://aws.amazon.com/blogs/machine-learning/announcing-new-tools-for-building-with-generative-ai-on-aws/). Also check on another blog our on [Enabling Foundation Models to Complete Tasks With Agents for Amazon Bedrock](https://aws.amazon.com/blogs/aws/preview-enable-foundation-models-to-complete-tasks-with-agents-for-amazon-bedrock/). \n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern generative-ai-showcase destroy \n```\n"
  },
  {
    "path": "docs/patterns/gmaestro.md",
    "content": "# gMaestro on EKS pattern\n\ngMaestro is a Kubernetes cost optimization solution that helps companies reduce spending on un-utilized resources.\nFor additional information, visit [gMaestro documentation](https://docs.gomaestro.org/).\n\n\nThis pattern deploys the following resources:\n- Creates a single EKS cluster that includes a managed node group\n- Deploys supporting add-ons: ClusterAutoScaler and MetricsServer\n- Deploys a single granulate-gmaestro deployment with a single pod on the EKS cluster\n\n\n## Prerequisites\nBefore using gMaestro, you need to:\n1. [Sign up](https://app.granulate.io/gMaestroSignup) to the gMaestro platform\n2. Download a config YAML file - After signing up to gMaestro, navigate to the [Deploy](https://app.granulate.io/deploy) on the left-hand menu, fill in the required fields and click on \"Generate Config File\" as shown bellow:\n\n![GmaestroGenerateConfigFile](images/gmaestro-generate-config-file.png)\n\n![GmaestroConfigFile](images/gmaestro-config-file.png)\n\n3. Create a secret (as a plaintext, not key/value) in AWS Secrets Manager:\n    ```bash\n    export MAESTRO_CLIENT_ID=\"<MAESTRO_CLIENT_ID value from the deployment section in the downloaded config file>\"\n    export MAESTRO_SECRET_NAME=\"<MAESTRO_SECRET_NAME your preferred secret name>\"\n    aws secretsmanager create-secret --name <MAESTRO_SECRET_NAME> --region $AWS_REGION \\\n        --description \"Encrypted client ID for Granulate gMaestro\" \\\n        --secret-string \"<MAESTRO_CLIENT_ID>\"\n    ```\n\n4. Follow the usage [instructions](../../README.md#usage) to install the dependencies\n   \n## Deployment\n\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nIf you haven't done it before, [bootstrap your cdk account and region](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html).\n\nUpdate `context` in `cdk.json` file located in the `cdk-eks-blueprints-patterns` directory as follows:\n\n```json\n\"context\": {\n    \"clusterName\": \"<MAESTRO_SERVICE_NAME value from the deployment section in the downloaded config file>\",\n    \"namespace\": \"<Where gMaestro will be installed>\",  \n}\n```\n\nRun the following commands:\n\n```sh\nmake deps\nmake build\nmake pattern gmaestro deploy\n```\n\n## Verify the resources\n\nUse the following command to validate that gMaestro installed successfully:\n\n```bash\n$ kubectl get pods -A | grep granulate-maestro\n\nNAMESPACE     NAME                                 READY   STATUS    RESTARTS   AGE\ndefault       granulate-maestro-6947dc87bc-k5nfc   1/1     Running   0          11m\n```\n\nAfter a few seconds, you will gain full visibility into your K8s cluster objects:\n\n![GmaestroRecommendations](images/gmaestro-recommendations.png)\n\nThe first rightsizing recommendations may take up to 5 minutes to load.\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern gmaestro destroy\n```\n\n## Support\n\nIf you have questions about gMaestro, catch us [on Slack](https://granulatecommunity.slack.com/archives/C03RK0HN2TU)!\n\n## Disclaimer\n\nThis pattern relies on an open-source NPM package gmaestro-eks-blueprints-addon. Please refer to the package npm site for more information.\n<https://www.npmjs.com/package/@granulate/gmaestro-eks-blueprints-addon>\n\nIf you have any questions about the npm package or find any defect, please post in the source repo at \n<https://github.com/Granulate/gmaestro-eks-blueprints-addon>"
  },
  {
    "path": "docs/patterns/graviton.md",
    "content": "# Graviton on EKS\n\nAWS Graviton processors are designed by AWS to deliver the best price performance for your cloud workloads running in Amazon EC2. These processors are ARM chips running on aarch64 architecture.\n\nAWS Graviton processors are supported by many Linux operating systems including Amazon Linux 2, Red Hat Enterprise Linux, SUSE, and Ubuntu. Many popular applications and services for security, monitoring and management, containers, and continuous integration and delivery (CI/CD) from AWS and software partners also support AWS Graviton-based instances.\n\nAWS Graviton processors feature key capabilities that enable you to run cloud native applications securely, and at scale. EC2 instances powered by AWS Graviton processors are built on the AWS Nitro System that features the AWS Nitro security chip with dedicated hardware and software for security functions, and support for encrypted Amazon Elastic Block Store (EBS) volumes by default.\n\n### Why an M7g instance?\nThere are 7 families of Graviton instances split into 5 categories.\nGeneral Purpose: M and T families\nCompute Optimized: C family\nMemory Optimized: R and X family\nStorage Optimized: I family\nAccelerated Computing: G family\n\nFor a blueprint pattern, the General Purpose and Compute Optimized categories make the most sense, since they are the most common use cases and are all Nitro-Enabled instances.  Being Nitro-Enabled means that these instances provide better networking security as well as increased performance compared to non Nitro-Enabled instances.  In these categories, there are 7 different instance types: M7g, M6g, T4g, C7g, C7gn, C6g, and C6gn.  T4g instances are specialized for burstable workloads, and both T4g and M6g instances are Graviton2 chips. M7g instances are Graviton3 chips, which offer 25% better compute performace than Graviton2 and support DDR5 memory that provides 50% more bandwith compared to DDR4. C6g and C6gn instances are also Graviton2 chips, and C7g instances are specialized for high performance computing.  For this general blueprint pattern, the M7g instance is the best option due to the high compute power, memory bandwith, networking bandwith, and broad use cases.\n\nThis pattern deploys the following resources:\n\n- Creates EKS Cluster Control plane with a managed node group running on an M family Graviton3 processor\n\n### Addons\nNot all of the listed EKS addons support the Graviton processors. To find a list of supported addons, visit the [documentation](https://github.com/aws-quickstart/cdk-eks-blueprints/blob/main/docs/addons/index.md).\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. `make`\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nUpdating npm\n\n```sh\nnpm install -g npm@latest\n```\n\nTo view patterns and deploy graviton pattern\n\n```sh\nmake list\nnpx cdk bootstrap\nmake pattern graviton deploy\n```\n\n## Verify the resources\n\nRun the update-kubeconfig command. You should be able to get the command from the CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n\n```sh\naws eks update-kubeconfig --name graviton-blueprint --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/graviton-construct-bluepr-gravitonconstructbluepri-1OZNO42GH3OCB\n```\n\nLet's verify the resources created from the steps above.\n\n```sh\nkubectl get nodes -o json | jq -r '.items[] | \"Name: \",.metadata.name,\"\\nInstance Type: \",.metadata.labels.\"beta.kubernetes.io/instance-type\",\"\\nArch: \",.metadata.labels.\"beta.kubernetes.io/arch\",\"\\n\"' # Output shows node on M famGraviton3 processor and ARM architecture\n```\n\n## Cleanup\n\nTo clean up your EKS Blueprint, run the following command:\n\n```sh\nmake pattern graviton destroy\n```\n"
  },
  {
    "path": "docs/patterns/instana.md",
    "content": "# IBM Instana on EKS pattern\nThe IBM® Instana® Addon for Amazon EKS Blueprint is designed to enhance observability, monitoring, and management capabilities for applications running on Amazon Elastic Kubernetes Service (EKS). Instana Addon focuses on enhancing the user experience by reducing the complexity and time required to install and configure an Instana host agent on Amazon EKS cluster.\n\nThis Addon will use IBM® Instana® Agent Operator in the namespace ```instana-agent``` to install and manage Instana Agent. It also configures custom resource values to configure the operator.\n\nThis pattern deploys the following resources:\n\n- Creates EKS Cluster Control plane with public endpoint (for demo purpose only) with a managed node group\n- Install and set up Instana Agent for monitoring your EKS workloads. (by using the provided environment variable and additional configuration parameters)\n\n\n## Prerequisites:\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. Instana backend application - Use SaaS (eg [aws](https://aws.amazon.com/marketplace/pp/prodview-hnqy5e3t3fzda?sr=0-1&ref_=beagle&applicationId=AWSMPContessa)) or Install self-hosted Instana backend ([on-premises](https://www.ibm.com/docs/en/instana-observability/current?topic=installing-configuring-self-hosted-instana-backend-premises))\n\n## Project Setup\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\n```\n\nGo inside project directory (eg. cdk-eks-blueprints-patterns)\n\n```sh\ncd cdk-eks-blueprints-patterns\n```\n\nInstall project dependencies.\n\n```sh\nmake deps\n```\n\n\n## Instana Agent Configuration\nGo to your Instana Backend application (Instana User Interface), click ... More > Agents > Installing Instana Agents and select 'Kubernetes' platform to get the Instana Agent Key, Instana Service Endpoint, Instana Service port. These steps are also described on the screenshot below.\n\n[Instana Agent Configuration](./images/instana-agent.png)\n\n\n## Usage : Using AWS Secret Manager Secrets \n### AWS Secret Manager Secrets (Optional)\nIf you wish to use AWS Secret Manager Secrets to pass Instana props (key, endpoint, and port), then you will be required to setup Secrets first.\n\n```shell\nexport SECRET_NAME=<aws_secret_name>\nexport INSTANA_AGENT_KEY=<instana_key>\nexport INSTANA_ENDPOINT_HOST_URL=<instana_host_endpoint>\nexport INSTANA_ENDPOINT_HOST_PORT=<instana_port>\"\naws secretsmanager create-secret \\\n  --name $SECRET_NAME \\\n  --secret-string \"{\\\"INSTANA_AGENT_KEY\\\":\\\"${INSTANA_AGENT_KEY}\\\",\n    \\\"INSTANA_ENDPOINT_HOST_URL\\\":\\\"${INSTANA_ENDPOINT_HOST_URL}\\\",\n    \\\"INSTANA_ENDPOINT_HOST_PORT\\\":\\\"${INSTANA_ENDPOINT_HOST_PORT}\\\"\n   }\"\n```\nsecret_name = AWS Secret Manager Secret name (eg. *instana-secret-params*).\n\n\n### Using AWS Secret Manager Secrets\nTo use AWS Secret Manager Secrets follow these steps:\n\n1. The actual settings for the secret name (```secretParamName```) are expected to be specified in the CDK context. Generically it is inside the cdk.context.json file of the current directory or in `~/.cdk.json` in your home directory.\n\n\t Example settings: Update the context in `cdk.json` file located in `cdk-eks-blueprints-patterns` directory\n\t ```json\n\t\"context\": {\n         \"secretParamName\": \"instana-secret-params\"\n     }\n    ```\n\n2. Go to project/lib/instana-construct/index.ts\n\n```typescript\nimport { loadYaml } from \"@aws-quickstart/eks-blueprints/dist/utils\";\nimport * as cdk from \"aws-cdk-lib\";\nimport { InstanaOperatorAddon } from \"@instana/aws-eks-blueprint-addon\";\nimport { EksBlueprint, utils } from \"@aws-quickstart/eks-blueprints\";\nimport { prevalidateSecrets } from \"../common/construct-utils\";\n\nexport const instanaProps: { [key: string]: any } = {};\n\nexport default class InstanaConstruct {\n    async buildAsync(scope: cdk.App, id: string) {\n        try {\n            await prevalidateSecrets(InstanaConstruct.name, undefined, 'instana-secret-params');\n\n            const secretParamName: string = utils.valueFromContext(scope, \"secretParamName\", undefined);\n            if(secretParamName != undefined) {\n                instanaProps.secretParamName = secretParamName;\n            }\n            const yamlObject = loadYaml(JSON.stringify(instanaProps));\n            const stackId = `${id}-blueprint`;\n            const addOns = new InstanaOperatorAddon(yamlObject);\n            EksBlueprint.builder()\n                .account(process.env.CDK_DEFAULT_ACCOUNT!)\n                .region(process.env.CDK_DEFAULT_REGION!)\n                .addOns(addOns)\n                .build(scope, stackId);\n            console.log(\"Blueprint built successfully.\");\n        } catch (error) {\n            console.error(\"Error:\", error);\n            throw new Error(`environment variables must be setup for the instana-operator pattern to work`);\n        }\n    }\n}\n```\n\n## Usage : Using Secrets in the Code\n\n### Setting up environment variable\nTo set the following environment variables from the CLI, use the corresponding values obtained from the Instana Service Endpoint and Port (as shown in the above screenshot), and the Instana Application Key (also shown in the above screenshot):\n\n- Set the value of **INSTANA_ENDPOINT_HOST_URL** to the Instana Service Endpoint.\n- Set the value of **INSTANA_ENDPOINT_HOST_PORT** to the Instana Service Port.\n- Set the value of **INSTANA_AGENT_KEY** to the Instana Application Key.\n\nSet the value of the following environment variable and run it on CLI to set those variables.\n\nFor an example:\n\n```shell\nexport INSTANA_AGENT_KEY=abc123\nexport INSTANA_ENDPOINT_HOST_URL=instana.example.com\nexport INSTANA_ENDPOINT_HOST_PORT=\"443\"\n```\n\n### Configure additional configuration parameters.\nTo configure additional parameters for Instana Agent according to your specific use case, follow these steps:\n\n- Go to project/lib/instana-construct/index.ts\n- Add the additional configuration parameters under ```const instanaProps``` variable.\n\nFor an example:\n\n```typescript\nexport const instanaProps = {\n agent: {\n    key: process.env.INSTANA_AGENT_KEY,// Mandatory Parameter\n    endpointHost: process.env.INSTANA_ENDPOINT_HOST_URL,//Mandatory Parameter\n    endpointPort: process.env.INSTANA_ENDPOINT_HOST_PORT, // Mandatory Parameter,\n    env: {\n\t\tINSTANA_AGENT_TAGS: \"staging\",\n    }\n  }\n};\n```\n\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\nTo view patterns and deploy ```instana-operator``` pattern\n\n```sh\nmake deps\nmake build\ncdk bootstrap\nmake pattern instana-operator deploy\n```\n\n\n## Verify the resources\n\nRun update-kubeconfig command. You should be able to get the command from CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n```sh\naws eks update-kubeconfig --name <your cluster name> --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/eks-blue1-eksblue1AccessRole32C5DF05-1NBFCH8INI08A\n```\n\nLets verify the resources created by Steps above.\n```sh\nkubectl get pods -n instana-agent # Output shows the EKS Managed Node group nodes under instana-agent namespace\n```\nOutput of the above command will be silimar to below one:\n\n```output\nNAMESPACE       NAME                                  \t\t\t\tREADY   \tSTATUS    RESTARTS   AGE\ninstana-agent   controller-manager-78479cb596-sktg9   \t1/1     \tRunning   \t\t\t\t\t0          56m\ninstana-agent   controller-manager-78479cb596-xz8kn   \t1/1     \tRunning   \t\t\t\t\t0          56m\ninstana-agent   instana-agent-gsqx8                   \t\t\t\t1/1     \tRunning   \t\t\t\t\t0          56m\n```\nRun following command to verify Instana Agent logs\n```shell\nkubectl logs <instana-agent-pod-name> -n instana-agent # Output shows instana agent logs. pod name in this example is instana-agent-gsqx8\n```\n\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern instana-operator destroy \n\n```\n\n## Disclaimer \nThis pattern relies on an open source NPM package [aws-eks-blueprint-addon](https://www.npmjs.com/package/%40instana/aws-eks-blueprint-addon). Please refer to the package npm site for more information.\n```\nhttps://www.npmjs.com/package/@instana/aws-eks-blueprint-addon'\n```\nIf you have any questions about the npm package or find any defect, please post in the source repo at:\nhttps://github.com/instana/instana-eks-blueprint-addon/issues\n"
  },
  {
    "path": "docs/patterns/jupyterhub.md",
    "content": "# JupyterHub on EKS Pattern\n\n## Objective\n\nJupyterHub is a multi-user Hub that spawns, manages, and proxies multiple instances of the single-user Jupyter notebook server. The hub can offer notebook servers to a class of students, a corporate data science workgroup, a scientific research project, or a high-performance computing group.\n\nThe objective of this pattern is to deploy JupyterHub on EKS using EKS Blueprints with the following features in place:\n- JupyterHub is hosted behind an ALB on EKS cluster across multiple AZs\n- JupyterHub allows for user friendly DNS name to route traffic to the load balancer, which is a subdomain of a parent domain in a separate account. This is representatitve of a typical global enterprise domain setup, where a central, global DNS account defines the parent domain (in Route53). The subdomain will be defined in Route53 from this account where the JupyterHub cluster is provisioned.\n- JupyterHub leverages an identity provider for user authentication.\n- JupyterHub uses persistent storage that is provided within a file system (i.e. EFS) when the user logs in\n- JupyterHub uses certificates to provide secured connection to the hub (the load balancer) \n- The hub has a persistent storage with an EBS volume\n\n## Approach\n\nSince we will be defining subdomains for a global enterprise domain across multiple environments, which are as a rule placed in separate AWS accounts, root domain should defined in a separate account. Let's call it global DNS account. \n\nOur blueprint will then include the following:\n\n1. AWS Loadbalancer controller to provision an ALB instance fronting the Kubernetes Ingress resource for the JupyterHub server. Deployed with a public certificate created from ACM (Certificate ARN must be provided post-creation via CDK context)\n2. External DNS to integrate ALB with Route53 and use custom domain to access the hub. \n3. Configurations to leverage existing user management via OAuth 2.0 protocol standard (i.e. Auth0).\n4. EFS file server for user persistent storage using the Blueprints.\n5. EBS volume for hub persistent storage.\n\n## Prerequisites\n1. Identity Provider that can be leveraged using 0Auth 2.0 protocol. The actual settings are expected to be specified in the CDK context. Generically it is inside the cdk.context.json file of the current directory or in `~/.cdk.json` in your home directory. Example settings:\n```\n{\n  \"context\": {\n    \"callbackUrl\": \"https://your.hub.domain.com/hub/oauth_callback\",\n    \"authUrl\": \"https://some.auth.address.com/authorize\",\n    \"tokenUrl\": \"https://some.auth.address.com/oauth/token\",\n    \"userDataUrl\": \"https://some.auth.address.com/userinfo\",\n    \"clientId\": \"someClientID\",\n    \"clientSecret\": \"someClientSecret\",\n    \"scope\": [\"openid\",\"name\",\"profile\",\"email\"],\n    \"usernameKey\": \"name\"\n  }\n}\n```\n2. The parent domain must be defined in a separate account (GLOBAL_DNS_ACCOUNT).\n3. The GLOBAL_DNS_ACCOUNT must contain a role with a trust policy to the workload(s) account. We naed it `DomainOperatorRole` but you can choose any arbitrary name for it.\n   1. Policies:  `arn:aws:iam::aws:policy/AmazonRoute53DomainsFullAccess` or alternatively you can provide `arn:aws:iam::aws:policy/AmazonRoute53ReadOnlyAccess` and `arn:aws:iam::aws:policy/AmazonRoute53AutoNamingFullAccess`.\n   2. Trust relationship to allow workload accounts to create subdomains (replace `<WORKLOAD_ACCOUNT>` with the actual value): \n   ```\n   {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::<WORKLOAD_ACCOUNT>:root\"\n            },\n            \"Action\": \"sts:AssumeRole\",\n            \"Condition\": {}\n        }\n    ]\n   }\n   ```\n4. The actual settings for the GLOBAL_DNS_ACCOUNT, hosted zone name, subzone name, and the JupyterHub hub subdomain names are expected to be specified in the CDK context. Generically it is inside the cdk.context.json file of the current directory or in `~/.cdk.json` in your home directory. Example settings:\n```\n{\n  \"context\": {\n    \"parent.dns.account\": \"<PARENT_ACCOUNT>\",\n    \"parent.hostedzone.name\": \"domain.com\",\n    \"dev.subzone.name\": \"hub.domain.com\",\n    \"jupyterhub.subzone.name\":\"your.hub.domain.com\",\n  }\n}\n```\n\n## Deploying\n\nOnce all pre-requisites are set you should be able to get a working cluster with all the objectives met, including a JupyterHub where users can log in using their credentials from the identity provider given.\n\n"
  },
  {
    "path": "docs/patterns/karpenter.md",
    "content": "# Karpenter on EKS\n\nKarpenter add-on is based on the [Karpenter](https://github.com/aws/karpenter) open source node provisioning project. It provides a more efficient and cost-effective way to manage workloads by launching just the right compute resources to handle a cluster's application.\n\nKarpenter works by:\n\n- Watching for pods that the Kubernetes scheduler has marked as unschedulable,\n- Evaluating scheduling constraints (resource requests, nodeselectors, affinities, tolerations, and topology spread constraints) requested by the pods,\n- Provisioning nodes that meet the requirements of the pods,\n- Scheduling the pods to run on the new nodes, and\n- Removing the nodes when the nodes are no longer needed\n\nTo learn more about Karpenter add on usage, please visit the documentation [here](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/karpenter/)\nThis pattern deploys the following resources:\n\n- Creates EKS Cluster Control plane with public endpoint (for demo purpose only) with a managed node group\n- Deploys supporting add-ons: AwsLoadBalancerController, VpcCni, CoreDns, KubeProxy,  CertManagerAddOn, KubeStateMetricsAddOn, MetricsServer\n- Deploy Karpenter on the EKS cluster\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. `make`\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nUpdating npm\n\n```sh\nnpm install -g npm@latest\n```\n\nTo view patterns and deploy karpenter pattern\n\n```sh\nmake list\nnpx cdk bootstrap\nmake pattern karpenter deploy\n```\n\n## Verify the resources\n\nRun the update-kubeconfig command. You should be able to get the command from the CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n\n```sh\naws eks update-kubeconfig --name karpenter-blueprint --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/karpenter-construct-bluepr-karpenterconstructbluepri-1OZNO42GH3OCB\n```\n\nLet's verify the resources created from the steps above.\n\n```bash\n# Assuming add-on is installed in the karpenter namespace.\n$ kubectl get po -n karpenter\nNAME                                          READY   STATUS    RESTARTS   AGE\nkarpenter-54fd978b89-hclmp   2/2     Running   0          99m\n```\n\n###  Testing with a sample deployment\n\nNow that the provisioner is deployed, Karpenter is active and ready to provision nodes. Create some pods using a deployment:\n\n```bash\ncat <<EOF | kubectl apply -f -\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: inflate\nspec:\n  replicas: 0\n  selector:\n    matchLabels:\n      app: inflate\n  template:\n    metadata:\n      labels:\n        app: inflate\n    spec:\n      terminationGracePeriodSeconds: 0\n      containers:\n        - name: inflate\n          image: public.ecr.aws/eks-distro/kubernetes/pause:3.2\n          resources:\n            requests:\n              cpu: 1\nEOF\n```\n\nNow scale the deployment:\n\n```bash\nkubectl scale deployment inflate --replicas 10\n```\n\nThe provisioner will then start deploying more nodes to deploy the scaled replicas. You can verify by either looking at the karpenter controller logs,\n\n```bash\nkubectl logs -f -n karpenter karpenter-54fd978b89-hclmp\n```\n\nor, by looking at the nodes being created:\n\n```bash\nkubectl get nodes\n```\n\n"
  },
  {
    "path": "docs/patterns/konveyor.md",
    "content": "# Konveyor Add-On for Amazon EKS Blueprints\n\n[Konveyor](https://konveyor.github.io/konveyor/) is an open-source application modernization platform that helps organizations safely and predictably modernize applications to new technologies, with an initial focus on accelerating the adoption of legacy applications to Kubernetes. Konveyor’s goal is to deliver a Unified Experience to the organizations embarking on their modernization journey. It follows a simple yet effective approach of surfacing the information about the application to aid a ‘Decision Maker’ to make decisions about their modernization and migration needs, plan the work in the form of ‘Migration waves’ and provide guidance to the developers to complete the needed migration/modernization by providing assets as well as a catalog of integrated tools to aid specific workflows.\n\nFeature set\n\n- Konveyor Hub: Central interface from where you manage your application portfolio and integrate with other Konveyor tools.\n\n- Categorize and group applications by different dimensions (pre-packaged or custom) aligned with technical criteria or your organization structure.\n\n- Assess applications containerization suitablity and risks assessment.\n\n- Assign priority, assess estimated migration effort, and define optimal migration strategy for individual applications.\n\n- Evaluate required changes for Java applications containerization through automated analysis (pre-packaged or custom rules).\n\n- Fully integrated Konveyor Data Intensive Validity Advisor (DiVA): Analyzes the data layer of applications and detect dependencies to different data stores and distributed transactions. Import target Java application source files to generate analysis results.\n\n[Konveyor](https://www.konveyor.io/) is an Open Source software developed by the Konveyor Community, and is a [CNCF](https://www.cncf.io/) Sandbox project.\n\nThis Open Source solution is packaged by [Claranet Switzerland](https://www.claranet.ch/) GmbH.\n\n## Arhcitecture\n\n<img src=\"./images/konveyor-architecture.png\" width=700 />\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine:\n\n- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) (also ensure it is [configured](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html#getting-started-quickstart-new))\n- [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n- [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n- [tsc](https://www.typescriptlang.org/download)\n- [make](https://www.gnu.org/software/make/)\n\nLet’s start by setting the account and region environment variables:\n\n```sh\nACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)\nAWS_REGION=$(aws configure get region)\n```\n\n## Deployment\n\nClone the repository:\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nSet the pattern's parameters in the CDK context by overriding the _cdk.json_ file (edit _PARENT_DOMAIN_NAME_ as it fits):\n\n```sh\nPARENT_DOMAIN_NAME=example.com\nHOSTED_ZONE_ID=$(aws route53 list-hosted-zones-by-name --dns-name $PARENT_DOMAIN_NAME --query \"HostedZones[].Id\" --output text | xargs basename)\ncat << EOF > cdk.json\n{\n    \"app\": \"npx ts-node dist/lib/common/default-main.js\",\n    \"context\": {\n        \"konveyor.parent.domain.name\":\"${PARENT_DOMAIN_NAME}\",\n        \"konveyor.hosted.zone.id\": \"${HOSTED_ZONE_ID}\"\n      }\n}\nEOF\n```\n\n(Optional) The full list of parameters you can set in the _context_ is:\n\n```\n    \"context\": {\n        \"konveyor.namespace.name\": ...,\n        \"konveyor.parent.domain.name\": ...,\n        \"konveyor.subdomain.label\": ...,\n        \"konveyor.hosted.zone.id\": ...,\n        \"konveyor.certificate.resource.name\": ...,\n      }\n```\n\nYou can assign values to the above keys according to the following criteria (values are required where you don't see _default_ mentioned):\n\n- \"konveyor.namespace.name\": Konveyor's namespace, the default is \"konveyor\"\n- \"konveyor.parent.domain.name\": the parent domain in your Hosted Zone\n- \"konveyor.subdomain.label\": to be used as _{\"subdomain.label\"}.{\"parent.domain.name\"}_, the default is \"backstage\"\n- \"konveyor.hosted.zone.id\": the Hosted zone ID (format: 20x chars/numbers)\n- \"konveyor.certificate.resource.name\": resource name of the certificate, registered by the resource provider, the default is \"konveyor-certificate\"\n\nIf you haven't done it before, [bootstrap your cdk account and region](https://docs.aws.amazon.com/cdk/v2/guide/bootstrapping.html).\n\nRun the following commands:\n\n```sh\nmake deps\nmake build\nmake pattern konveyor deploy\n```\n\nWhen deployment completes, the output will be similar to the following:\n\n<img src=\"./images/konveyor-cdk-output.png\"/>\n\n## Log in\n\nOnce the deployment ends navigate to\n\n`https://<subdomain>.<parent-domain>`\n\nAnd enter the default admin credentials:\n\n- Username: `admin`\n- Password: `Passw0rd!`\n\n## Koveyor UI\n\nLogin page\n\n<img src=\"./images/konveyor-login.png\" width=500/>\n\nHome Page\n\n<img src=\"./images/konveyor-home.png\" widht=500/>\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern konveyor destroy\n```\n"
  },
  {
    "path": "docs/patterns/kubeflow.md",
    "content": "# Kubeflow on EKS\nThe Kubeflow project is dedicated to making deployments of machine learning (ML) workflows on Kubernetes simple, portable and scalable.\nOur goal is not to recreate other services, but to provide a straightforward way to deploy best-of-breed open-source systems for ML to diverse infrastructures.\nAnywhere you are running Kubernetes, you should be able to run Kubeflow.\n\nThis pattern deploys the following resources:\n\n- Creates EKS Cluster Control plane with public endpoint (for demo purpose only) with a managed node group\n- Deploys supporting add-ons: ClusterAutoScaler, AwsLoadBalancerController, VpcCni, CoreDns, KubeProxy, EbsCsiDriver, CertManagerAddOn, KubeStateMetricsAddOn, PrometheusNodeExporterAddOn, AdotCollectorAddOn, AmpAddOn,\n- Deploy Kubeflow on the EKS cluster\n\n\n## Prerequisites:\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n\n\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\n```\n\nUpdating npm\n\n```sh\nnpm install -g npm@latest\n```\n\nTo view patterns and deploy kubeflow pattern\n\n```sh\nmake list\ncdk bootstrap\nmake pattern kubeflow deploy\n```\n\n\n## Verify the resources\n\n\nRun update-kubeconfig command. You should be able to get the command from CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n```sh\naws eks update-kubeconfig --name <your cluster name> --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/kubeflow-blueprint-kubeflowblueprintMastersRole0C1-saJBO\n```\n\nLet’s verify the resources created by Steps above.\n```sh\nkubectl get nodes # Output shows the EKS Managed Node group nodes\n\nkubectl get ns | kubeflow # Output shows kubeflow namespace\n\nkubectl get pods --namespace=kubeflow-pipelines  # Output shows kubeflow pods\n```\n\n\n## Execute Machine learning jobs on Kubeflow\nlog into Kubeflow pipeline UI by creating a port-forward to the ml-pipeline-ui service<br>\n\n```sh\nkubectl port-forward svc/ml-pipeline-ui 9000:80 -n =kubeflow-pipelines\n\n```\nand open this browser: http://localhost:9000/#/pipelines\nmore pipeline examples can be found at https://www.kubeflow.org/docs/components/pipelines/legacy-v1/tutorials/\n\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n\n```sh\ncdk destroy kubeflow-blueprint \n\n```\n\n## Disclaimer \nThis pattern relies on an open source NPM package eks-blueprints-cdk-kubeflow-ext. Please refer to the package npm site for more information.\nhttps://www.npmjs.com/package/eks-blueprints-cdk-kubeflow-ext\n"
  },
  {
    "path": "docs/patterns/kubeshark.md",
    "content": "# Kubeshark AddOn\n\n[kubeshark](https://github.com/kubeshark/kubeshark)  is an API Traffic Analyzer for Kubernetes providing real-time, protocol-level visibility into Kubernetes’ internal network, capturing and monitoring all traffic and payloads going in, out and across containers, pods, nodes and clusters.\n\n\nThis pattern deploys the following resources:\n- Creates EKS Cluster Control plane with managed nodegroup\n- Install and set up kubeshark\n\n## Prerequisites:\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n\n## Project Setup\n1.) Clone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\n```\n\n2.) Go inside project directory (eg. cdk-eks-blueprints-patterns)\n\n```sh\ncd cdk-eks-blueprints-patterns\n```\n\n3.) Install project dependencies.\n\n```sh\nmake deps\n```\n\n4.) import kubeshark\n```\nnpm i kubeshark\n```\n5.) To view patterns and deploy kubeshark pattern, run the below command.\n```\nmake list\ncdk bootstrap\nmake pattern kubeshark deploy\n```\n## Verify the resources\n\nRun update-kubeconfig command. You should be able to get the command from CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n```sh\naws eks update-kubeconfig --name <your cluster name> --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/eks-blue1-eksblue1AccessRole32C5DF05-1NBFCH8INI08A\n```\n\n1.) verify the resources created by Steps above.\n```sh\n$ kubectl get deployments -n kube-system\n\nNAME                                                          READY   UP-TO-DATE   AVAILABLE   AGE\nblueprints-addon-kubeshark                               1/1     1            1           20m\n```\n\n\n2.) Access to kubeshark.\n\n```sh\n$ kubectl -n kube-system port-forward svc/kubeshark-front 3000:80\n```\n\nOpen the [dashboard](http://localhost:3000)\n\nThen you should be able to see view like this\n![dashboard](https://raw.githubusercontent.com/kubeshark/assets/master/png/kubeshark-ui.png)\n\n\n3.) deploy nginx pod using the below command.\n```\nkubectl apply -f - <<EOF\napiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx\nspec:\n  containers:\n  - name: nginx\n    image: nginx\nEOF\n```\n\n\n4.) Try to access \"aws.com\" to generate traffic flow using the below command.\n```\nkubectl exec nginx curl https://aws.com\n```\n\n\n5.) Access kubeshark using the below command.\n```\nkubectl -n kube-system port-forward svc/kubeshark-front 3000:80\n```\n\n\n6.) Run Kubeshark query to identify the traffic flow.\n```\n(src.pod.metadata.name == \"nginx\" or dst.pod.metadata name == \"nginx\") and request.questions[0].name == \"aws.com\" or (src.name == \"nginx\" and src.namespace == \"default\" and dst.name == \"kube-dns\" and dst.namespace == \"kube-system\")\n```\nAs shown below, the Kubeshark query used to identify the traffic flowing from the pod \"nginx\" in the \"default\" namespace to \"aws.com\" and \"coredns\". The query is writen by [Kubeshark Filter Language (KFL)](https://docs.kubeshark.co/en/filtering#kfl-syntax-reference) is the language implemented inside kubeshark/worker that enables the user to filter the traffic efficiently and precisely.\n\n![query](https://github.com/zghanem0/kubeshark/blob/main/api.png?raw=true)\n\nAlso you can visualize the traffic flow and bandwidth using service map feature as shown below.\n![Service Map](https://github.com/zghanem0/kubeshark/blob/main/map.png?raw=true)\n\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\nmake pattern kubeshark destroy \n```\n\n## Disclaimer \nThis pattern relies on an open source NPM package [aws-eks-blueprint-addon](https://www.npmjs.com/package/kubeshark). Please refer to the package npm site for more information.\n```\nhttps://www.npmjs.com/package/kubeshark'\n```\n\nIf you have any questions about the npm package or find any defect, please post in the source repo at:\nhttps://github.com/zghanem0/kubeshark/issues\n"
  },
  {
    "path": "docs/patterns/multi-cluster-conformitron.md",
    "content": "# Multi-cluster pattern with observability, cost optimizations and metrics aggregation\n\n## Objective\n\nThis pattern was started to solve a problem faced at AWS. We often get third-party software for validation and need a consistent automated approach to run Kubernetes evaluator testing, deployment of containerized products, and validation in Kubernetes environments on a variety of Amazon EKS environments. \n\nIn this pattern we:\n\n1. Automate deployment of multiple EKS cluster in a region, with a Continuous Deployment pipeline triggered upon a commit to the GitHub repository that hosts the pipeline configuration.\n\n1. Configure the EKS clusters to deploy with different architectures (x86 or ARM or Bottlerocket) and different Kubernetes versions (3 most recent by default).\n\n1. Automate testing of all the available [EKS Anywhere Addons](https://github.com/aws-samples/eks-anywhere-addons), on each of the clusters, essentially testing their compatibility across all the potential architecture/version available today on AWS. \n\n1. Deploying this pattern 24x7 we observed high costs (300$ a day). By using the AWS Systems Manager Automations and AutoScaling Groups we scale-down to zero during non-business hours resulting in 60% cost savings. We also borrowed optimized OTEL collector configurations from  [CDK Observability Accelerator](https://github.com/aws-observability/cdk-aws-observability-accelerator) to further reduce Prometheus storage costs. \n\nTo learn more about our EKS Addon validation checkout our [blog](https://aws.amazon.com/blogs/containers/conformitron-validate-third-party-software-with-amazon-eks-and-amazon-eks-anywhere/)\n\n![Architecture of multi-cluster deployment](images/conformitron.png)\n\n### GitOps confguration\n\nGitOps is a branch of DevOps that focuses on using Git code repositories to manage infrastructure and application code deployments.\n\nFor this pattern there is a git driven deployment using GitHub and Codepipeline which automatically redploys the EKS Clusters when modifications are made to the GitHub repo. \n\nSecondly, for the deployment of workloads on the cluster we leverage FluxCD, this a GitOps approach for the workloads i.e. the third-party-software we want to validate on our hardware.\n\nWe require some additional secrets to be created in Secrets Manager for the pattern to function properly\n\n1. AWS CodePipeline Bootstrap - The AWS CodePipeline points to the GitHub fork of this repository i.e [cdk-eks-blueprint-patterns] (https://github.com/aws-samples/cdk-eks-blueprints-patterns). \n\nA `github-token` secret must be stored as plaintext in AWS Secrets Manager for the CodePipeline to access the webhooks on GitHub. For more information on how/why to set it up, please refer to the [docs](https://docs.aws.amazon.com/codepipeline/latest/userguide/GitHub-create-personal-token-CLI.html). The GitHub Personal Access Token should have these scopes:\n   1. *repo* - to read your forked cdk-blueprint-patterns repostiory\n   1. *admin:repo_hook* - if you plan to use webhooks (enabled by default)\n\n1. FluxCD Bootstrap - The FluxCD points to the [EKS Anywhere Addons](https://github.com/aws-samples/eks-anywhere-addons) repository. Since this is a public repository you will not need to add a github token to read it.\n\n As part of the FluxCD configuration, it uses Kustomize to apply all the addons that are in the repository along with deploying their functional tests and a custom validator cronJob.\n\n\n## Prerequisites\nStart by setting the account and region environment variables:\n\n```sh\nexport ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)\nexport AWS_REGION=$(aws configure get region)\n```\n1. In case you haven't done this before, bootstrap your AWS Account for AWS CDK use using:\n\n    ```bash\n    cdk bootstrap\n    ```\n\n1. Fork this repository (cdk-eks-blueprints-patterns) to your GitHub organisation/user\n1. Git clone your forked repository onto your machine\n1. Install the AWS CDK Toolkit globally on your machine using\n\n    ```bash\n    npm install -g aws-cdk@2.133.0\n    ```\n\n1. Increase AWS service quota for required resources, navigate to [Service Quota Tutorial](https://aws.amazon.com/getting-started/hands-on/request-service-quota-increase/) to learn more\n```\n   SERVICE                                   | QUOTA NAME                         | REQUESTED QUOTA\n   Amazon Virtual Private Cloud (Amazon VPC) | NAT gateways per Availability Zone | 30 \n   Amazon Virtual Private Cloud (Amazon VPC) | VPCs per region                    | 30\n   Amazon Elastic Compute Cloud (Amazon EC2) | EC2-VPC Elastic IPs                | 30\n```\nWe are using seperate VPC as a best practice, but you can use default vpc if you prefer. Also, If you decide to use different regions for each cluster you dont need quota increase, please reach out if you have need for this use case.\n\n1. Amazon Managed Grafana Workspace: To visualize metrics collected, you need an Amazon Managed Grafana workspace. If you have an existing workspace, create environment variables `AMG_ENDPOINT_URL` as described below. \n\nElse, to create a new workspace, visit and run our [supporting example for Grafana Deployment](https://aws-observability.github.io/terraform-aws-observability-accelerator/helpers/managed-grafana/)\n\n```bash\nexport AMG_ENDPOINT_URL=https://g-xxx.grafana-workspace.region.amazonaws.com\nexport AMG_WORKSPACE_ID=g-xxx\n```\n\n1. Grafana API Key: Amazon Managed Grafana provides a control plane API for generating Grafana API keys or Service Account Tokens. This allows programatic provisioning of Grafana dashboards using the EKS grafana operator.\n\n    ```bash\n    export AMG_API_KEY=$(aws grafana create-workspace-api-key \\\n      --key-name \"grafana-operator-key\" \\\n      --key-role \"ADMIN\" \\\n      --seconds-to-live 432000 \\\n      --workspace-id $AMG_WORKSPACE_ID \\\n      --query key \\\n      --output text)\n    ```\n\n1. AWS SSM Parameter Store for GRAFANA API KEY: Update the Grafana API key secret in AWS SSM Parameter Store using the above new Grafana API key. This will be referenced by Grafana Operator deployment of our solution to access and provision Grafana dashboards from Amazon EKS monitoring Cluster\n\n```bash\naws ssm put-parameter --name \"/grafana-api-key\" \\\n    --type \"SecureString\" \\\n    --value $AMG_API_KEY \\\n    --region $AWS_REGION\n```\n\n1. Amazon Managed Prometheus Workspace: To store observability metrics from all clusters we will use Amazon Managed Prometheus due to it's ease of setup and easy integration with other AWS services. We recommend setting up a new seperate Prometheus workspace using the CLI commands below. The provisioning of a new AMP workspace can be automated by leveraging the `.resourceProvider` in our CDK blueprints. See [Example](https://github.com/aws-observability/cdk-aws-observability-accelerator/blob/main/lib/existing-eks-opensource-observability-pattern/index.ts). We intentionally left this out to allow to connecting with existing AMP deployments, but please reach out to us if you need guidance on automate this provisioning.\n\n```bash\naws amp create-workspace --alias conformitron\n```\n\nCopy the `workspaceID` from the output and export it as a variable\n\n```bash\nexport AMP_WS_ID=ws-xxxxxxx-xxxx-xxxx-xxxx-xxxxxx\n```\n\n\n1. Modify the code in your forked repo to point to your GitHub username/organisation. Open the [pattern file source code](../../lib/multi-cluster-construct/pipeline.ts) and look for the declared const of `gitOwner`. Change it to your GitHub username.\n\n\n## Deploying\n\nClone the repository:\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nSet the pattern's parameters in the CDK context by overriding the _cdk.json_ file (edit _PARENT_DOMAIN_NAME_ as it fits):\n```sh\ncat << EOF > cdk.json\n{\n    \"app\": \"npx ts-node dist/lib/common/default-main.js\",\n    \"context\": {\n        \"conformitron.amp.endpoint\": \"https://aps-workspaces.${AWS_REGION}.amazonaws.com/workspaces/${AMP_WS_ID}/\",\n        \"conformitron.amp.arn\":\"arn:aws:aps:${AWS_REGION}:${ACCOUNT_ID}:workspace/${AMP_WS_ID}\",\n        \"conformitron.amg.endpoint\": \"${AMG_ENDPOINT_URL}\",\n        \"conformitron.version\": [\"1.28\",\"1.29\",\"1.30\"],\n        \"fluxRepository\": {\n        \"name\": \"grafana-dashboards\",\n        \"namespace\": \"grafana-operator\",\n        \"repository\": {\n          \"repoUrl\": \"https://github.com/aws-observability/aws-observability-accelerator\",\n          \"name\": \"grafana-dashboards\",\n          \"targetRevision\": \"main\",\n          \"path\": \"./artifacts/grafana-operator-manifests/eks/infrastructure\"\n        },\n        \"values\": {\n          \"GRAFANA_CLUSTER_DASH_URL\" : \"https://raw.githubusercontent.com/aws-observability/aws-observability-accelerator/main/artifacts/grafana-dashboards/eks/infrastructure/cluster.json\",\n          \"GRAFANA_KUBELET_DASH_URL\" : \"https://raw.githubusercontent.com/aws-observability/aws-observability-accelerator/main/artifacts/grafana-dashboards/eks/infrastructure/kubelet.json\",\n          \"GRAFANA_NSWRKLDS_DASH_URL\" : \"https://raw.githubusercontent.com/aws-observability/aws-observability-accelerator/main/artifacts/grafana-dashboards/eks/infrastructure/namespace-workloads.json\",\n          \"GRAFANA_NODEEXP_DASH_URL\" : \"https://raw.githubusercontent.com/aws-observability/aws-observability-accelerator/main/artifacts/grafana-dashboards/eks/infrastructure/nodeexporter-nodes.json\",\n          \"GRAFANA_NODES_DASH_URL\" : \"https://raw.githubusercontent.com/aws-observability/aws-observability-accelerator/main/artifacts/grafana-dashboards/eks/infrastructure/nodes.json\",\n          \"GRAFANA_WORKLOADS_DASH_URL\" : \"https://raw.githubusercontent.com/aws-observability/aws-observability-accelerator/main/artifacts/grafana-dashboards/eks/infrastructure/workloads.json\"\n        },\n        \"kustomizations\": [\n          {\n            \"kustomizationPath\": \"./artifacts/grafana-operator-manifests/eks/infrastructure\"\n          }\n        ]\n    }\n  }\n}\n\nEOF\n```\n\nYou are now ready to deploy the pipeline. Run the following command from the root of this repository to deploy the pipeline stack:\n\n```bash\nmake pattern multi-cluster-conformitron deploy multi-cluster-central-pipeline\n```\n\nNow you can go to [AWS CodePipeline console](https://eu-west-1.console.aws.amazon.com/codesuite/codepipeline/pipelines), and see how it was automatically created to deploy multiple Amazon EKS clusters to different environments.\n\n## Grafana Dashboards\n![Dashboard 1](images/ConformitronDashboard1.png)\n\n![Dashboard 2](images/ConformitronDashboard2.png)\n\n![Dashboard 3](images/ConformitronDashboard3.png)\n\n\n# SSM Cost Optimizations for conformitron clusters\n\nRunning all the clusters by default for 24 hours results in a daily spend of $300+\n\nTo minimize these costs we have written a systems manager automation which automatically scales down autoscaling group to 0 desired nodes during off-business hours.\n\nOn weekdays 5 PM PST clusters are scaled to 0 -> CRON EXPRESSION:  `0 17 ? * MON-FRI *`\nOn weekdays 5 AM PST clusters are scaled to 1 -> CRON EXPRESSION:  `0 05 ? * MON-FRI *`\nOn weekends clusters stay scaled to 0.\n\nThese optimizations bring down the weekly cost to less than 1000$ essentially for a more than 60% cost savings.\n\nPlease find the SSM Automation documents `lib/multi-cluster-construct/resources/cost-optimization/scaleDownEksToZero.yml` and `lib/multi-cluster-construct/resources/cost-optimization/scaleUpEksToOne.yml`. \n\nLets take a look at one of the scripts `scaleDownEksToZero.yml`\n\n```yaml\nschemaVersion: '0.3'\n...\n...\nmainSteps:\n  ...\n  ...\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig      <---- Update the managed node group \n      clusterName: arm-1-26-blueprint <---- Modify according to your naming convention\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0                    <---- New Scaling Configuration\n        maxSize: 1\n        desiredSize: 0                <---- Scale To zero\n```\nBy triggering this automation at 5PM on Weekdays we automatically scale down clusters during off-hours.\n\nTo run these scripts first you will have to modify update them with your own account_ID\nWe will use `sed` command to automatically update the files\n```bash\nsed \"s/ACCOUNT_ID/$ACCOUNT_ID/g\" scaleDownEksToZero.yml > scaleDownEksToZeroNew.yml\nsed \"s/ACCOUNT_ID/$ACCOUNT_ID/g\" scaleUpEksToOne.yml > scaleUpEksToOneNew.yml\n```\n\n1. Then navigate to the Systems Manager > Documents and Create a new Automation.\n\n![Cost Optimization Step 1](images/CostOptimizationSSM1.png)\n\n1. Click on JSON and copy over the yml content to create a new runbook\n\n![Cost Optimization Step 2](images/CostOptimizationSSM2.png)\n\n1. Once saved, navigate to EventBridge > Scheduler > Schedules\n\n![Cost Optimization Step 3](images/CostOptimizationEventBridge.png)\n\n1. Create a new schedule with the CRON expression specified aboce\n\n![Cost Optimization Step 4](images/CostOptimizationEventBridge2.png)\n\n1. For Target select \"StartAutomationExecution\" and type in the document name from step 2\n\n![Cost Optimization Step 5](images/CostOptimizationEventBridge3.png)\n\n"
  },
  {
    "path": "docs/patterns/nginx.md",
    "content": "# NGINX Pattern\n\n## Objective\n\nWhen setting up a target platform across multiple dimensions that question of ingress must be solved. Ideally, it should work in such as way that workloads provisioned on the target environments could be accessible via internet exposing sub-domains of some predefined global domain name. \n\nCommunication with the workloads should leverage secure TLS protected Load balancer with proper public (or private) certificate.\n\nA single cluster will deploy workloads from multiple teams and each of them should be able to expose workloads routed to their corresponding namespace. So, teams are expected to define ingress objects. \n\nIn addition, this approach should work not only for a single cluster, but also across multiple regions and environments. \n\n## Approach\n\nSince we will be defining subdomains for a global enterprise domain across multiple environments, which are as a rule placed in separate AWS accounts, root domain should defined in a separate account. Let's call it global DNS account. \n\nSub-domains are then defined in the target accounts (let's call them workload accounts).\n\nOur blueprint will then include the following:\n\n1. NGINX ingress controller to enable teams to create/configure their ingress objects. \n2. External DNS to integrate NGINX and public-facing NLB with Route53. \n3. AWS Loadbalancer controller to provision an NLB instance with each cluster fronting the NGINX ingress. Deployed with a public certificate that will also be provisioned as part of the blueprint.\n4. Team onboarding that leverage the ingress capabilities through ArgoCD. \n5. Other popular add-ons.\n\n## Prerequisites\n1. `argo-admin-password` secret must be defined as plain text (not key/value) in `us-west-2`  region.\n2. The parent domain must be defined in a separate account (GLOBAL_DNS_ACCOUNT).\n3. The GLOBAL_DNS_ACCOUNT must contain a role with a trust policy to the workload(s) account. We naed it `DomainOperatorRole` but you can choose any arbitrary name for it.\n   1. Policies:  `arn:aws:iam::aws:policy/AmazonRoute53DomainsFullAccess` or alternatively you can provide `arn:aws:iam::aws:policy/AmazonRoute53ReadOnlyAccess` and `arn:aws:iam::aws:policy/AmazonRoute53AutoNamingFullAccess`.\n   2. Trust relationship to allow workload accounts to create subdomains (replace `<WORKLOAD_ACCOUNT>` with the actual value): \n   ```\n   {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": {\n                \"AWS\": \"arn:aws:iam::<WORKLOAD_ACCOUNT>:root\"\n            },\n            \"Action\": \"sts:AssumeRole\",\n            \"Condition\": {}\n        }\n    ]\n   }\n   ```\n4. The actual settings for the GLOBAL_DNS_ACCOUNT, hosted zone name and expected subzone name are expected to be specified in the CDK context. Generically it is inside the cdk.context.json file of the current directory or in `~/.cdk.json` in your home directory. Example settings:\n```\n{\n  \"context\": {\n    \"parent.dns.account\": \"<PARENT_ACCOUNT>\",\n    \"parent.hostedzone.name\": \"mycompany.a2z.com\",\n    \"dev.subzone.name\": \"dev.mycompany.a2z.com\",\n  }\n}\n```\n\n\n## Deploying\n\nOnce all pre-requisites are set you should be able to get a working cluster with all the objectives met, including workloads with an example of team-specific ingress objects. \n\n"
  },
  {
    "path": "docs/patterns/observability/existing-eks-apiserver-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/existing-eks-observability-accelerators/existing-eks-apiserver-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/existing-eks-awsnative-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/existing-eks-observability-accelerators/existing-eks-awsnative-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/existing-eks-mixed-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/existing-eks-observability-accelerators/existing-eks-mixed-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/existing-eks-nginx-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/existing-eks-observability-accelerators/existing-eks-nginx-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/existing-eks-opensource-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/existing-eks-observability-accelerators/existing-eks-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/multi-acc-new-eks-mixed-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/multi-new-eks-observability-accelerators/multi-acc-new-eks-mixed-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/multi-account-monitoring.md",
    "content": "# Multi Account Open Source Observability Pattern.\n\n## Architecture\n\nThe following figure illustrates the architecture of the pattern we will be deploying for Multi Account Observability pattern using open source tooling such as AWS Distro for Open Telemetry (ADOT), Amazon Managed Service for Prometheus (AMP), Amazon Managed Grafana :\n\n![Architecture](../images/setup_amg-cross-account.png)\n\n## Objective\n\n1. Deploying two production grade Amazon EKS cluster across 2 AWS Accounts ( Prod1, Prod2 account ) through a Continuous Deployment infrastructure pipeline triggered upon a commit to the repository that holds the pipeline configuration in an another AWS account (pipeline account).\n\n1. Deploying ADOT add-on, AMP add-on to Prod 1 Amazon EKS Cluster to remote write metrics to AMP workspace in Prod 1 AWS Account. Deploying ADOT add-on, CloudWatch add-on to Prod 1 Amazon EKS Cluster to write metrics to CloudWatch in Prod 2 AWS Account.\n\n1. Configuring GitOps tooling (ArgoCD addon) to support deployment of [ho11y](https://github.com/aws-observability/aws-o11y-recipes/tree/main/sandbox/ho11y) and [yelb](https://github.com/mreferre/yelb) sample applications, in a way that restricts each application to be deployed only into the team namespace, by using ArgoCD projects.\n\n1. Setting up IAM roles in Prod 1 and Prod 2 Accounts to allow an AMG service role in the Monitoring account (4th AWS account) to access metrics from AMP workspace in Prod 1 account and CloudWatch namespace in Prod 2 account.\n\n1. Setting Amazon Managed Grafana to visualize AMP metrics from Amazon EKS cluster in Prod account 1 and CloudWatch metrics on workloads in Amazon EKS cluster in Prod account 2.\n\n### GitOps confguration\n\nFor GitOps, the blueprint bootstrap the ArgoCD addon and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\n\nYou can find the team-geordie configuration for this pattern in the workload repository under the folder [`team-geordie`](https://github.com/aws-samples/eks-blueprints-workloads/tree/main/teams/team-geordie).\n\n## Prerequisites\n\n1. AWS Control Tower deployed in your AWS environment in the management account. If you have not already installed AWS Control Tower, follow the [Getting Started with AWS Control Tower documentation](https://docs.aws.amazon.com/controltower/latest/userguide/getting-started-with-control-tower.html), or you can enable AWS Organizations in the AWS Management Console account and enable AWS SSO.\n\n1. An AWS account under AWS Control Tower called Prod 1 Account(Workloads Account A aka prodEnv1) provisioned using the AWS Service Catalog Account Factory product AWS Control Tower Account vending process or AWS Organization.\n\n1. An AWS account under AWS Control Tower called Prod 2 Account(Workloads Account B aka prodEnv2) provisioned using the AWS Service Catalog Account Factory product AWS Control Tower Account vending process or AWS Organization.\n\n1. An AWS account under AWS Control Tower called Pipeline Account (aka pipelineEnv) provisioned using the AWS Service Catalog Account Factory product AWS Control Tower Account vending process or AWS Organization.\n\n1. An AWS account under AWS Control Tower called Monitoring Account (Grafana Account aka monitoringEnv) provisioned using the AWS Service Catalog Account Factory product AWS Control Tower Account vending process or AWS Organization.\n\n## Deploying\n\n1. Fork this repository to your GitHub organisation/user.\n\n1. Clone your forked repository.\n\n1. Set environment variable `AWS_REGION` with region from where `pipelineEnv` account will be bootstrapped.\n\n    ```bash\n    export AWS_REGION=<YOUR AWS REGION>\n    ```\n\n1. Install the AWS CDK Toolkit globally on your machine using\n\n    ```bash\n    npm install -g aws-cdk\n    ```\n\n1. Create secret `github-ssh-key` in `AWS_REGION` of `pipelineEnv` account. This secret must contain GitHub SSH private key as a JSON structure containing fields `sshPrivateKey` and `url` in `pipelineEnv` account. This will be used by ArgoCD addon to authenticate against any GitHub repository (private or public). The secret is expected to be defined in the region where the pipeline will be deployed to. For more information on SSH credentials setup see [ArgoCD Secrets Support](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/#secrets-support).\n\n    ```bash\n    aws secretsmanager create-secret --region $AWS_REGION \\\n    --name github-ssh-key \\\n    --description \"SSH private key for ArgoCD authentication to GitHub repository\" \\\n    --secret-string '{\n        \"sshPrivateKey\":\"<SSH private key>\",\n        \"url\":\"git@github\"\n    }'\n    ```\n\n1. Create `github-token` secret in `AWS_REGION` of `pipelineEnv` account. This secret must be stored as a plain text in AWS Secrets Manager for the GitHub pipeline in `pipelineEnv` account. For more information on how to set it up, please refer to the [docs](https://docs.aws.amazon.com/codepipeline/latest/userguide/GitHub-create-personal-token-CLI.html). The GitHub Personal Access Token should have these scopes:\n    \n    1. *repo* - to read the repository\n\n    2. *admin:repo_hook* - if you plan to use webhooks (enabled by default)\n\n    ```bash\n    aws secretsmanager create-secret --region $AWS_REGION \\\n    --name github-token \\\n    --description \"GitHub Personal Access Token for CodePipeline to access GitHub account\" \\\n    --secret-string \"<GitHub Personal Access Token>\"\n    ```    \n\n1. Create secret `cdk-context` in `us-east-1` region as a plain text in AWS Secrets Manager for the GitHub pipeline in `pipelineEnv` account. `cdk-context` secret must be stored as a plain text in the following format in AWS Secrets Manager for cdk context for all the 4 AWS accounts used by the solution in `pipelineEnv` account. This secret must be created in `us-east-1` region.\n\n    ```bash\n    aws secretsmanager create-secret --region us-east-1 \\\n    --name cdk-context \\\n    --description \"AWS account details of different environments used by Multi account open source Observability pattern\" \\\n    --secret-string '{\n    \"context\": {\n        \"prodEnv1\": {\n            \"account\": \"<prodEnv1 account number>\",\n            \"region\": \"<AWS REGION>\"\n        },\n        \"prodEnv2\": {\n            \"account\": \"<prodEnv2 account number>\",\n            \"region\": \"<AWS REGION>\"\n        },\n        \"pipelineEnv\": {\n            \"account\": \"<pipelineEnv account number>\",\n            \"region\": \"<AWS REGION>\"\n        },\n        \"monitoringEnv\": {\n            \"account\": \"<prodmonitoringEnvEnv1 account number>\",\n            \"region\": \"<AWS REGION>\"\n        }\n    }\n    }'\n    ```\n\n1. Create the following IAM users and attach `administrator` policy to required accounts.\n    \n    1. IAM user `pipeline-admin` with `administrator` policy in Pipeline AWS Account\n\n        ```bash\n        aws iam create-user \\\n        [--profile pipelineEnv-admin-profile] \\\n        --user-name pipeline-admin\n\n        aws iam attach-user-policy \\\n        [--profile pipelineEnv-admin-profile] \\\n        --user-name pipeline-admin \\\n        --policy-arn arn:aws:iam::aws:policy/AdministratorAccess\n        ```\n\n    1. IAM user `prod1-admin` with `administrator` policy in Prod 1 AWS Account\n\n        ```bash\n        aws iam create-user \\\n        [--profile prodEnv1-admin-profile] \\\n        --user-name prod1-admin\n\n        aws iam attach-user-policy \\\n        [--profile prodEnv1-admin-profile] \\\n        --user-name prod1-admin \\\n        --policy-arn arn:aws:iam::aws:policy/AdministratorAccess\n        ```\n    \n    1. IAM user `prod2-admin` with `administrator` policy in Prod 2 AWS Account\n\n        ```bash\n        aws iam create-user \\\n        [--profile prodEnv2-admin-profile] \\\n        --user-name prod2-admin\n\n        aws iam attach-user-policy \\\n        [--profile prodEnv2-admin-profile] \\\n        --user-name prod2-admin \\\n        --policy-arn arn:aws:iam::aws:policy/AdministratorAccess\n        ```    \n    \n    1. IAM user `mon-admin` with `administrator` policy in Monitoring AWS Account\n\n        ```bash\n        aws iam create-user \\\n        [--profile monitoringEnv-admin-profile] \\\n        --user-name mon-admin\n\n        aws iam attach-user-policy \\\n        [--profile monitoringEnv-admin-profile] \\\n        --user-name mon-admin \\\n        --policy-arn arn:aws:iam::aws:policy/AdministratorAccess\n        ```\n    \n    1. IAM user `team-geordi` in Prod 1 and Prod 2 AWS Account\n\n        ```bash\n        aws iam create-user \\\n        [--profile prodEnv1-admin-profile] \\\n        --user-name team-geordi\n\n        aws iam create-user \\\n        [--profile prodEnv2-admin-profile] \\\n        --user-name team-geordi        \n        ```    \n    \n    1. IAM user `team-platform` in Prod 1 and Prod 2 AWS Account\n\n        ```bash\n        aws iam create-user \\\n        [--profile prodEnv1-admin-profile] \\\n        --user-name team-platform\n\n        aws iam create-user \\\n        [--profile prodEnv2-admin-profile] \\\n        --user-name team-platform     \n        ```\n\n1. Install project dependencies by running `npm install` in the main folder of this cloned repository\n\n1. Bootstrap all 4 AWS accounts using step mentioned for **different environment for deploying CDK applications** in [Deploying Pipelines](https://aws-quickstart.github.io/cdk-eks-blueprints/pipelines/#deploying-pipelines). If you have bootstrapped earlier, please remove them before proceeding with this step. Remember to set `pipelineEnv` account number in `--trust` flag. You can also refer to commands mentioned below:\n\n    ```bash\n    # bootstrap prodEnv1 account with trust access from pipelineEnv account\n    env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \\\n        [--profile prodEnv1-admin-profile] \\\n        --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \\\n        --trust <pipelineEnv account number> \\\n        aws://<prodEnv1 account number>/$AWS_REGION\n\n    # bootstrap prodEnv2 account with trust access from pipelineEnv account\n    env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \\\n        [--profile prodEnv2-admin-profile] \\\n        --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \\\n        --trust <pipelineEnv account number> \\\n        aws://<prodEnv2 account number>/$AWS_REGION\n\n    # bootstrap pipelineEnv account WITHOUT explicit trust \n    env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \\\n        [--profile pipelineEnv-admin-profile] \\\n        --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \\\n        aws://<pipelineEnv account number>/$AWS_REGION\n\n    # bootstrap monitoringEnv account with trust access from pipelineEnv account\n    env CDK_NEW_BOOTSTRAP=1 npx cdk bootstrap \\\n        [--profile monitoringEnv-admin-profile] \\\n        --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess \\\n        --trust <pipelineEnv account number> \\\n        aws://<monitoringEnv account number>/$AWS_REGION\n    ```\n\n1. Modify the code of `lib/pipeline-multi-env-gitops/index.ts` and `lib/multi-account-monitoring/pipeline.ts` in your forked repo to point to your GitHub username/organisation. Look for the declared const of `gitOwner` and change it to your GitHub username and commit changes to your forked repo. This is needed because the AWS CodePipeline that will be automatically created will be triggered upon commits that are made in your forked repo.\n\n1. Once all pre-requisites are set you are ready to deploy the pipeline. Run the following command from the root of this repository to deploy the pipeline stack in `pipelineEnv` account:\n\n    ```bash\n    make build\n    make pattern pipeline-multienv-monitoring deploy multi-account-central-pipeline\n    ```\n\n1. Now you can go to [AWS CodePipeline console](https://eu-west-1.console.aws.amazon.com/codesuite/codepipeline/pipelines), and see how it was automatically created to deploy multiple Amazon EKS clusters to different environments. \n\n1. The deployment automation will create `ampPrometheusDataSourceRole` with permissions to retrieve metrics from AMP in Prod 1 Account, `cloudwatchDataSourceRole` with permissions to retrieve metrics from CloudWatch in Prod 2 Account and `amgWorkspaceIamRole` in monitoring account to assume roles in Prod 1 and Prod 2 account for retrieving and visualizing metrics in Grafana.\n\n1. Next, manually follow the following steps from [AWS Open Source blog](https://aws.amazon.com/blogs/opensource/setting-up-amazon-managed-grafana-cross-account-data-source-using-customer-managed-iam-roles/#:~:text=AWS%20SSO%20in%20the%20management%20account) :\n    1. AWS SSO in the management account\n    2. Query metrics in Monitoring account from Amazon Managed Prometheus workspace in Prod 1 Account\n    3. Query metrics in the Monitoring account from Amazon CloudWatch in Prod 1 Account\n\n![Metrics from AMP](../images/AMG%20-%20Metrics%20from%20AMP.png)\n\n![Metrics from CloudWatch](../images/AMG%20-%20Metrics%20from%20CloudWatch.png)\n\n### Validating Custom Metrics and Traces from ho11y App\n\n1. Run the below command in both clusters to generate traces to X-Ray and Amazon Managed Grafana Console out the sample `ho11y` app :\n\n    ```\n    frontend_pod=`kubectl get pod -n geordie --no-headers -l app=frontend -o jsonpath='{.items[*].metadata.name}'`\n    loop_counter=0\n    while [ $loop_counter -le 5000 ] ;\n    do\n            kubectl exec -n geordie -it $frontend_pod -- curl downstream0.geordie.svc.cluster.local;\n            echo ;\n            loop_counter=$[$loop_counter+1];\n    done\n    ```\n### Traces and Service Map screenshots from X-Ray Console\n\n![Traces of ho11y App on X-Ray Console](../images/XRAY%20-%20Traces.png)\n\n![Service Map of ho11y App on X-Ray Console](../images/XRAY%20-%20Service%20Map.png)\n\n### Custom Metrics from ho11y App on Amazon Managed Grafana Console using AMP as data source\n\n![Exploring Metrics from ho11y with AMP as Data source in AMG Console](../images/Explore%20AMG.png)\n\n### Custom Metrics from ho11y App on Amazon Managed Grafana Console using CloudWatch as data source\n\n![Exploring Metrics from ho11y with CloudWatch as Data source in AMG Console](../images/Explore%20AMG.png)\n\n### Notes\n\nThis pattern consumes multiple Elastic IP addresses, because 3 VPCs with 3 subnets are created by this pattern in Prod 1 and Prod 2 AWS Accounts. Make sure your account limits for EIP are increased to support additional 9 EIPs (1 per Subnets).\n\n"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-apiserver-opensource-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-apiserver-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-awsnative-fargate-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-awsnative-fargate-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-gpu-opensource-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-gpu-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-graviton-opensource-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-graviton-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-java-opensource-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-java-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-mixed-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-mixed-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-native.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-awsnative-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-nginx-opensource-observability.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-nginx-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/observability/single-new-eks-opensource.md",
    "content": "{{ external_markdown('https://raw.githubusercontent.com/aws-observability/cdk-aws-observability-accelerator/main/docs/patterns/single-new-eks-observability-accelerators/single-new-eks-opensource-observability.md', '') }}"
  },
  {
    "path": "docs/patterns/paralus.md",
    "content": "# Paralus on EKS\n\nThe Paralus project is a free open-source tool that enables controlled audited access to Kubernetes infrastructure. It comes with just-in-time service account creation and user-level credential management that integrates with your existing RBAC and SSO providers of choice. Learn more by visiting the offical documentation page: <https://www.paralus.io/>\n\nThis pattern deploys the following resources:\n\n- Creates a single EKS cluster with a public endpoint (for demo purpose only) that includes a managed node group\n- Deploys supporting AddOn:  AwsLoadBalancerController, VpcCni, KubeProxy, EbsCsiDriverAddOn\n- Deploy Paralus on the EKS cluster\n\n**NOTE: Paralus installs a few dependent modules such as Postgres, Kratos, and also comes with a built-in dashboard. At it's core, Paralus works atop domain-based routing, inter-service communication, and supports the AddOns mentioned above.**\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n\n## Deploy an EKS Cluster using Amazon EKS Blueprints for CDK\n\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\n```\n\nUpdate FQDN information for your installation following the example below:\n\n```json\n    fqdn: {\n        \"domain\": \"yourdomain.com\",\n        \"hostname\": \"console-eks\",\n        \"coreConnectorSubdomain\": \"*.core-connector.eks\",\n        \"userSubdomain\": \"*.user.eks\"\n    }\n```\n\nUpdating npm\n\n```sh\nnpm install -g npm@latest\n```\n\nTo view patterns and deploy the Paralus pattern, run the commands below:\n\n```sh\ncdk list\ncdk bootstrap\nmake pattern paralus deploy\n```\n\n## Verify the resources\n\nRun the update-kubeconfig command below. You should be able to get the command from the CDK output message once your cluster has been finished deploying. More information can be found at <https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access>\n\n```sh\naws eks update-kubeconfig --name <your cluster name> --region <your region> --role-arn arn:aws:iam::1234567890121:role/paralus-blueprint-paralusblueprintMastersRoleF3287-EI3XEBO1107B\n```\n\nLet’s verify the resources created by steps above.\n\n```sh\nkubectl get nodes # Output will provide list of running nodes in your cluster\n\nkubectl get ns | grep paralus # Output shows Paralus namespace\n\nkubectl get pods --namespace=paralus-system  # Output shows Paralus pods\n\nblueprints-addon-paralus-contour-contour-7857f4cd9-kqhgp   1/1     Running                 \nblueprints-addon-paralus-contour-envoy-mx8z7               2/2     Running                 \nblueprints-addon-paralus-fluent-bit-525tt                  1/1     Running                 \nblueprints-addon-paralus-kratos-588775bc47-wf5gf           2/2     Running                 \nblueprints-addon-paralus-kratos-courier-0                  2/2     Running                 \nblueprints-addon-paralus-postgresql-0                      1/1     Running                 \ndashboard-6d8b54d78b-d8cks                                 1/1     Running                 \nparalus-66d9bbf698-qznzl                                   2/2     Running                 \nprompt-54d45cff79-h9x95                                    2/2     Running   \nrelay-server-79448564cb-nf5tj                              2/2     Running              \n```\n\n[Learn more](https://www.paralus.io/docs/architecture/core-components) about the various components that are deployed as part of Paralus.\n\n## Configure DNS Settings\n\nOnce Paralus is installed, continue with following steps to configure DNS settings, reset default password and start using Paralus.\n\nObtain the external ip address by executing below command against the installation\n`kubectl get svc blueprints-addon-paralus-contour-envoy -n paralus-system`\n\n```sh\nNAME                            TYPE           CLUSTER-IP       EXTERNAL-IP                                                                     PORT(S)                         AGE\nblueprints-addon-paralus-contour-envoy         LoadBalancer   10.100.101.216   a814da526d40d4661bf9f04d66ca53b5-65bfb655b5662d24.elb.us-west-2.amazonaws.com   80:31810/TCP,443:30292/TCP      10m\n```\n\nUpdate the DNS settings to add CNAME records:\n\n```sh\n    name: console-eks \n    value: a814da526d40d4661bf9f04d66ca53b5-65bfb655b5662d24.elb.us-west-2.amazonaws.com\n    \n    name: *.core-connector.eks  \n    value: a814da526d40d4661bf9f04d66ca53b5-65bfb655b5662d24.elb.us-west-2.amazonaws.com\n    \n    name: *.user.eks \n    value: a814da526d40d4661bf9f04d66ca53b5-65bfb655b5662d24.elb.us-west-2.amazonaws.com\n```\n\nObtain your default password and reset it upon first login\n\n`kubectl logs -f --namespace paralus-system $(kubectl get pods --namespace paralus-system -l app.kubernetes.io/name='paralus' -o jsonpath='{ .items[0].metadata.name }') initialize | grep 'Org Admin default password:'`\n\nYou can now access dashboard with <http://console-eks.yourdomain.com> ( refers to the hostname.domain specified during installation ), start importing clusters and using paralus.\n\nNote: you can also refer to this [paralus eks blogpost](https://www.paralus.io/blog/eks-quickstart#configuring-dns-settings)\n\n## Paralus Features & Usage\n\n<https://www.paralus.io/docs/usage/>\n\n## Configuring centralized kubectl access to clusters\n\nKubectl is one of the most widely used tools to interact with Kubernetes. The command line tool allows you to deploy applications, inspect, and manage resources. It authenticates with the control plane for your cluster and makes API calls to the Kubernetes API. In short if you are working with Kubernetes - you will use kubectl the most.\n\nIn most modern day scenarios, there are multiple users who are accessing various clusters. This makes it all more important to ensure that every user or group has access to only those resources that they are allowed to. A couple different approaches to achieve this include using namespaces and role based access control. While these are good, most enterprise grade application deployments require something more robust.\n\nThat’s where Paralus comes in. It allows you to configure centralized kubectl access to multiple clusters all from a single dashboard. It allows you to create groups, assign projects and users, and provide access. Check out this blog post for a deep dive into how you can use Paralus to import different clusters to Paralus and configure access to them using zero trust principles built in. [Read More](https://www.paralus.io/blog/centralized-kubectl-access#the-use-case)\n\n## Cleanup\n\nTo clean up your EKS Blueprints, run the following commands:\n\n```sh\ncdk destroy paralus-blueprint \n```\n\n## Troubleshooting\n\nIf postgres pvc is not getting a volume allocated, it probably is due to the iam permissions. Please refer this <https://docs.aws.amazon.com/eks/latest/userguide/csi-iam-role.html> to assign approriate policies to kubernetes sa\n\n## Disclaimer\n\nThis pattern relies on an open-source NPM package paralus-eks-blueprints-addon. Please refer to the package npm site for more information.\n<https://www.npmjs.com/package/@paralus/paralus-eks-blueprints-addon>\n\nIf you have any questions about the npm package or find any defect, please post in the source repo at \n<https://github.com/paralus/eks-blueprints-addon>\n"
  },
  {
    "path": "docs/patterns/pipeline-multi-env-gitops.md",
    "content": "# Pipeline Multi Environment Pattern\n\n## Objective\n\n1. Deploying an EKS cluster across 3 environments( dev, test, and prod ), with a Continuous Deployment pipeline triggered upon a commit to the repository that holds the pipeline configuration.\n2. Configuring GitOps tooling (ArgoCD addon) to support multi-team and multi-repositories configuration, in a way that restricts each application to be deployed only into the team namespace, by using ArgoCD projects\n\n### GitOps confguration\n\nFor GitOps, the blueprint bootstrap the ArgoCD addon and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\nThe pattern uses the ECSDEMO applications as sample applications to demonstrate how to setup a GitOps configuration with multiple teams and multiple applications. The pattern include the following configurations in terms io:\n\n1. Application team - it defines 3 application teams that corresponds with the 3 sample applications used\n2. ArgoCD bootstrap - the pattern configure the ArgoCD addon to point to the [workload repository](https://github.com/aws-samples/eks-blueprints-workloads) of the EKS Blueprints samples\n3. ArgoCD projects - as part of the ArgoCD addon bootstrap, the pattern generate an ArgoCD project for each application team. The ArgoCD are used in order to restrict the deployment of an application to a specific target namespace\n\nYou can find the App of Apps configuration for this pattern in the workload repository under the folder [`multi-repo`](https://github.com/aws-samples/eks-blueprints-workloads/tree/main/multi-repo).\n\n## Prerequisites\n\n1. Fork this repository to your GitHub organisation/user\n2. Clone your forked repository\n3. Install the AWS CDK Toolkit globally on your machine using\n\n    ```bash\n    npm install -g aws-cdk\n    ```\n\n4. `github-ssh-key` - must contain GitHub SSH private key as a JSON structure containing fields `sshPrivateKey` and `url`. This will be used by ArgoCD addon to authenticate against ay GitHub repository (private or public). The secret is expected to be defined in the region where the pipeline will be deployed to. For more information on SSH credentials setup see [ArgoCD Secrets Support](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/#secrets-support).\n\n5. `github-token` secret must be stored in AWS Secrets Manager for the GitHub pipeline. For more information on how to set it up, please refer to the [docs](https://docs.aws.amazon.com/codepipeline/latest/userguide/GitHub-create-personal-token-CLI.html). The GitHub Personal Access Token should have these scopes:\n   1. *repo* - to read the repository\n   2. *admin:repo_hook* - if you plan to use webhooks (enabled by default)\n\n6. Create the relevant users that will be used by the different teams\n\n    ```bash\n    aws iam create-user --user-name frontend-user\n    aws iam create-user --user-name nodejs-user\n    aws iam create-user --user-name crystal-user\n    aws iam create-user --user-name platform-user\n    ```\n\n7. Install project dependencies by running `npm install` in the main folder of this cloned repository\n\n8. In case you haven't done this before, bootstrap your AWS Account for AWS CDK use using:\n\n    ```bash\n    cdk bootstrap\n    ```\n\n9. Modify the code in your forked repo to point to your GitHub username/organisation. This is needed because the AWS CodePipeline that will be automatically created will be triggered upon commits that are made in your forked repo. Open the [pattenrn file source code](../../lib/pipeline-multi-env-gitops/index.ts) and look for the declared const of `gitOwner`. Change it to your GitHub username.\n\n10. *OPTIONAL* - As mentioned above, this pattern uses another repository for GitOps. This is the ArgoCD App of Apps configuration that resides in the [aws-samples](https://github.com/aws-samples/eks-blueprints-workloads/tree/main/multi-repo) organisation. If you would like to modify the App of Apps configuration and customise it to your needs, then use the following instructions:\n\n    1. Fork the [App of Apps](https://github.com/aws-samples/eks-blueprints-workloads/tree/main/multi-repo) workloads repo to your GitHub username\n\n    2. Modify the [pattern code](../../lib/pipeline-multi-env-gitops/index.ts) with the following changes:\n\n       1. Change the consts of `devArgoAddonConfig`, `testArgoAddonConfig`, and `prodArgoAddonConfig` to point to your GitHub username\n\n       2. In the `createArgoAddonConfig` function, look for the `git@github.com:aws-samples/eks-blueprints-workloads.git` code under the `sourceRepos` configurations, and add another reference to your forked workload repository\n\n## Deploying\n\nOnce all pre-requisites are set you are ready to deploy the pipeline. Run the following command from the root of this repository to deploy the pipeline stack:\n\n```bash\nmake pattern pipeline-multienv-gitops deploy eks-blueprint-pipeline-stack\n```\n\nNow you can go to [AWS CodePipeline console](https://eu-west-1.console.aws.amazon.com/codesuite/codepipeline/pipelines), and see how it was automatically created to deploy multiple Amazon EKS clusters to different environments.\n\n### Notes\n\n1. In case your pipeline fails on the first run, it's because that the AWS CodeBuild step needs elevated permissions at build time. This is described in the official [docs](https://aws-quickstart.github.io/cdk-eks-blueprints/pipelines/#troubleshooting). To resolve this, locate `AccessDeniedException` in the CodeBuild build logs, and attach the following inline policy to it:\n\n    ```json\n    {\n        \"Version\": \"2012-10-17\",\n        \"Statement\": [\n            {\n                \"Sid\": \"VisualEditor0\",\n                \"Effect\": \"Allow\",\n                \"Action\": [\n                    \"sts:AssumeRole\",\n                    \"secretsmanager:GetSecretValue\",\n                    \"secretsmanager:DescribeSecret\",\n                    \"cloudformation:*\"\n                ],\n                \"Resource\": \"*\"\n            }\n        ]\n    }\n    ```\n\nThe above inconvenience has been fixed in the Blueprints framework as well as in the pattern, so please report such cases if you encounter them. This item is left here for reference in case customers modify the pattern to require additional permissions at build time. \n\n2. This pattern consumes multiple Elastic IP addresses, because 3 VPCs with 3 subnets are created by this pattern. Make sure your account limit for EIP are increased to support additional 9 EIPs (1 per Subnets)\n"
  },
  {
    "path": "docs/patterns/secureingresscognito.md",
    "content": "# Secure Ingress using Cognito Pattern\n\n## Objective\n\nThe objective of this pattern is to provide a secure authentication mechanism for customer applications using Amazon Cognito, ALB, and Route53, ensuring that only authorized users can access the application. The Kubecost tool is used as a reference or sample implementation to demonstrate the pattern's capabilities.\n\nTo achieve this objective, the pattern utilizes Amazon Cognito to provide user authentication for the application's ingress, with ALB's built-in support for user authentication handling routine tasks such as user sign-up, sign-in, and sign-out. In addition to Amazon Cognito, ALB integrates with any OpenID Connect compliant identity provider (IdP) for a single sign-on experience across applications. ACM and Route53 provide SSL/TLS certificates to secure connections to ALB and authenticate users, preventing sensitive information from being intercepted or tampered with during transmission.\n\nThe pattern also leverages Kubecost to provide real-time cost visibility and analysis for Kubernetes clusters, enabling customers to make informed decisions about resource allocation and utilization. This pattern can be easily adapted and extended to secure ingress for any application, providing a unified and secure solution for user authentication while optimizing costs. By implementing this solution, Amazon EKS customers can have a reliable, scalable, and secure authentication mechanism for their applications, with a cost optimization tool to manage and reduce the costs associated with their Kubernetes clusters.\n\n\n## Architecture\n\n![Kubecost Architecture](./images/secure-ingress-kubecost-new.png)\n\n\n## Approach\n\nThis blueprint will include the following:\n\n* A new Well-Architected VPC with both Public and Private subnets.\n* A new Well-Architected EKS cluster in the region and account you specify.\n* [EBS CSI Driver Amazon EKS Add-on](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/ebs-csi-driver/) allows Amazon Elastic Kubernetes Service (Amazon EKS) clusters to manage the lifecycle of Amazon EBS volumes for persistent volumes.\n* AWS and Kubernetes resources needed to support [AWS Load Balancer Controller](https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html).\n* [Amazon VPC CNI add-on (VpcCni)](https://docs.aws.amazon.com/eks/latest/userguide/managing-vpc-cni.html) into your cluster to support native VPC networking for Amazon EKS.\n* [External-DNS](https://github.com/kubernetes-sigs/external-dns) allows integration of exposed Kubernetes services and Ingresses with DNS providers\n* [Kubecost](https://kubecost.com/) provides real-time cost visibility and insights by uncovering patterns that create overspending on infrastructure to help teams prioritize where to focus optimization efforts\n* [Argo CD](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/) is a declarative, GitOps continuous delivery tool for Kubernetes. The Argo CD add-on provisions Argo CD into an EKS cluster, and bootstraping your workloads from public and private Git repositories.\n* Create the necessary Cognito resources like user pool, user pool client, domain, [Pre sign-up Lambda trigger and Pre authentication Lambda triggers](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html)  etc.., and passed to the Argo CD app of apps pattern from which ingress resources can reference.\n\n## GitOps confguration\n\nFor GitOps, the blueprint bootstrap the ArgoCD addon and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\n\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n\n## Deploy\n\n1. Let’s start by setting a few environment variables. Change the Region as needed.\n\n```\nACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text)\nAWS_REGION=us-west-2\n```\n\n2. Clone the repository and install dependency packages. This repository contains CDK v2 code written in TypeScript.\n\n```\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\nnpm i\n```\n\n3. argo-admin-password secret must be defined as plain text (not key/value) in `us-west-2`  region.\n\n```\naws secretsmanager create-secret --name argo-admin-secret \\\n    --description \"Admin Password for ArgoCD\" \\\n    --secret-string \"password123$\" \\\n    --region \"us-west-2\"\n```\n4. The CDK code expects the allowed domain and subdomain names in the CDK context file (cdk.json).\n\nCreate two environment variables. The PARENT_HOSTED_ZONE variable contains your company’s domain name. The DEV_SUBZONE_NAME will be the address for your Kubecost dashboard. \n\nGenerate the cdk.json file:\n\n```\nPARENT_HOSTED_ZONE=mycompany.a2z.com\nDEV_SUBZONE_NAME=dev.mycompany.a2z.com\ncat << EOF > cdk.json\n{\n    \"app\": \"npx ts-node dist/lib/common/default-main.js\",\n    \"context\": {\n        \"parent.hostedzone.name\": \"${PARENT_HOSTED_ZONE}\",\n        \"dev.subzone.name\": \"${DEV_SUBZONE_NAME}\"\n      }\n}\nEOF\n```\n\n\n5. In this solution, we’ll allow access to the Kubecost dashboard based on user email addresses. You can control access to the dashboard by allow-listing an entire domain or individual email addresses. \n\nUsers are required to sign-up before they can access the Kubecost dashboard. The pre sign-up Lambda trigger only allows sign-ups when user’s email domain matches allow-listed domains. When users sign-up, Cognito sends a verification code to their email address. Users have to verify access (using the one time valid code) to their email before they get access to the dashboard. \n\nIf you’d like to limit access to the dashboard by email addresses, you can also create a parameter to store allowed email addresses and add a logic to the pre authentication Lambda trigger.\n\nCreate below parameters with allowed email addresses and domains in the AWS Systems Manager Parameter Store:\n\n```\nexport SSM_PARAMETER_KEY=\"/secure-ingress-auth-cognito/ALLOWED_DOMAINS\"\nexport SSM_PARAMETER_VALUE=\"emaildomain1.com,emaildomain2.com\"\n\naws ssm put-parameter \\\n  --name \"$SSM_PARAMETER_KEY\" \\\n  --value \"$SSM_PARAMETER_VALUE\" \\\n  --type \"String\" \\\n  --region $AWS_REGION\n```\n\n\n6. Execute the commands below to bootstrap the AWS environment in `us-west-2`\n\n```\ncdk bootstrap aws://$ACCOUNT_ID/$AWS_REGION\n```\n\n7. Run the following command from the root of this repository to deploy the pipeline stack:\n\n```\nmake build\nmake pattern secure-ingress-cognito deploy secure-ingress-blueprint\n```\n\n## Cluster Access\n\nOnce the deploy completes, you will see output in your terminal window similar to the following:\n\n```\nOutputs:\nsecure-ingress-blueprint.secureingressblueprintClusterNameD6A1BE5C = secure-ingress-blueprint\nsecure-ingress-blueprint.secureingressblueprintConfigCommandD0275968 =  aws eks update-kubeconfig —name secure-ingress-blueprint —region us-west-2 —role-arn arn:aws:iam::<ACCOUNT ID>:role/secure-ingress-blueprint-secureingressblueprintMas-7JD5S67SG7M0\nsecure-ingress-blueprint.secureingressblueprintGetTokenCommand21BE2184 =  aws eks get-token —cluster-name secure-ingress-blueprint —region us-west-2 —role-arn arn:aws:iam::<ACCOUNT ID>:role/secure-ingress-blueprint-secureingressblueprintMas-7JD5S67SG7M0\n```\n```\nStack ARN:\narn:aws:cloudformation:us-west-2:<ACCOUNT ID>:stack/secure-ingress-blueprint/64017120-91ce-11ed-93b2-0a67951f5d5d\n```\n\n\nTo update your Kubernetes config for your new cluster, copy and run the secure-ingress-blueprint.secureingressblueprintConfigCommandD0275968 command (the second command) in your terminal.\n\n```\naws eks update-kubeconfig —name secure-ingress-blueprint —region us-west-2 —role-arn arn:aws:iam::<ACCOUNT ID>:role/secure-ingress-blueprint-secureingressblueprintMas-7JD5S67SG7M0\n```\n\nValidate that you now have kubectl access to your cluster via the following:\n\n```\nkubectl get all -n kubecost\n```\n\nYou should see output that lists all namespaces in your cluster.\n\n\n## Test authentication\n\nPoint your browser to the URL of the Kubecost app in your cluster. You can get the URL from the cdk.json file using the below command.\n\n```\nawk -F':' '/dev.subzone.name/ {print $2}' cdk.json | tr -d '\",' | xargs echo\n```\n\nYour browser will be redirected to a sign-in page. This page is provided by Amazon Cognito hosted UI.\n\nSince this is your first time accessing the application, sign up as a new user. The data you input here will be saved in the Amazon Cognito user pool you created earlier in the post. \n\n![Cognito Signup Process](./images/Cognito-Signup-1.png)\n\nSelect “Sign up” and use your email address and create a password\n\n![Cognito Signup Process](./images/Cognito-Signup-2.png)\n\n![Cognito Signup Process](./images/Cognito-Signup-3.png)\n\nUse the verification code received in your email and confirm the account. Once you sign in, ALB will send you to the Kubecost app’s UI:\n\n![Kubecost](./images/Cognito-Kubecost-1.png)\n\nSelect the “AWS Cluster #1” to view the cost overview, savings and efficiency details.\n\n![Kubecost Dashboard](./images/Cognito-Kubecost-2.png)\n\n"
  },
  {
    "path": "docs/patterns/security/eks-config-rules.md",
    "content": "# Security Best Practices for Amazon EKS\n\n## Objective\n\nThe objective of this pattern is to demonstrate how to enable AWS Config configuration recorder and AWS Config managed rules for EKS security best practices in your AWS account `CDK_DEFAULT_ACCOUNT` and region `CDK_DEFAULT_REGION` and verify the status of the rules.\n\n## Prerequisites\n\n1. Follow the usage [instructions](https://github.com/aws-samples/cdk-eks-blueprints-patterns/blob/main/README.md#usage) to install the dependencies and perform the repository setup.\n2. `argo-admin-password` secret must be defined in Secrets Manager in the same region as the EKS cluster.\n\n## Deploy\n\nTo bootstrap the CDK toolkit and list all stacks in the app, run the following commands:\n\n```bash\ncdk bootstrap\nmake list\n```\n\n### Deploy AWS Config Configuration Recorder\n\nUse the AWS Config setup blueprints pattern to enable AWS Config in your account and region by running the following command:\n\n```bash\nmake pattern eks-config-rules deploy eks-config-setup\n```\n\n### Deploy Config Rules for EKS Security Best Practices\n\nNow enable the AWS Config managed rules for EKS security best practices by running the following command:\n\n```bash\nmake pattern eks-config-rules deploy eks-config-rules-setup\n```\n\n## Verify\n\n### Verify the status of the AWS Config managed rules for EKS security best practices\n\nUsing the following AWS CLI command, get a list of the AWS Config rules with their evaluation status.\n\n```bash\naws configservice describe-config-rule-evaluation-status\n```\n\nThe output will look something like the following.\n\n```json\n{\n    \"ConfigRulesEvaluationStatus\": [\n        ...\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksEndpointNoPublicAccess49-37QJEXYZALLB\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-luqz0p\",\n            \"ConfigRuleId\": \"config-rule-luqz0p\",\n            \"LastSuccessfulInvocationTime\": \"2023-05-30T00:33:26.878000+00:00\",\n            \"LastSuccessfulEvaluationTime\": \"2023-05-30T00:33:27.539000+00:00\",\n            \"FirstActivatedTime\": \"2023-05-27T00:32:41.020000+00:00\",\n            \"FirstEvaluationStarted\": true\n        },\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksOldestSupportedVersionAD-Z65N0TEQSF96\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-psbc54\",\n            \"ConfigRuleId\": \"config-rule-psbc54\",\n            \"LastSuccessfulInvocationTime\": \"2023-05-27T07:56:05.182000+00:00\",\n            \"LastSuccessfulEvaluationTime\": \"2023-05-27T07:56:07.542000+00:00\",\n            \"FirstActivatedTime\": \"2023-05-25T22:44:21.666000+00:00\",\n            \"FirstEvaluationStarted\": true\n        },\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksSecretsEncrypted7566BFCD-HUQX4WXUDEFA\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-kzohng\",\n            \"ConfigRuleId\": \"config-rule-kzohng\",\n            \"LastSuccessfulInvocationTime\": \"2023-05-30T00:33:26.902000+00:00\",\n            \"LastSuccessfulEvaluationTime\": \"2023-05-30T00:33:27.616000+00:00\",\n            \"FirstActivatedTime\": \"2023-05-27T00:32:41.006000+00:00\",\n            \"FirstEvaluationStarted\": true\n        },\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksSupportedVersionCDB3159A-1VNH10LGMMJX\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-oaio54\",\n            \"ConfigRuleId\": \"config-rule-oaio54\",\n            \"LastSuccessfulInvocationTime\": \"2023-05-27T07:56:05.223000+00:00\",\n            \"LastSuccessfulEvaluationTime\": \"2023-05-27T07:56:05.420000+00:00\",\n            \"FirstActivatedTime\": \"2023-05-25T22:51:26.563000+00:00\",\n            \"FirstEvaluationStarted\": true\n        }\n        ...\n    ]\n}\n```\n\nYou can search for the EKS specific rules. Make a note of the unique `ConfigRuleName` of each of the AWS Config rules for EKS security best practices.\n\nUsing the unique names of the EKS Config rules from **your account and region** shown after running the previous AWS CLI command, you can verify each EKS Config rule configuration and state using the following AWS CLI command (remember to replace the rule names below with your rule names).\n\n```bash\naws configservice describe-config-rules --config-rule-names \"eks-config-rules-setup-EksEndpointNoPublicAccess<your rule id>\" \"eks-config-rules-setup-EksOldestSupportedVersion<your rule id>\" \"eks-config-rules-setup-EksSecretsEncrypted<your rule id>\" \"eks-config-rules-set\nup-EksSupportedVersion<your rule id>\"\n```\n\n```json\n{\n    \"ConfigRules\": [\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksEndpointNoPublicAccess49-37QJEXYZALLB\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-luqz0p\",\n            \"ConfigRuleId\": \"config-rule-luqz0p\",\n            \"Source\": {\n                \"Owner\": \"AWS\",\n                \"SourceIdentifier\": \"EKS_ENDPOINT_NO_PUBLIC_ACCESS\"\n            },\n            \"ConfigRuleState\": \"ACTIVE\",\n            \"EvaluationModes\": [\n                {\n                    \"Mode\": \"DETECTIVE\"\n                }\n            ]\n        },\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksOldestSupportedVersionAD-Z65N0TEQSF96\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-psbc54\",\n            \"ConfigRuleId\": \"config-rule-psbc54\",\n            \"Source\": {\n                \"Owner\": \"AWS\",\n                \"SourceIdentifier\": \"EKS_CLUSTER_OLDEST_SUPPORTED_VERSION\"\n            },\n            \"InputParameters\": \"{\\\"oldestVersionSupported\\\":\\\"1.25\\\"}\",\n            \"ConfigRuleState\": \"ACTIVE\",\n            \"EvaluationModes\": [\n                {\n                    \"Mode\": \"DETECTIVE\"\n                }\n            ]\n        },\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksSecretsEncrypted7566BFCD-HUQX4WXUDEFA\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-kzohng\",\n            \"ConfigRuleId\": \"config-rule-kzohng\",\n            \"Source\": {\n                \"Owner\": \"AWS\",\n                \"SourceIdentifier\": \"EKS_SECRETS_ENCRYPTED\"\n            },\n            \"ConfigRuleState\": \"ACTIVE\",\n            \"EvaluationModes\": [\n                {\n                    \"Mode\": \"DETECTIVE\"\n                }\n            ]\n        },\n        {\n            \"ConfigRuleName\": \"eks-config-rules-setup-EksSupportedVersionCDB3159A-1VNH10LGMMJX\",\n            \"ConfigRuleArn\": \"arn:aws:config:us-east-1:XXXXXXXXXXX:config-rule/config-rule-oaio54\",\n            \"ConfigRuleId\": \"config-rule-oaio54\",\n            \"Source\": {\n                \"Owner\": \"AWS\",\n                \"SourceIdentifier\": \"EKS_CLUSTER_SUPPORTED_VERSION\"\n            },\n            \"InputParameters\": \"{\\\"oldestVersionSupported\\\":\\\"1.25\\\"}\",\n            \"ConfigRuleState\": \"ACTIVE\",\n            \"EvaluationModes\": [\n                {\n                    \"Mode\": \"DETECTIVE\"\n                }\n            ]\n        }\n    ]\n}\n```\n\nNote that you can see the parameter value of the rules with required `InputParameters` (`EKS_CLUSTER_OLDEST_SUPPORTED_VERSION` and `EKS_CLUSTER_OLDEST_SUPPORTED_VERSION`), and the `ConfigRuleState` for each of the rules which is `ACTIVE`.\n"
  },
  {
    "path": "docs/patterns/security/encryption-at-rest.md",
    "content": "# Data at Rest Encryption\n\n## Objective\n\nThe objective of this pattern is to demonstrate how to enable encryption at rest for EKS cluster using EBS/EFS storage.\n\nTo achieve this objective, the pattern utilizes [EBS CSI Driver Amazon EKS Add-on](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/ebs-csi-driver/) to enable encryption-at-rest for EBS volumes and [EFS CSI Driver Amazon EKS Add-on](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/efs-csi-driver/) to enable encryption-at-rest for EFS volumes.\n\nThe pattern also leverages [KMS resource provider](https://aws-quickstart.github.io/cdk-eks-blueprints/resource-providers/kms-key-providers/) to create KMS keys for EBS/EFS encryption-at-rest and [EFS File System resource provider](https://aws-quickstart.github.io/cdk-eks-blueprints/resource-providers/efs-providers/) to create an encrypted EFS file system.\n\n## GitOps confguration\n\nFor GitOps, the blueprint bootstraps the ArgoCD addon and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\n\nThe sample repository contains the following workloads:\n\n1. team-platform creates a storage class for EBS and EFS volumes.\n2. team-data creates a persistent volume claim for EBS and EFS volumes and a pod that mounts the volumes.\n\n## Prerequisites\n\n1. Follow the usage [instructions](https://github.com/aws-samples/cdk-eks-blueprints-patterns/blob/main/README.md#usage) to install the dependencies and perform the repository setup.\n2. `argo-admin-password` secret must be defined in Secrets Manager in the same region as the EKS cluster.\n\n## Deploy\n\nTo bootstrap the CDK toolkit and list all stacks in the app, run the following commands:\n\n```bash\ncdk bootstrap\nmake list\n```\n\nTo deploy the pattern, run the following command:\n\n```bash\nmake pattern data-at-rest-encryption deploy\n```\n\n## Verify\n\nNow you can verify that the EBS and EFS volumes are encrypted.\n\n### EBS\n\nTo list all the PersistentVolumeClaims (PVCs) that exist in the Kubernetes cluster's namespace named \"data\", run the following command:\n\n```bash\nkubectl get pvc -n data\n```\n\nThe output should look similar to the following:\n\n```bash\nNAME                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS    AGE\ngp2-encrypted-pvc   Bound    pvc-78bd070e-8eba-4b01-a378-462bb806beb3   10Gi       RWO            gp2-encrypted   14m\n```\n\nTo describe an Amazon Elastic Block Store (EBS) volume that is associated with a PersistentVolume (PV) in Kubernetes, run the following command (please replace the PVC-IDENTIFIER with the PVC name from the previous step):\n\n```bash\naws ec2 describe-volumes --region us-east-1 --filters \"Name=tag:kubernetes.io/created-for/pv/name,Values=<PVC-IDENTIFIER>\" --query 'Volumes[*].{VolumeId:VolumeId, Encrypted:Encrypted, KmsKeyId:KmsKeyId}'\n```\n\nThe output should look similar to the following:\n\n```bash\n[\n    {\n        \"VolumeId\": \"vol-09332f96a58e67385\",\n        \"Encrypted\": true,\n        \"KmsKeyId\": \"arn:aws:kms:us-east-1:111122223333:key/a8b9fa0b-955f-4f85-85c1-8f911003390e\"\n    }\n]\n```\n\n### EFS\n\nTo list all the StorageClasses that are defined in the Kubernetes cluster, run the following command:\n\n```bash\nkubectl get storageclass\n```\n\nThe output should look similar to the following:\n\n```bash\nNAME                      PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE\nefs-encrypted (default)   efs.csi.aws.com         Delete          Immediate              false                  70m\n```\n\nTo retrieve the KMS Key ID parameter of a specific StorageClass named \"efs-encrypted\" in the Kubernetes cluster, run the following command:\n\n```bash\nkubectl get storageclass efs-encrypted -o jsonpath='{.parameters.kmsKeyId}'\n```\n\nThe output should look similar to the following:\n\n```bash\narn:aws:kms:us-east-1:111222333444:key/19f4f602-dcf3-42a5-8eef-38f2af4b3626%  \n```\n\nTo list all the PersistentVolumeClaims (PVCs) that exist in the Kubernetes cluster's namespace named \"data\", run the following command:\n\n```bash\nkubectl get pvc -n data\n```\n\nThe output should look similar to the following:\n\n```bash\nNAME                  STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS    AGE\nefs-encrypted-claim   Bound    pvc-06df2640-ae2f-44ae-8d5c-82c72e56a9ae   10Gi       RWX            efs-encrypted   63m\n```\n\nTo list all the pods that are running in the Kubernetes cluster's namespace named \"data\", run the following command:\n\n```bash\nkubectl get pods -n data\n```\n\nThe output should look similar to the following:\n\n```bash\nNAME                 READY   STATUS    RESTARTS   AGE\nefs-encryption-app   1/1     Running   0          63m\n```\n\nTo get detailed information about a PersistentVolumeClaim (PVC) named \"efs-encrypted-claim\" in the \"data\" namespace of the Kubernetes cluster, run the following command:\n\n```bash\nkubectl describe pvc efs-encrypted-claim -n data\n```\n\nThe output should look similar to the following:\n\n```bash\nName:          efs-encrypted-claim\nNamespace:     data\nStorageClass:  efs-encrypted\nStatus:        Bound\nVolume:        pvc-06df2640-ae2f-44ae-8d5c-82c72e56a9ae\nLabels:        argocd.argoproj.io/instance=team-data\nAnnotations:   pv.kubernetes.io/bind-completed: yes\n               pv.kubernetes.io/bound-by-controller: yes\n               volume.beta.kubernetes.io/storage-provisioner: efs.csi.aws.com\n               volume.kubernetes.io/storage-provisioner: efs.csi.aws.com\nFinalizers:    [kubernetes.io/pvc-protection]\nCapacity:      10Gi\nAccess Modes:  RWX\nVolumeMode:    Filesystem\nUsed By:       efs-encryption-app\nEvents:        <none>\n```\n"
  },
  {
    "path": "docs/patterns/security/guardduty.md",
    "content": "# Amazon GuardDuty Protection\n\n## Objective\n\nThe objective of this pattern is to demonstrate how to enable Amazon GuardDuty Detector across your AWS accounts, use GuardDuty optional features, and how to automate notifications via Amazon SNS based on security findings generated by GuardDuty.\n\nSupported features:\n\n- [Foundational data sources](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_data-sources.html) - these data sources are enabled by default, no need to mention them in the pattern input\n- [EKS Audit Log Monitoring](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-eks-audit-log-monitoring.html)\n- [EKS Runtime Monitoring](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-eks-runtime-monitoring.html)\n- [Malware Protection in Amazon GuardDuty](https://docs.aws.amazon.com/guardduty/latest/ug/malware-protection.html)\n- [GuardDuty RDS Protection](https://docs.aws.amazon.com/guardduty/latest/ug/rds-protection.html)\n- [Amazon S3 Protection in Amazon GuardDuty](https://docs.aws.amazon.com/guardduty/latest/ug/s3-protection.html)\n\nThe pattern consists of two components:\n\n- `GuardDutySetupStack` - enables GuardDuty Detector for the account. The stack also creates an SNS topic, SNS Subscription, and Amazon EventBridge Rule.\n- A blueprint that deploys a sample GitOps workload that triggers a GuardDuty finding.\n\nThe list of optional features is adjustable via the `features` parameter in the [GuardDutySetupStack](../../../lib/security/guardduty-construct/guardduty-setup.ts) stack.\n\n## GitOps configuration\n\nFor GitOps, the blueprint bootstraps the ArgoCD addon and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\n\nThe sample repository contains the following workloads:\n\n- `team-danger` runs a pod in a privileged mode which is a [security anti-pattern](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-kubernetes.html#privilegeescalation-kubernetes-privilegedcontainer)\n- `team-danger` runs a pod with a [malicious file](https://docs.aws.amazon.com/guardduty/latest/ug/findings-malware-protection.html#execution-malware-kubernetes-maliciousfile)\n\n## Prerequisites\n\n1. Follow the usage [instructions](https://github.com/aws-samples/cdk-eks-blueprints-patterns/blob/main/README.md#usage) to install the dependencies and perform the repository setup.\n2. `argo-admin-password` secret must be defined in Secrets Manager in the same region as the EKS cluster.\n\n## Deploy\n\nTo bootstrap the CDK toolkit and list all stacks in the app, run the following commands:\n\n```bash\ncdk bootstrap\nmake list\n```\n\n### Deploying the `GuardDutySetupStack` stack\n\nThe `GuardDutySetupStack` stack enables GuardDuty Detector for the account with all the features of your choice enabled.\n\nTo deploy the stack, run the following command:\n\n```bash\nmake pattern guardduty deploy guardduty-setup\n```\n\n### Deploying the blueprint workload\n\nThe blueprint deploys a sample GitOps workload that triggers a GuardDuty finding.\n\nTo deploy the blueprint, run the following command:\n\n```bash\nmake pattern guardduty deploy guardduty-blueprint\n```\n\n## Verify\n\nRun update-kubeconfig command. You should be able to get the command from CDK output message. More information can be found [here](https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access). Please replace `<your cluster name>`, `<your region>`, and `<your cluster role arn>` with the values from the CDK output message.\n\n```bash\naws eks update-kubeconfig --name <your cluster name> --region <your region> --role-arn <your cluster role arn>\n```\n\n### Verifying that the GuardDuty detector is enabled\n\nNow you can check that the GuardDuty detector is successfully enabled with all the required data sources.\n\nTo list all detectors in the region, run the following command:\n\n```bash\naws guardduty list-detectors --region us-east-1\n```\n\nThe output should look like this:\n\n```json\n{\n    \"DetectorIds\": [\n        \"80c3c03d44819a984b035b000aa9b3da\"\n    ]\n}\n```\n\nTo check the detector's configuration, run the following command (please replace `<DETECTOR-ID>` with the ID of the detector):\n\n```bash\naws guardduty get-detector --detector-id <DETECTOR-ID> --region us-east-1\n```\n\nThe output should look like this:\n\n```json\n{\n    \"CreatedAt\": \"2023-04-14T15:55:27.088Z\",\n    \"FindingPublishingFrequency\": \"SIX_HOURS\",\n    \"ServiceRole\": \"arn:aws:iam::123456789012:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty\",\n    \"Status\": \"ENABLED\",\n    \"UpdatedAt\": \"2023-04-14T15:55:27.088Z\",\n    \"DataSources\": {\n        \"CloudTrail\": {\n            \"Status\": \"ENABLED\"\n        },\n        \"DNSLogs\": {\n            \"Status\": \"ENABLED\"\n        },\n        \"FlowLogs\": {\n            \"Status\": \"ENABLED\"\n        },\n        \"S3Logs\": {\n            \"Status\": \"ENABLED\"\n        },\n        \"Kubernetes\": {\n            \"AuditLogs\": {\n                \"Status\": \"ENABLED\"\n            }\n        },\n        \"MalwareProtection\": {\n            \"ScanEc2InstanceWithFindings\": {\n                \"EbsVolumes\": {\n                    \"Status\": \"ENABLED\"\n                }\n            },\n            \"ServiceRole\": \"arn:aws:iam::123456789012:role/aws-service-role/malware-protection.guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDutyMalwareProtection\"\n        }\n    },\n    \"Tags\": {},\n    \"Features\": [\n        {\n            \"Name\": \"CLOUD_TRAIL\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T11:08:44-05:00\"\n        },\n        {\n            \"Name\": \"DNS_LOGS\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T11:08:44-05:00\"\n        },\n        {\n            \"Name\": \"FLOW_LOGS\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T11:08:44-05:00\"\n        },\n        {\n            \"Name\": \"S3_DATA_EVENTS\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T10:55:27-05:00\"\n        },\n        {\n            \"Name\": \"EKS_AUDIT_LOGS\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T10:55:27-05:00\"\n        },\n        {\n            \"Name\": \"EBS_MALWARE_PROTECTION\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T10:55:27-05:00\"\n        },\n        {\n            \"Name\": \"RDS_LOGIN_EVENTS\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T10:55:27-05:00\"\n        },\n        {\n            \"Name\": \"EKS_RUNTIME_MONITORING\",\n            \"Status\": \"ENABLED\",\n            \"UpdatedAt\": \"2023-04-14T10:55:27-05:00\",\n            \"AdditionalConfiguration\": [\n                {\n                    \"Name\": \"EKS_ADDON_MANAGEMENT\",\n                    \"Status\": \"ENABLED\",\n                    \"UpdatedAt\": \"2023-04-14T10:55:27-05:00\"\n                }\n            ]\n        }\n    ]\n}\n ```\n\n### Verifying that the GuardDuty findings are generated\n\nTo list all findings in the region, run the following command (please replace `<DETECTOR-ID>` with the ID of the detector):\n\n```bash\naws guardduty list-findings --detector-id <DETECTOR-ID> --region us-east-1\n```\n\nThe output should look like this:\n\n```json\n{\n    \"FindingIds\": [\n        \"f2c3859c6ca25b3057d13470a992bbd7\"\n    ]\n}\n```\n\nTo check the finding's details, run the following command (please replace `<DETECTOR-ID>` and `<FINDING-ID>` with the ID of the detector and the ID of the finding):\n\n```bash\naws guardduty get-findings --detector-id <DETECTOR-ID> --finding-ids <FINDING-ID> --region us-east-1\n```\n\nThe list of findings contains `PrivilegeEscalation:Kubernetes/PrivilegedContainer` as expected:\n\n```json\n{\n    \"Findings\": [\n        {\n            \"AccountId\": \"123456789012\",\n            \"Arn\": \"arn:aws:guardduty:us-east-1:123456789012:detector/94c3858788bc1444ceedab472bab5d7e/finding/f2c3859c6ca25b3057d13470a992bbd7\",\n            \"CreatedAt\": \"2023-03-22T21:28:07.748Z\",\n            \"Description\": \"A privileged container with root level access was launched on EKS Cluster guardduty-blueprint. If this behavior is not expected, it may indicate that your credentials are compromised.\",\n            \"Id\": \"f2c3859c6ca25b3057d13470a992bbd7\",\n            \"Partition\": \"aws\",\n            \"Region\": \"us-east-1\",\n            \"Resource\": {\n                \"EksClusterDetails\": {\n                    \"Name\": \"guardduty-blueprint\",\n                    \"Arn\": \"arn:aws:eks:us-east-1:123456789012:cluster/guardduty-blueprint\",\n                    \"VpcId\": \"vpc-02b68c9ddc1d403ab\",\n                    \"Status\": \"ACTIVE\",\n                    \"Tags\": [],\n                    \"CreatedAt\": \"2023-03-22T15:48:25.752000-05:00\"\n                },\n                \"KubernetesDetails\": {\n                    \"KubernetesUserDetails\": {\n                        \"Username\": \"system:serviceaccount:argocd:argocd-application-controller\",\n                        \"Uid\": \"1871d525-442e-487f-ae60-81336d1ff0cf\",\n                        \"Groups\": [\n                            \"system:serviceaccounts\",\n                            \"system:serviceaccounts:argocd\",\n                            \"system:authenticated\"\n                        ]\n                    },\n                    \"KubernetesWorkloadDetails\": {\n                        \"Name\": \"privileged-pod\",\n                        \"Type\": \"pods\",\n                        \"Uid\": \"33a3c89e-3280-474d-b8cb-fdf03394fc15\",\n                        \"Namespace\": \"argocd\",\n                        \"HostNetwork\": false,\n                        \"Containers\": [\n                            {\n                                \"Name\": \"app\",\n                                \"Image\": \"centos\",\n                                \"ImagePrefix\": \"\",\n                                \"SecurityContext\": {\n                                    \"Privileged\": true\n                                }\n                            }\n                        ]\n                    }\n                },\n                \"ResourceType\": \"EKSCluster\"\n            },\n            \"SchemaVersion\": \"2.0\",\n            \"Service\": {\n                \"Action\": {\n                    \"ActionType\": \"KUBERNETES_API_CALL\",\n                    \"KubernetesApiCallAction\": {\n                        \"RequestUri\": \"/api/v1/namespaces/argocd/pods\",\n                        \"Verb\": \"create\",\n                        \"UserAgent\": \"argocd-application-controller/v0.0.0 (linux/amd64) kubernetes/$Format\",\n                        \"RemoteIpDetails\": {\n                            \"City\": {\n                                \"CityName\": \"UNKNOWN\"\n                            },\n                            \"Country\": {},\n                            \"GeoLocation\": {\n                                \"Lat\": 0.0,\n                                \"Lon\": 0.0\n                            },\n                            \"IpAddressV4\": \"10.0.205.129\",\n                            \"Organization\": {\n                                \"Asn\": \"0\",\n                                \"AsnOrg\": \"UNKNOWN\",\n                                \"Isp\": \"UNKNOWN\",\n                                \"Org\": \"UNKNOWN\"\n                            }\n                        },\n                        \"StatusCode\": 201\n                    }\n                },\n                \"Archived\": false,\n                \"Count\": 1,\n                \"DetectorId\": \"94c3858788bc1444ceedab472bab5d7e\",\n                \"EventFirstSeen\": \"2023-03-22T21:27:18.186Z\",\n                \"EventLastSeen\": \"2023-03-22T21:27:18.630Z\",\n                \"ResourceRole\": \"TARGET\",\n                \"ServiceName\": \"guardduty\",\n                \"AdditionalInfo\": {\n                    \"Value\": \"{}\",\n                    \"Type\": \"default\"\n                }\n            },\n            \"Severity\": 5,\n            \"Title\": \"Privileged container with root level access launched on the EKS Cluster.\",\n            \"Type\": \"PrivilegeEscalation:Kubernetes/PrivilegedContainer\",\n            \"UpdatedAt\": \"2023-03-22T21:28:07.748Z\"\n        }\n    ]\n}\n```\n\n### Verifying that the GuardDuty Runtime Monitoring agents are automatically deployed\n\nTo verify that the GuardDuty Runtime Monitoring agents are automatically deployed, run the following command:\n\n```bash\nkubectl get pods -A\n```\n\nThe output should look like this:\n\n```bash\nNAMESPACE          NAME                                                              READY   STATUS    RESTARTS        AGE\namazon-guardduty   aws-guardduty-agent-qrm22                                         1/1     Running   0               25m\nargocd             blueprints-addon-argocd-application-controller-0                  1/1     Running   0               3m25s\nargocd             blueprints-addon-argocd-applicationset-controller-7c4c75877579s   1/1     Running   0               3m25s\nargocd             blueprints-addon-argocd-dex-server-c6687d84f-q4697                1/1     Running   1 (3m21s ago)   3m25s\nargocd             blueprints-addon-argocd-notifications-controller-7c74f76c5wh4nb   1/1     Running   0               3m25s\nargocd             blueprints-addon-argocd-redis-595cc69fff-9985j                    1/1     Running   0               3m25s\nargocd             blueprints-addon-argocd-repo-server-7f75c7796c-229c4              1/1     Running   0               3m25s\nargocd             blueprints-addon-argocd-server-86867c9dd8-p6qk7                   1/1     Running   0               3m25s\nargocd             privileged-pod                                                    1/1     Running   0               115s\nkube-system        aws-node-4lhp7                                                    1/1     Running   0               26m\nkube-system        coredns-79989457d9-jncrb                                          1/1     Running   0               32m\nkube-system        coredns-79989457d9-l5jcg                                          1/1     Running   0               32m\nkube-system        kube-proxy-hwkwm                                                  1/1     Running   0               26m\n```\n\nAs you can see, the GuardDuty Runtime Monitoring agent is deployed in the `amazon-guardduty` namespace.\n"
  },
  {
    "path": "docs/patterns/security/image-scanning.md",
    "content": "# Amazon ECR Image Scanning\n\n## Objective\n\nThe objective of this pattern is to demonstrate how to enable and configure Amazon ECR image scanning.\n\nThe following scanning types are offered:\n\n- **Enhanced scanning** — Amazon ECR integrates with Amazon Inspector to provide automated, continuous scanning of your repositories. Your container images are scanned for both operating systems and programing language package vulnerabilities. As new vulnerabilities appear, the scan results are updated and Amazon Inspector emits an event to EventBridge to notify you.\n- **Basic scanning** — Amazon ECR uses the Common Vulnerabilities and Exposures (CVEs) database from the open-source Clair project. With basic scanning, you configure your repositories to scan on push or you can perform manual scans and Amazon ECR provides a list of scan findings.\n\nThe pattern consists of two components:\n\n- `ImageScanningSetupStack` - configures the Amazon ECR image scanning and the ECR automated re-scan duration in Inspector.\n- A blueprint that deploys a sample GitOps workload that pushes images to Amazon ECR and triggers the image scanning.\n\n## Configuration\n\nYou can configure the following parameters in the [ImageScanningSetupStack](../../../lib/security/image-vulnerability-scanning/image-scanning-setup.ts) stack:\n\n- `scanType` - The type of scan to perform. Valid values are `BASIC` and `ENHANCED`.\n- Enhanced scanning only:\n  - `enhancedContinuousScanDuration` - the Amazon ECR automated re-scan duration setting determines how long Amazon Inspector continuously monitors images pushed into repositories. When the number of days from when an image is first pushed exceeds the automated re-scan duration configuration, Amazon Inspector stops monitoring the image. When Amazon Inspector stops monitoring an image, the scan status of the image is changed to inactive with a reason code of expired, and all associated findings for the image are scheduled to be closed. Valid values are `LIFETIME`, `DAYS_30`, and `DAYS_180`.\n  - `enhancedScanRules` - the scanning rules.\n- Basic scanning only:\n  - `basicScanRules` - the scanning rules.\n\nPlease refer to the [Amazon ECR Image Scanning](https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-scanning.html) documentation for more information and how to use filters.\n\n## GitOps confguration\n\nFor GitOps, the blueprint bootstraps the ArgoCD addon and points to the [EKS Blueprints Workload](https://github.com/aws-samples/eks-blueprints-workloads) sample repository.\n\nThe sample repository contains the following workloads:\n\n- `team-scan` pushes a Docker image to Amazon ECR and triggers the image scanning.\n\n## Prerequisites\n\n1. Follow the usage [instructions](https://github.com/aws-samples/cdk-eks-blueprints-patterns/blob/main/README.md#usage) to install the dependencies and perform the repository setup.\n2. `argo-admin-password` secret must be defined in Secrets Manager in the same region as the EKS cluster.\n\n## Deploy\n\nTo bootstrap the CDK toolkit and list all stacks in the app, run the following commands:\n\n```bash\ncdk bootstrap\nmake list\n```\n\n### Deploying the `ImageScanningSetupStack` stack\n\nThe `ImageScanningSetupStack` configures the Amazon ECR image scanning and the ECR automated re-scan duration in Inspector.\n\nTo deploy the stack, run the following command:\n\n```bash\nmake pattern ecr-image-scanning deploy image-scanning-setup\n```\n\n### Deploying the blueprint\n\nThe blueprint deploys a sample GitOps workload that pushes images to Amazon ECR and triggers the image scanning.\n\nTo deploy the blueprint, run the following command:\n\n```bash\nmake pattern ecr-image-scanning deploy image-scanning-workload-blueprint\n```\n\n## Verify\n\n### Verifying that the image scanning is enabled\n\nTo verify that the image scanning is enabled at the registry level, run the following command:\n\n```bash\naws ecr get-registry-scanning-configuration\n```\n\nThe output should look similar to the following:\n\n```json\n{\n    \"registryId\": \"123456789012\",\n    \"scanningConfiguration\": {\n        \"scanType\": \"ENHANCED\",\n        \"rules\": [\n            {\n                \"scanFrequency\": \"CONTINUOUS_SCAN\",\n                \"repositoryFilters\": [\n                    {\n                        \"filter\": \"prod\",\n                        \"filterType\": \"WILDCARD\"\n                    }\n                ]\n            },\n            {\n                \"scanFrequency\": \"SCAN_ON_PUSH\",\n                \"repositoryFilters\": [\n                    {\n                        \"filter\": \"*\",\n                        \"filterType\": \"WILDCARD\"\n                    }\n                ]\n            }\n        ]\n    }\n}\n```\n\n### Verifying that the image is pushed to Amazon ECR\n\nTo verify that the image is pushed to Amazon ECR, run the following command (please replace `<REPOSITORY-NAME>` with the repository name):\n\n```bash\naws ecr describe-images --repository-name <REPOSITORY-NAME>\n```\n\nThe output should look similar to the following:\n\n```json\n{\n    \"imageDetails\": [\n        {\n            \"registryId\": \"123456789012\",\n            \"repositoryName\": \"image-scanning-workload-blueprint-imagescanningrepository754c6116-arh0wk3afnkw\",\n            \"imageDigest\": \"sha256:a1801b843b1bfaf77c501e7a6d3f709401a1e0c83863037fa3aab063a7fdb9dc\",\n            \"imageTags\": [\n                \"latest\"\n            ],\n            \"imageSizeInBytes\": 83520228,\n            \"imagePushedAt\": \"2023-04-17T17:22:33-05:00\",\n            \"imageManifestMediaType\": \"application/vnd.docker.distribution.manifest.v2+json\",\n            \"artifactMediaType\": \"application/vnd.docker.container.image.v1+json\",\n            \"lastRecordedPullTime\": \"2023-04-17T17:22:33.966000-05:00\"\n        }\n    ]\n}\n```\n\n### Checking the image scanning findings\n\nTo check the image scanning findings, run the following command (please replace `<REPOSITORY-NAME>` with the repository name):\n\n```bash\naws ecr describe-image-scan-findings --repository-name <REPOSITORY-NAME> --image-id imageTag=latest\n```\n\nThe output should look similar to the following:\n\n```json\n{\n    \"imageScanFindings\": {\n        \"enhancedFindings\": [\n            {\n                \"awsAccountId\": \"123456789012\",\n                \"description\": \"basic/unit-name.c in systemd prior to 246.15, 247.8, 248.5, and 249.1 has a Memory Allocation with an Excessive Size Value (involving strdupa and alloca for a pathname controlled by a local attacker) that results in an operating system crash.\",\n                \"findingArn\": \"arn:aws:inspector2:us-east-1:123456789012:finding/0407d7719da0fc8a8f44991f0bf524d6\",\n                \"firstObservedAt\": \"2023-04-17T17:40:39.940000-05:00\",\n                \"lastObservedAt\": \"2023-04-17T17:40:39.940000-05:00\",\n                \"packageVulnerabilityDetails\": {\n                    \"cvss\": [\n                        {\n                            \"baseScore\": 4.9,\n                            \"scoringVector\": \"AV:L/AC:L/Au:N/C:N/I:N/A:C\",\n                            \"source\": \"NVD\",\n                            \"version\": \"2.0\"\n                        },\n                        {\n                            \"baseScore\": 5.5,\n                            \"scoringVector\": \"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H\",\n                            \"source\": \"NVD\",\n                            \"version\": \"3.1\"\n                        }\n                    ],\n                    \"referenceUrls\": [\n                        \"https://www.debian.org/security/2021/dsa-4942\",\n                        \"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/42TMJVNYRY65B4QCJICBYOEIVZV3KUYI/\",\n                        \"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/2LSDMHAKI4LGFOCSPXNVVSEWQFAVFWR7/\",\n                        \"https://security.gentoo.org/glsa/202107-48\",\n                        \"https://cert-portal.siemens.com/productcert/pdf/ssa-222547.pdf\"\n                    ],\n                    \"relatedVulnerabilities\": [],\n                    \"source\": \"NVD\",\n                    \"sourceUrl\": \"https://nvd.nist.gov/vuln/detail/CVE-2021-33910\",\n                    \"vendorCreatedAt\": \"2021-07-20T14:15:00-05:00\",\n                    \"vendorSeverity\": \"MEDIUM\",\n                    \"vendorUpdatedAt\": \"2022-06-14T06:15:00-05:00\",\n                    \"vulnerabilityId\": \"CVE-2021-33910\",\n                    \"vulnerablePackages\": [\n                        {\n                            \"arch\": \"X86_64\",\n                            \"epoch\": 0,\n                            \"name\": \"systemd-pam\",\n                            \"packageManager\": \"OS\",\n                            \"release\": \"45.el8\",\n                            \"sourceLayerHash\": \"sha256:a1d0c75327776413fa0db9ed3adcdbadedc95a662eb1d360dad82bb913f8a1d1\",\n                            \"version\": \"239\"\n                        },\n                        {\n                            \"arch\": \"X86_64\",\n                            \"epoch\": 0,\n                            \"name\": \"systemd\",\n                            \"packageManager\": \"OS\",\n                            \"release\": \"45.el8\",\n                            \"sourceLayerHash\": \"sha256:a1d0c75327776413fa0db9ed3adcdbadedc95a662eb1d360dad82bb913f8a1d1\",\n                            \"version\": \"239\"\n                        },\n                        {\n                            \"arch\": \"X86_64\",\n                            \"epoch\": 0,\n                            \"name\": \"systemd-libs\",\n                            \"packageManager\": \"OS\",\n                            \"release\": \"45.el8\",\n                            \"sourceLayerHash\": \"sha256:a1d0c75327776413fa0db9ed3adcdbadedc95a662eb1d360dad82bb913f8a1d1\",\n                            \"version\": \"239\"\n                        },\n                        {\n                            \"arch\": \"X86_64\",\n                            \"epoch\": 0,\n                            \"name\": \"systemd-udev\",\n                            \"packageManager\": \"OS\",\n                            \"release\": \"45.el8\",\n                            \"sourceLayerHash\": \"sha256:a1d0c75327776413fa0db9ed3adcdbadedc95a662eb1d360dad82bb913f8a1d1\",\n                            \"version\": \"239\"\n                        }\n                    ]\n                },\n                \"remediation\": {\n                    \"recommendation\": {\n                        \"text\": \"None Provided\"\n                    }\n                },\n                \"resources\": [\n                    {\n                        \"details\": {\n                            \"awsEcrContainerImage\": {\n                                \"architecture\": \"amd64\",\n                                \"imageHash\": \"sha256:a1801b843b1bfaf77c501e7a6d3f709401a1e0c83863037fa3aab063a7fdb9dc\",\n                                \"imageTags\": [\n                                    \"latest\"\n                                ],\n                                \"platform\": \"CENTOS_8\",\n                                \"pushedAt\": \"2023-04-17T17:22:33-05:00\",\n                                \"registry\": \"123456789012\",\n                                \"repositoryName\": \"image-scanning-workload-blueprint-imagescanningrepository754c6116-arh0wk3afnkw\"\n                            }\n                        },\n                        \"id\": \"arn:aws:ecr:us-east-1:123456789012:repository/image-scanning-workload-blueprint-imagescanningrepository754c6116-arh0wk3afnkw/sha256:a1801b843b1bfaf77c501e7a6d3f709401a1e0c83863037fa3aab063a7fdb9dc\",\n                        \"tags\": {},\n                        \"type\": \"AWS_ECR_CONTAINER_IMAGE\"\n                    }\n                ],\n                \"score\": 5.5,\n                \"scoreDetails\": {\n                    \"cvss\": {\n                        \"adjustments\": [],\n                        \"score\": 5.5,\n                        \"scoreSource\": \"NVD\",\n                        \"scoringVector\": \"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H\",\n                        \"version\": \"3.1\"\n                    }\n                },\n                \"severity\": \"MEDIUM\",\n                \"status\": \"ACTIVE\",\n                \"title\": \"CVE-2021-33910 - systemd-pam, systemd and 2 more\",\n                \"type\": \"PACKAGE_VULNERABILITY\",\n                \"updatedAt\": \"2023-04-17T17:40:39.940000-05:00\"\n            },\n        }\n...            \n```\n\nYou can also check the findings in Inspector2.\n\n```bash\naws inspector2 list-findings\n```\n\nThe output should look similar to the following:\n\n```json\n{\n    \"findings\": [\n        {\n            \"awsAccountId\": \"123456789012\",\n            \"description\": \"When curl is instructed to get content using the metalink feature, and a user name and password are used to download the metalink XML file, those same credentials are then subsequently passed on to each of the servers from which curl will download or try to download the contents from. Often contrary to the user's expectations and intentions and without telling the user it happened.\",\n            \"exploitAvailable\": \"NO\",\n            \"findingArn\": \"arn:aws:inspector2:us-east-1:123456789012:finding/006e8eac196bf27417099413ce74eb1a\",\n            \"firstObservedAt\": \"2023-04-14T21:03:02.932000-05:00\",\n            \"fixAvailable\": \"YES\",\n            \"inspectorScore\": 5.3,\n            \"inspectorScoreDetails\": {\n                \"adjustedCvss\": {\n                    \"adjustments\": [],\n                    \"cvssSource\": \"NVD\",\n                    \"score\": 5.3,\n                    \"scoreSource\": \"NVD\",\n                    \"scoringVector\": \"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N\",\n                    \"version\": \"3.1\"\n                }\n            },\n            \"lastObservedAt\": \"2023-04-17T13:34:41.687000-05:00\",\n            \"packageVulnerabilityDetails\": {\n                \"cvss\": [\n                    {\n                        \"baseScore\": 2.6,\n                        \"scoringVector\": \"AV:N/AC:H/Au:N/C:P/I:N/A:N\",\n                        \"source\": \"NVD\",\n                        \"version\": \"2.0\"\n                    },\n                    {\n                        \"baseScore\": 5.3,\n                        \"scoringVector\": \"CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N\",\n                        \"source\": \"NVD\",\n                        \"version\": \"3.1\"\n                    }\n                ],\n                \"referenceUrls\": [\n                    \"https://hackerone.com/reports/1213181\",\n                    \"https://security.gentoo.org/glsa/202212-01\",\n                    \"https://cert-portal.siemens.com/productcert/pdf/ssa-389290.pdf\",\n                    \"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/FRUCW2UVNYUDZF72DQLFQR4PJEC6CF7V/\",\n                    \"https://www.oracle.com/security-alerts/cpuoct2021.html\"\n                ],\n                \"relatedVulnerabilities\": [],\n                \"source\": \"NVD\",\n                \"sourceUrl\": \"https://nvd.nist.gov/vuln/detail/CVE-2021-22923\",\n                \"vendorCreatedAt\": \"2021-08-05T16:15:00-05:00\",\n                \"vendorSeverity\": \"MEDIUM\",\n                \"vendorUpdatedAt\": \"2023-01-05T12:17:00-06:00\",\n                \"vulnerabilityId\": \"CVE-2021-22923\",\n                \"vulnerablePackages\": [\n                    {\n                        \"arch\": \"AARCH64\",\n                        \"epoch\": 0,\n                        \"fixedInVersion\": \"0:7.61.1-18.el8_4.1\",\n                        \"name\": \"curl\",\n                        \"packageManager\": \"OS\",\n                        \"release\": \"18.el8\",\n                        \"remediation\": \"dnf update curl\",\n                        \"sourceLayerHash\": \"sha256:52f9ef134af7dd14738733e567402af86136287d9468978d044780a6435a1193\",\n                        \"version\": \"7.61.1\"\n                    },\n                    {\n                        \"arch\": \"AARCH64\",\n                        \"epoch\": 0,\n                        \"fixedInVersion\": \"0:7.61.1-18.el8_4.1\",\n                        \"name\": \"libcurl-minimal\",\n                        \"packageManager\": \"OS\",\n                        \"release\": \"18.el8\",\n                        \"remediation\": \"dnf update libcurl-minimal\",\n                        \"sourceLayerHash\": \"sha256:52f9ef134af7dd14738733e567402af86136287d9468978d044780a6435a1193\",\n                        \"version\": \"7.61.1\"\n                    }\n                ]\n            },\n            \"remediation\": {\n                \"recommendation\": {\n                    \"text\": \"None Provided\"\n                }\n            },\n            \"resources\": [\n                {\n                    \"details\": {\n                        \"awsEcrContainerImage\": {\n                            \"architecture\": \"arm64\",\n                            \"imageHash\": \"sha256:65a4aad1156d8a0679537cb78519a17eb7142e05a968b26a5361153006224fdc\",\n                            \"imageTags\": [\n                                \"latest\"\n                            ],\n                            \"platform\": \"CENTOS_8\",\n                            \"pushedAt\": \"2023-04-17T13:34:34-05:00\",\n                            \"registry\": \"123456789012\",\n                            \"repositoryName\": \"cdk-hnb659fds-container-assets-123456789012-us-east-1\"\n                        }\n                    },\n                    \"id\": \"arn:aws:ecr:us-east-1:123456789012:repository/cdk-hnb659fds-container-assets-123456789012-us-east-1/sha256:65a4aad1156d8a0679537cb78519a17eb7142e05a968b26a5361153006224fdc\",\n                    \"partition\": \"aws\",\n                    \"region\": \"us-east-1\",\n                    \"tags\": {},\n                    \"type\": \"AWS_ECR_CONTAINER_IMAGE\"\n                }\n            ],\n            \"severity\": \"MEDIUM\",\n            \"status\": \"CLOSED\",\n            \"title\": \"CVE-2021-22923 - curl, libcurl-minimal\",\n            \"type\": \"PACKAGE_VULNERABILITY\",\n            \"updatedAt\": \"2023-04-17T13:36:44.258000-05:00\"\n        },\n...\n```\n"
  },
  {
    "path": "docs/patterns/security/securityhub.md",
    "content": "# AWS Security Hub Monitoring\n\n## Objective\n\nThe objective of this pattern is to demonstrate how to enable AWS Security Hub and default security standards in your AWS account, verify that it is enabled, and get findings from AWS Security Hub.\n\nThe pattern will enable AWS Security Hub in the `CDK_DEFAULT_ACCOUNT` and `CDK_DEFAULT_REGION`.\n\n## Prerequisites\n\n1. Follow the usage [instructions](https://github.com/aws-samples/cdk-eks-blueprints-patterns/blob/main/README.md#usage) to install the dependencies and perform the repository setup.\n2. `argo-admin-password` secret must be defined in Secrets Manager in the same region as the EKS cluster.\n3. Complete the steps to [enable AWS Config and deploy the Security Best Practices for Amazon EKS AWS Config managed rules](eks-config-rules.md).\n\n**Optional (but recommended):**  If you have not done so already, follow the steps to deploy the [Amazon GuardDuty stack and blueprint](guardduty.md). Since Amazon GuardDuty automatically sends its findings to AWS Security Hub, the sample EKS finding will appear in AWS Security Hub about five minutes after it has been enabled in the same region.\n\n## Deploy\n\nTo bootstrap the CDK toolkit and list all stacks in the app, run the following commands:\n\n```bash\ncdk bootstrap\nmake list\n```\n\n### Deploy AWS Security Hub\n\nTo enable AWS Security Hub in the account and region deploy the stack, run the following command.\n\n```bash\nmake pattern securityhub deploy securityhub-setup\n```\n\nOnce deployed, AWS Security Hub will automatically enable all controls that are part of the default security standards. Currently, the default security standards that are automatically enabled are [AWS Foundational Security Best Practices](https://docs.aws.amazon.com/securityhub/latest/userguide/fsbp-standard.html) and the [Center for Internet Security (CIS) AWS Foundations Benchmark v1.2.0](https://docs.aws.amazon.com/securityhub/latest/userguide/cis-aws-foundations-benchmark.html).\n\n## Verify\n\n### Verify that AWS Security Hub is enabled\n\nNow you can check that AWS Security Hub is successfully enabled by using the AWS CLI to query the same account and region.\n\nUsing the AWS CLI run following command in the same account and region where you deployed the stack.\n\n```bash\naws securityhub describe-hub\n```\n\nIf you successfully enabled AWS Security Hub, you will see the following.\n\n```json\n{\n    \"HubArn\": \"arn:aws:securityhub:us-east-1:XXXXXXXXXXXX:hub/default\",\n    \"SubscribedAt\": \"2021-08-18T00:52:40.624Z\",\n    \"AutoEnableControls\": true,\n    \"ControlFindingGenerator\": \"SECURITY_CONTROL\"\n}\n```\n\n### View findings in AWS Security Hub\n\nUse the following AWS CLI commands to view your findings in AWS Security Hub.\n\nTo list critical findings and findings related to controls that have a failed status according to the enabled [AWS Security Hub security standards](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards.html) in the same account and region, run the following command:\n\n```bash\naws securityhub get-findings --filter 'SeverityLabel={Value=CRITICAL,Comparison=EQUALS},ComplianceStatus={Value=FAILED,Comparison=EQUALS}'\n```\n\nBelow is an example of an IAM finding that relates to a [failed IAM control](https://docs.aws.amazon.com/securityhub/latest/userguide/iam-controls.html#iam-6) that AWS Security Hub found related to the enabled [security standards](https://docs.aws.amazon.com/securityhub/latest/userguide/standards-reference.html), and will likely be present in your list of findings if you or your organization are not using a hardware MFA device for your AWS root account:\n\n```json\n{\n    \"Findings\": [\n        {\n            \"SchemaVersion\": \"2018-10-08\",\n            \"Id\": \"arn:aws:securityhub:us-east-1:XXXXXXXXXXX:security-control/IAM.6/finding/494ffa38-0b6e-46d1-98f4-e605ec09d045\",\n            \"ProductArn\": \"arn:aws:securityhub:us-east-1::product/aws/securityhub\",\n            \"ProductName\": \"Security Hub\",\n            \"CompanyName\": \"AWS\",\n            \"Region\": \"us-east-1\",\n            \"GeneratorId\": \"security-control/IAM.6\",\n            \"AwsAccountId\": \"XXXXXXXXXXX\",\n            \"Types\": [\n                \"Software and Configuration Checks/Industry and Regulatory Standards\"\n            ],\n            \"FirstObservedAt\": \"2023-03-04T00:54:44.307Z\",\n            \"LastObservedAt\": \"2023-05-31T01:20:18.210Z\",\n            \"CreatedAt\": \"2023-03-04T00:54:44.307Z\",\n            \"UpdatedAt\": \"2023-05-31T01:20:05.845Z\",\n            \"Severity\": {\n                \"Label\": \"CRITICAL\",\n                \"Normalized\": 90,\n                \"Original\": \"CRITICAL\"\n            },\n            \"Title\": \"Hardware MFA should be enabled for the root user\",\n            \"Description\": \"This AWS control checks whether your AWS account is enabled to use a hardware multi-factor authentication (MFA) device to sign in with root user credentials.\",\n            \"Remediation\": {\n                \"Recommendation\": {\n                    \"Text\": \"For information on how to correct this issue, consult the AWS Security Hub controls documentation.\",\n                    \"Url\": \"https://docs.aws.amazon.com/console/securityhub/IAM.6/remediation\"\n                }\n            },\n            \"ProductFields\": {\n                \"RelatedAWSResources:0/name\": \"securityhub-root-account-hardware-mfa-enabled-24e3b344\",\n                \"RelatedAWSResources:0/type\": \"AWS::Config::ConfigRule\",\n                \"aws/securityhub/ProductName\": \"Security Hub\",\n                \"aws/securityhub/CompanyName\": \"AWS\",\n                \"Resources:0/Id\": \"arn:aws:iam::XXXXXXXXXXX:root\",\n                \"aws/securityhub/FindingId\": \"arn:aws:securityhub:us-east-1::product/aws/securityhub/arn:aws:securityhub:us-east-1:XXXXXXXXXXX:security-control/IAM.6/finding/494ffa38-0b6e-46d1-98f4-e605ec09d045\"\n            },\n            \"Resources\": [\n                {\n                    \"Type\": \"AwsAccount\",\n                    \"Id\": \"AWS::::Account:XXXXXXXXXXX\",\n                    \"Partition\": \"aws\",\n                    \"Region\": \"us-east-1\"\n                }\n            ],\n            \"Compliance\": {\n                \"Status\": \"FAILED\",\n                \"RelatedRequirements\": [\n                    \"CIS AWS Foundations Benchmark v1.2.0/1.14\",\n                    \"CIS AWS Foundations Benchmark v1.4.0/1.6\",\n                    \"NIST.800-53.r5 AC-2(1)\",\n                    \"NIST.800-53.r5 AC-3(15)\",\n                    \"NIST.800-53.r5 IA-2(1)\",\n                    \"NIST.800-53.r5 IA-2(2)\",\n                    \"NIST.800-53.r5 IA-2(6)\",\n                    \"NIST.800-53.r5 IA-2(8)\",\n                    \"PCI DSS v3.2.1/8.3.1\"\n                ],\n                \"SecurityControlId\": \"IAM.6\",\n                \"AssociatedStandards\": [\n                    {\n                        \"StandardsId\": \"ruleset/cis-aws-foundations-benchmark/v/1.2.0\"\n                    },\n                    {\n                        \"StandardsId\": \"standards/aws-foundational-security-best-practices/v/1.0.0\"\n                    },\n                    {\n                        \"StandardsId\": \"standards/cis-aws-foundations-benchmark/v/1.4.0\"\n                    },\n                    {\n                        \"StandardsId\": \"standards/nist-800-53/v/5.0.0\"\n                    },\n                    {\n                        \"StandardsId\": \"standards/pci-dss/v/3.2.1\"\n                    }\n                ]\n            },\n            \"WorkflowState\": \"NEW\",\n            \"Workflow\": {\n                \"Status\": \"NEW\"\n            },\n            \"RecordState\": \"ACTIVE\",\n            \"FindingProviderFields\": {\n                \"Severity\": {\n                    \"Label\": \"CRITICAL\",\n                    \"Original\": \"CRITICAL\"\n                },\n                \"Types\": [\n                    \"Software and Configuration Checks/Industry and Regulatory Standards\"\n                ]\n            }\n        }\n    ]\n}\n```\n\nTo search for findings related to the Security Best Practices for Amazon EKS Config managed rules, run the following AWS CLI command:\n\n```bash\naws securityhub get-findings --filters 'GeneratorId={Value=\"security-control/EKS.1\", Comparison=\"EQUALS\"}'\n```\n\nYou might see a finding such as the following:\n\n```json\n{\n    \"Findings\": [\n        {\n            \"SchemaVersion\": \"2018-10-08\",\n            \"Id\": \"arn:aws:securityhub:us-east-1:XXXXXXXXXXX:security-control/EKS.1/finding/931a06d9-1b1d-431b-8b91-1ff86829b400\",\n            \"ProductArn\": \"arn:aws:securityhub:us-east-1::product/aws/securityhub\",\n            \"ProductName\": \"Security Hub\",\n            \"CompanyName\": \"AWS\",\n            \"Region\": \"us-east-1\",\n            \"GeneratorId\": \"security-control/EKS.1\",\n            \"AwsAccountId\": \"XXXXXXXXXXX\",\n            \"Types\": [\n                \"Software and Configuration Checks/Industry and Regulatory Standards\"\n            ],\n            \"FirstObservedAt\": \"2023-05-09T10:34:36.736Z\",\n            \"LastObservedAt\": \"2023-05-30T10:27:41.205Z\",\n            \"CreatedAt\": \"2023-05-09T10:34:36.736Z\",\n            \"UpdatedAt\": \"2023-05-30T10:27:34.574Z\",\n            \"Severity\": {\n                \"Label\": \"HIGH\",\n                \"Normalized\": 70,\n                \"Original\": \"HIGH\"\n            },\n            \"Title\": \"EKS cluster endpoints should not be publicly accessible\",\n            \"Description\": \"This control checks whether an Amazon EKS cluster endpoint is publicly accessible. The control fails if an EKS cluster has an endpoint that is publicly accessible.\",\n            \"Remediation\": {\n                \"Recommendation\": {\n                    \"Text\": \"For information on how to correct this issue, consult the AWS Security Hub controls documentation.\",\n                    \"Url\": \"https://docs.aws.amazon.com/console/securityhub/EKS.1/remediation\"\n                }\n            },\n            \"ProductFields\": {\n                \"RelatedAWSResources:0/name\": \"securityhub-eks-endpoint-no-public-access-f5aecad6\",\n                \"RelatedAWSResources:0/type\": \"AWS::Config::ConfigRule\",\n                \"aws/securityhub/ProductName\": \"Security Hub\",\n                \"aws/securityhub/CompanyName\": \"AWS\",\n                \"aws/securityhub/annotation\": \"Cluster Endpoint of starter-blueprint is Publicly accessible\",\n                \"Resources:0/Id\": \"arn:aws:eks:us-east-1:XXXXXXXXXXX:cluster/starter-blueprint\",\n                \"aws/securityhub/FindingId\": \"arn:aws:securityhub:us-east-1::product/aws/securityhub/arn:aws:securityhub:us-east-1:XXXXXXXXXXX:security-control/EKS.1/finding/931a06d9-1b1d-431b-8b91-1ff86829b400\"\n            },\n            \"Resources\": [\n                {\n                    \"Type\": \"AwsEksCluster\",\n                    \"Id\": \"arn:aws:eks:us-east-1:XXXXXXXXXXX:cluster/starter-blueprint\",\n                    \"Partition\": \"aws\",\n                    \"Region\": \"us-east-1\"\n                }\n            ],\n            \"Compliance\": {\n                \"Status\": \"FAILED\",\n                \"RelatedRequirements\": [\n                    \"NIST.800-53.r5 AC-21\",\n                    \"NIST.800-53.r5 AC-3\",\n                    \"NIST.800-53.r5 AC-3(7)\",\n                    \"NIST.800-53.r5 AC-4\",\n                    \"NIST.800-53.r5 AC-4(21)\",\n                    \"NIST.800-53.r5 AC-6\",\n                    \"NIST.800-53.r5 SC-7\",\n                    \"NIST.800-53.r5 SC-7(11)\",\n                    \"NIST.800-53.r5 SC-7(16)\",\n                    \"NIST.800-53.r5 SC-7(20)\",\n                    \"NIST.800-53.r5 SC-7(21)\",\n                    \"NIST.800-53.r5 SC-7(3)\",\n                    \"NIST.800-53.r5 SC-7(4)\",\n                    \"NIST.800-53.r5 SC-7(9)\"\n                ],\n                \"SecurityControlId\": \"EKS.1\",\n                \"AssociatedStandards\": [\n                    {\n                        \"StandardsId\": \"standards/aws-foundational-security-best-practices/v/1.0.0\"\n                    },\n                    {\n                        \"StandardsId\": \"standards/nist-800-53/v/5.0.0\"\n                    }\n                ]\n            },\n            \"WorkflowState\": \"NEW\",\n            \"Workflow\": {\n                \"Status\": \"NEW\"\n            },\n            \"RecordState\": \"ACTIVE\",\n            \"FindingProviderFields\": {\n                \"Severity\": {\n                    \"Label\": \"HIGH\",\n                    \"Original\": \"HIGH\"\n                },\n                \"Types\": [\n                    \"Software and Configuration Checks/Industry and Regulatory Standards\"\n                ]\n            }\n        }\n        \n    ]\n}\n```\n\nTo see any findings generated by Amazon GuardDuty in AWS Security Hub, run the following command:\n\n```bash\naws securityhub get-findings --filters 'ProductName={Value=\"GuardDuty\",Comparison=\"EQUALS\"}'\n```\n\n```json\n{\n    \"Findings\": [\n        {\n            \"SchemaVersion\": \"2018-10-08\",\n            \"Id\": \"arn:aws:guardduty:us-east-1:XXXXXXXXXXX:detector/68b6db88cfef1e59333ecbccd8e816b5/finding/0ec437473c147f649d1437f94d615224\",\n            \"ProductArn\": \"arn:aws:securityhub:us-east-1::product/aws/guardduty\",\n            \"ProductName\": \"GuardDuty\",\n            \"CompanyName\": \"Amazon\",\n            \"Region\": \"us-east-1\",\n            \"GeneratorId\": \"arn:aws:guardduty:us-east-1:XXXXXXXXXXX:detector/68b6db88cfef1e59333ecbccd8e816b5\",\n            \"AwsAccountId\": \"XXXXXXXXXXX\",\n            \"Types\": [\n                \"TTPs/PrivilegeEscalation/PrivilegeEscalation:Kubernetes-PrivilegedContainer\"\n            ],\n            ...\n            \"Severity\": {\n                \"Product\": 5,\n                \"Label\": \"MEDIUM\",\n                \"Normalized\": 50\n            },\n            \"Title\": \"Privileged container with root level access launched on the EKS Cluster.\",\n            \"Description\": \"A privileged container with root level access was launched on EKS Cluster guardduty-blueprint. If this behavior is not expected, it may indicate that your credentials are compromised.\",\n            \"SourceUrl\": \"https://us-east-1.console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?macros=current&fId=0ec437473c147f649d1437f94d615224\",\n            \"ProductFields\": {\n                ...\n            },\n            \"Resources\": [\n                { ... }\n            ],\n            \"WorkflowState\": \"NEW\",\n            \"Workflow\": {\n                \"Status\": \"NEW\"\n            },\n            \"RecordState\": \"ACTIVE\",\n            \"FindingProviderFields\": {\n                \"Severity\": {\n                    \"Label\": \"MEDIUM\"\n                },\n                \"Types\": [\n                    \"TTPs/PrivilegeEscalation/PrivilegeEscalation:Kubernetes-PrivilegedContainer\"\n                ]\n            },\n            \"Sample\": false\n        }\n    ]\n}\n```\n\nIf you deployed the [Amazon GuardDuty Protection EKS Blueprints pattern](https://github.com/aws-samples/cdk-eks-blueprints-patterns/blob/main/docs/patterns/security/guardduty.md) to the same account and region where you enabled AWS Security Hub, you should see Amazon GuardDuty findings like the one above. The sample workload deployed with the [Amazon GuardDuty pattern](guardduty.md) which contains a privileged container is detected by Amazon GuardDuty and generates the `Kubernetes-PrivilegedContainer` finding. Amazon GuardDuty automatically sent this finding to AWS Security Hub where it can be viewed and triaged.\n"
  },
  {
    "path": "docs/patterns/union.md",
    "content": "# Union.ai on EKS Pattern\n\nUnion.ai empowers AI development teams to rapidly ship high-quality code to production by offering optimized performance, resource efficiency, and workflow authoring experience. With Union.ai your team can:\n\n- Run complex AI workloads with performance, scale, and efficiency.\n- Scale out to multiple regions, clusters, and clouds as needed for resource availability, scale, or compliance.\n\nUnion.ai’s modular architecture allows for great flexibility and control. The customer can decide how many clusters to have, their shape, and who has access to what. All communication is encrypted.\n\n<p align=\"center\">\n  <a href=\"https://www.union.ai\">\n    <img alt=\"Union Self-managed Architecture\" src=\"https://www.union.ai/docs/v1/selfmanaged/_static/images/deployment/architecture.svg\" width=\"600\" />\n  </a>\n</p>\n\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine:\n\n- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) (also ensure it is [configured](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html#getting-started-quickstart-new))\n- [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n- [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n- [tsc](https://www.typescriptlang.org/download)\n- [make](https://www.gnu.org/software/make/)\n- uctl\n\n### Installing `uctl`\n\nOn Mac:\n```bash\nbrew tap unionai/homebrew-tap\nbrew install uctl\n```\n\nWith cURL:\n```bash\ncurl -sL https://raw.githubusercontent.com/unionai/uctl/main/install.sh | bash\n```\n\n## Deployment\n\n### Setup Union Credentials\n\nBoth the control plane URL and cluster name will be provided by Union.  Union will also provide authentication information for your account to access the hosted control plane.\n\n```bash\nexport UNION_CONTROL_PLANE_URL=<YOUR_UNION_CONTROL_PLANE_URL>\nexport UNION_CLUSTER_NAME=<YOUR_SELECTED_CLUSTER_NAME>\nexport UNION_ORG_NAME=<YOUR_SELECTED_ORG_NAME>\n\nuctl config init --host=$UNION_CONTROL_PLANE_URL\nuctl selfserve provision-dataplane-resources --clusterName $UNION_CLUSTER_NAME --provider aws\n```\n\nThis command will output the ID, name, and secret used by Union services to communicate with the control plane.\n\n### Create Union Secrets in AWS Secrets Manager\n\n```bash\nexport UNION_SECRET_NAME=union-secret\naws secretsmanager create-secret --name $UNION_SECRET_NAME \\\n  --secret-string \"{\\\"host\\\":\\\"$UNION_CONTROL_PLANE_URL\\\",\\\"clusterName\\\":\\\"$UNION_CLUSTER_NAME\\\",\\\"orgName\\\":\\\"$UNION_ORG_NAME\\\"}\"\n\nexport UNION_CLIENT_SECRET_NAME=union-client-secret\nexport UNION_CLIENT_ID_SECRET_VALUE=<CLUSTERAUTHCLIENTID_FROM_SELFSERVE_COMMAND>\nexport UNION_SECRET_SECRET_VALUE=<CLUSTERAUTHCLIENTSECRET_FROM_SELFSERVE_COMMAND>\n\naws secretsmanager create-secret --name $UNION_CLIENT_SECRET_NAME \\\n  --secret-string \"{\\\"clientId\\\":\\\"$UNION_CLIENT_ID_SECRET_VALUE\\\",\\\"clientSecret\\\":\\\"$UNION_SECRET_SECRET_VALUE\\\"}\"\n```\n\n### Clone the repository:\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\n### Run the following commands:\n\n```sh\nmake deps\nmake build\nmake pattern unionai deploy\n```\n\n### Validation\n\nRun the command:\n```bash\nkubectl get deploy -A\n```\n\nOutput should be:\n```bash\nNAMESPACE     NAME                                                  READY   UP-TO-DATE   AVAILABLE   AGE\nkube-system   blueprints-addon-metrics-server                       1/1     1            1           57d\nkube-system   blueprints-addon-union-dataplane-kube-state-metrics   1/1     1            1           57d\nunionai       executor                                              1/1     1            1           57d\nunionai       flytepropeller                                        1/1     1            1           57d\nunionai       flytepropeller-webhook                                1/1     1            1           57d\nunionai       opencost                                              1/1     1            1           57d\nunionai       prometheus-operator                                   1/1     1            1           57d\nunionai       syncresources                                         1/1     1            1           57d\nunionai       union-operator                                        1/1     1            1           57d\nunionai       union-operator-proxy                                  1/1     1            1           57d\n```\n\nTo validate the cluster has been successfully registered to the Union control plane run the command:\n```bash\nuctl get cluster\n```\n\nOutput should be:\n```bash\n ----------- ------- --------------- -----------\n| NAME      | ORG   | STATE         | HEALTH    |\n ----------- ------- --------------- -----------\n| <cluster> | <org> | STATE_ENABLED | HEALTHY   |\n ----------- ------- --------------- -----------\n1 rows\n```\n\n### 8. Register and run example workflows\n\n```bash\nuctl register examples --project=union-health-monitoring --domain=development\nuctl validate snacks --project=union-health-monitoring --domain=development\n ---------------------- ----------------------------------- ---------- -------------------------------- -------------- ----------- ---------------\n| NAME                 | LAUNCH PLAN NAME                  | VERSION  | STARTED AT                     | ELAPSED TIME | RESULT    | ERROR MESSAGE |\n ---------------------- ----------------------------------- ---------- -------------------------------- -------------- ----------- ---------------\n| alskkhcd6wx5m6cqjlwm | basics.hello_world.hello_world_wf | v0.3.341 | 2025-05-09T18:30:02.968183352Z | 4.452440953s | SUCCEEDED |               |\n ---------------------- ----------------------------------- ---------- -------------------------------- -------------- ----------- ---------------\n1 rows\n```\n"
  },
  {
    "path": "docs/patterns/windows.md",
    "content": "# Windows Nodes on EKS\n\nWe (AWS) have received many requests to add windows node group support from the customers who run their workloads on Windows. Customers want to scale these workloads on Kubernetes alongside their Linux workloads. Amazon EKS supports windows node groups and you can Windows worker node group to an Amazon EKS cluster. This pattern Creates EKS Cluster Control plane with a managed node group running windows node. Please check our AWS doc on [Enabling Windows support for your Amazon EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/windows-support.html) to learn more about considerations, prerequisites on running windows nodes with EKS cluster. Also please refer to this AWS doc to learn about [Amazon EKS optimized Windows AMIs](https://docs.aws.amazon.com/eks/latest/userguide/eks-optimized-windows-ami.html).\n\n### Addons\nNot all of the listed EKS addons support windows. We are currently working on a list of supported addons documentation which will be published [here](https://github.com/aws-quickstart/cdk-eks-blueprints/blob/main/docs/addons/index.md).\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. `make`\n\n## Configuration Options\n\nThe pattern exposes the `WindowsBuilder` construct to build cluster with windows node groups. At the moment, adding windows nodes to the cluster requires at least one linux node group present to deploy core add-ons, such as VPC-CNI and CoreDNS. \n\nThe `WindowsBuilder` provides a set of options, most of which are similar to managed node groups. \n\nIn addition, it provides an attribute `noScheduleForWindowsNodes : true | false`. When set to `true` it will automatically add a `NoSchedule` taint to the Windows nodes. This approach is a safe way to disallow any application that does not provide proper tolerations to be scheduled on Windows nodes. \n\nIn this scenario, in order to schedule a workload (application/add-on) on Windows nodes, customers can apply the following node selectors and tolerations to their deployments:\n\n```yaml\nnodeSelector:\n  kubernetes.io/os: windows\ntolerations:\n  - key: \"os\"\n    operator: \"Equal\"\n    value: \"windows\"\n    effect: \"NoSchedule\"\n```\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\nClone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\nUpdating npm\n\n```sh\nnpm install -g npm@latest\n```\n\nTo view patterns and deploy kubeflow pattern\n\n```sh\nmake list\nnpx cdk bootstrap\nmake pattern windows deploy\n```\n\n## Verify the resources\n\nRun the update-kubeconfig command. You should be able to get the command from the CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n\n```sh\naws eks update-kubeconfig --name windows-eks-blueprint --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/windows-construct-bluepr-windowsconstructbluepri-1OZNO42GH3OCB\n```\n\nLet's verify the resources created from the steps above.\n\n```sh\nkubectl get nodes -o json | jq -r '.items[] | \"Name: \",.metadata.name,\"\\nInstance Type: \",.metadata.labels.\"beta.kubernetes.io/instance-type\",\"\\nOS Type: \",.metadata.labels.\"beta.kubernetes.io/os\",\"\\n\"' # Output shows Windows and Linux Nodes\n```\n\n## Deploy sample windows application\n\nCreate a namespace for the windows app called windows\n\n```sh\nkubectl create ns windows\n```\n\nCreate a yaml file for the app from the configuration below and save it as windows-server-2022.yaml\n\n```yaml\n---\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: windows-server-iis-ltsc2022\n  namespace: windows\nspec:\n  selector:\n    matchLabels:\n      app: windows-server-iis-ltsc2022\n      tier: backend\n      track: stable\n  replicas: 2\n  template:\n    metadata:\n      labels:\n        app: windows-server-iis-ltsc2022\n        tier: backend\n        track: stable\n    spec:\n      containers:\n      - name: windows-server-iis-ltsc2022\n        image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2022\n        ports:\n        - name: http\n          containerPort: 80\n        imagePullPolicy: IfNotPresent\n        command:\n        - powershell.exe\n        - -command\n        - \"Add-WindowsFeature Web-Server; Invoke-WebRequest -UseBasicParsing -Uri 'https://dotnetbinaries.blob.core.windows.net/servicemonitor/2.0.1.6/ServiceMonitor.exe' -OutFile 'C:\\\\ServiceMonitor.exe'; echo '<html><body><br/><br/><H1>Our first pods running on Windows managed node groups! Powered by Windows Server LTSC 2022.<H1></body><html>' > C:\\\\inetpub\\\\wwwroot\\\\iisstart.htm; C:\\\\ServiceMonitor.exe 'w3svc'; \"\n      nodeSelector:\n        kubernetes.io/os: windows\n      tolerations:\n          - key: \"os\"\n            operator: \"Equal\"\n            value: \"windows\"\n            effect: \"NoSchedule\"\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: windows-server-iis-ltsc2022-service\n  namespace: windows\nspec:\n  ports:\n  - port: 80\n    protocol: TCP\n    targetPort: 80\n  selector:\n    app: windows-server-iis-ltsc2022\n    tier: backend\n    track: stable\n  sessionAffinity: None\n  type: LoadBalancer\n```\n\nDeploy the sample app\n\n```sh\nkubectl apply -f windows-server-2022.yaml\n```\n\nVerify the resources created successfully\n\n```sh\nkubectl get -n windows svc,deploy,pods\n```\n\n### Reference\n\nPlease reference our [blog](https://aws.amazon.com/blogs/containers/deploying-amazon-eks-windows-managed-node-groups/#:~:text=2.-,Deploy%20a%20sample%20application,-Now%20that%20ourhttps://aws.amazon.com/blogs/containers/deploying-amazon-eks-windows-managed-node-groups/#:~:text=2.-,Deploy%20a%20sample%20application,-Now%20that%20our) on Deploying Amazon EKS Windows managed node groups to learn more about this topic.\n\n\n## Cleanup\n\nFirst delete the windows app\n\n```sh\nkubectl delete -f windows-server-2022.yaml\nkubectl delete ns windows\n```\n\nTo clean up your EKS Blueprint, run the following command:\n\n```sh\nmake pattern windows destroy\n```\n"
  },
  {
    "path": "docs/patterns/workloads-codecommit.md",
    "content": "# EKS Cluster with ArgoCD and Workloads in private AWS CodeCommit repository\n\n## Objective\n\nThis example shows how to provision an EKS cluster with:\n\n- ArgoCD\n- Workloads deployed by ArgoCD\n- Private AWS CodeCommit repository to store the configurations of workloads\n- Setup to trigger ArgoCD projects sync on git push to AWS CodeCommit repository\n\nPattern source: /lib/workloads-codecommit-construct/index.ts\n\n## Architecture\n\n![Architectural diagram](./images/argocd-cc.png)\n\nTo better understand how ArgoCD works with EKS Blueprints, read the EKS Blueprints ArgoCD [Documentation](https://aws-quickstart.github.io/cdk-eks-blueprints/addons/argo-cd/)\n\n- After a push to AWS CodeCommit repository notification trigger calls AWS Lambda\n- AWS Lambda calls ArgoCD webhook URL to trigger ArgoCD projects sync\n\n## Prerequisites\n\nEnsure that you have installed the following tools on your machine.\n\n1. [aws cli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n2. [kubectl](https://Kubernetes.io/docs/tasks/tools/)\n3. [cdk](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html#getting_started_install)\n4. [npm](https://docs.npmjs.com/cli/v8/commands/npm-install)\n5. [jq](https://jqlang.github.io/jq/)\n6. `make`\n\n## Deploy EKS Cluster with Amazon EKS Blueprints for CDK\n\n1. Clone the repository\n\n```sh\ngit clone https://github.com/aws-samples/cdk-eks-blueprints-patterns.git\ncd cdk-eks-blueprints-patterns\n```\n\n2. Update npm\n\n```sh\nnpm install -g npm@latest\n```\n\n3. View patterns and deploy workloads-codecommit pattern\n\n```sh\nmake list\nnpx cdk bootstrap\nmake pattern workloads-codecommit deploy\n```\n\n## Verify the resources\n\n1. Run the update-kubeconfig command. You should be able to get the command from the CDK output message. More information can be found at https://aws-quickstart.github.io/cdk-eks-blueprints/getting-started/#cluster-access\n\n```sh\naws eks update-kubeconfig --name workloads-codecommit-blueprint --region <your region> --role-arn arn:aws:iam::xxxxxxxxx:role/workloads-codecommit-blue-workloadscodecommitbluepr-VH6YOKWPAt5H\n```\n\n2. Verify the resources created from the steps above.\n\n```bash\n$ kubectl get po -n argocd\nNAME                                          READY   STATUS    RESTARTS   AGE\nblueprints-addon-argocd-application-controller-0                  1/1     Running   0          1h\nblueprints-addon-argocd-applicationset-controller-7b78c7fc5dmkx   1/1     Running   0          1h\nblueprints-addon-argocd-dex-server-6cf94ddc54-p68pl               1/1     Running   0          1h\nblueprints-addon-argocd-notifications-controller-6f6b7d95ckhf6p   1/1     Running   0          1h\nblueprints-addon-argocd-redis-b8dbc7dc6-dvbkr                     1/1     Running   0          1h\nblueprints-addon-argocd-repo-server-66df7f448f-kvwmw              1/1     Running   0          1h\nblueprints-addon-argocd-server-584db5f545-8xp48                   1/1     Running   0          1h\n```\n\n## Get ArgoCD Url and credentials\n\n```bash\nuntil kubectl get svc blueprints-addon-argocd-server -n argocd -o json | jq --raw-output '.status.loadBalancer.ingress[0].hostname' | grep -m 1 \"elb.amazonaws.com\"; do sleep 5 ; done;\nexport ARGOCD_SERVER=`kubectl get svc blueprints-addon-argocd-server -n argocd -o json | jq --raw-output '.status.loadBalancer.ingress[0].hostname'`\nexport CC_REPO_NAME=eks-blueprints-workloads-cc\n\necho \"ArgoCD URL: https://$ARGOCD_SERVER\"\necho \"ArgoCD server user: admin\"\necho \"ArgoCD admin password: $(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=\"{.data.password}\" | base64 -d)\"\n```\n\n## Create notification trigger from AWS CodeCommit push to ArgoCD Sync\n\n```bash\nexport LAMBDA_ARN=$(aws lambda get-function --function-name eks-blueprints-workloads-cc-webhook | jq -r .Configuration.FunctionArn)\n\ncat > trigger.json <<EOF\n[\n  {\n      \"destinationArn\": \"${LAMBDA_ARN}\",\n      \"branches\": [],\n      \"name\": \"${CC_REPO_NAME}-trigger\",\n      \"customData\": \"${ARGOCD_SERVER}\",\n      \"events\": [\n          \"all\"\n      ]\n  }\n]\nEOF\n\naws codecommit put-repository-triggers --repository-name $CC_REPO_NAME --triggers file://trigger.json --no-cli-pager\nrm trigger.json\n```\n\n## Set AWS_REGION\n\n```bash\nexport AWS_REGION=$(aws ec2 describe-availability-zones --output text --query 'AvailabilityZones[0].[RegionName]')\necho $AWS_REGION\n```\n\n## Populate AWS CodeCommit with Blueprint workloads Sample repository\n\n```bash\npushd ..\ngit clone https://github.com/aws-samples/eks-blueprints-workloads.git\ngit clone codecommit::$AWS_REGION://$CC_REPO_NAME\ncd $CC_REPO_NAME\ngit checkout -b main\ncd ..\nrsync -av eks-blueprints-workloads/ $CC_REPO_NAME --exclude .git\ncd $CC_REPO_NAME\ngit add . && git commit -m \"initial commit\" && git push --set-upstream origin main\npopd\n```\n\nArgoCD will receive notification and will start sync.\n\n![ArgoCD sync](./images/argocd-cc-workloads.png)\n\n## Destroy\n\nTo teardown and remove the resources created in this example:\n\n1. Delete \"bootstrap-apps\" project in ArgoCD UI and wait until ArgoCD delete workloads\n\n2. Delete deployed resources\n\n```sh\ncd cdk-eks-blueprints-patterns\nmake pattern workloads-codecommit destroy\n```\n\n3. Delete cloned repositories (`if necessary`)\n\n```sh\npushd ..\nrm -rf eks-blueprints-workloads-cc\nrm -rf eks-blueprints-workloads\npopd\n```\n"
  },
  {
    "path": "jest.config.js",
    "content": "module.exports = {\n  roots: ['<rootDir>/test'],\n  testMatch: ['**/*.test.ts'],\n  transform: {\n    '^.+\\\\.tsx?$': 'ts-jest'\n  }\n};\n"
  },
  {
    "path": "lib/amp-monitoring/index.ts",
    "content": "import { Construct } from 'constructs';\n\n// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as amp from 'aws-cdk-lib/aws-aps';\n\n// Team implementations\nimport * as team from '../teams/multi-account-monitoring';\n\n/**\n * Demonstrates how to leverage more than one node group along with Fargate profiles.\n */\nexport default class AmpMonitoringConstruct {\n    build(scope: Construct, id: string, account?: string, region?: string ) {\n        // Setup platform team\n        const accountID = account ?? process.env.CDK_DEFAULT_ACCOUNT! ;\n        const awsRegion =  region ?? process.env.CDK_DEFAULT_REGION! ;\n \n        const stackID = `${id}-blueprint`;\n        this.create(scope, accountID, awsRegion)\n            .build(scope, stackID);\n    }\n\n    create(scope: Construct, account?: string, region?: string ) {\n        // Setup platform team\n        const accountID = account ?? process.env.CDK_DEFAULT_ACCOUNT! ;\n        const awsRegion =  region ?? process.env.CDK_DEFAULT_REGION! ;\n        const ampWorkspaceName = \"multi-account-monitoring\";\n        const ampPrometheusEndpoint = (blueprints.getNamedResource(ampWorkspaceName) as unknown as amp.CfnWorkspace).attrPrometheusEndpoint;\n\n        return blueprints.EksBlueprint.builder()\n            .account(accountID)\n            .region(awsRegion)\n            .version('auto')\n            .resourceProvider(ampWorkspaceName, new blueprints.CreateAmpProvider(ampWorkspaceName, ampWorkspaceName))\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.CertManagerAddOn,\n                new blueprints.KubeStateMetricsAddOn,\n                new blueprints.PrometheusNodeExporterAddOn,\n                new blueprints.AdotCollectorAddOn,\n                new blueprints.addons.AmpAddOn({\n                    ampPrometheusEndpoint: ampPrometheusEndpoint,\n                }),\n                new blueprints.XrayAdotAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n                new blueprints.SecretsStoreAddOn\n            )\n            .teams(new team.TeamGeordi, new team.CorePlatformTeam);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/argo-config-managent/index.ts",
    "content": ""
  },
  {
    "path": "lib/aws-batch-on-eks-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { BatchEksTeam } from '@aws-quickstart/eks-blueprints';\nimport { PolicyStatement, Effect } from 'aws-cdk-lib/aws-iam';\n\nexport default class BatchOnEKSConstruct {\n    build(scope: Construct, id: string, teams: BatchEksTeam[]) {\n        \n        const batchIamPolicy = new PolicyStatement({\n            effect: Effect.ALLOW,\n            actions: [\n                \"cloudwatch:PutMetricData\",\n                \"ec2:DescribeVolumes\",\n                \"ec2:DescribeTags\",\n                \"logs:PutLogEvents\",\n                \"logs:DescribeLogStreams\",\n                \"logs:DescribeLogGroups\",\n                \"logs:CreateLogStream\",\n                \"logs:CreateLogGroup\"\n            ],\n            resources: [\"*\"],\n        });\n        const stackID = `${id}-blueprint`;\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION!)\n            .addOns(\n                new blueprints.AwsBatchAddOn(), \n                new blueprints.AwsForFluentBitAddOn({\n                    iamPolicies:[batchIamPolicy],\n                    values: {\n                        cloudWatch: {\n                            enabled: true,\n                            region: process.env.CDK_DEFAULT_REGION!,\n                            logGroupName: '/aws/batch/batch-team-a-logs'\n                        },\n                        tolerations: [{\n                            \"key\": \"batch.amazonaws.com/batch-node\", \"operator\": \"Exists\"\n                        }]\n                    }\n                })\n            )\n            .teams(...teams)\n            .version('auto')\n            .build(scope, stackID);\n    }\n}"
  },
  {
    "path": "lib/backstage-construct/backstage-secret-addon.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from 'constructs';\nimport { dependable } from \"@aws-quickstart/eks-blueprints/dist/utils\";\nimport { ISecret } from 'aws-cdk-lib/aws-secretsmanager';\n\nexport interface BackstageSecretAddOnProps {\n    /**\n    * Backstage Namespace\n    */\n    namespace: string,\n\n    /**\n     * The name of the Secret\n     */\n    databaseSecretTargetName: string,\n\n    /**\n     * The name of the Secret from the Resource Provider\n     */\n    databaseSecretResourceName: string\n}\n\nexport class BackstageSecretAddOn implements blueprints.ClusterAddOn {\n    readonly props: BackstageSecretAddOnProps;\n\n    constructor(props: BackstageSecretAddOnProps) {\n        this.props = props;\n    }\n\n    @dependable(blueprints.addons.ExternalsSecretsAddOn.name)\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n\n        const secretStoreName = \"secret-manager-store\";\n        const secretStore = new eks.KubernetesManifest(cluster.stack, \"ClusterSecretStore\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"external-secrets.io/v1beta1\",\n                    kind: \"ClusterSecretStore\",\n                    metadata: {\n                        name: secretStoreName,\n                        namespace: this.props.namespace\n                    },\n                    spec: {\n                        provider: {\n                            aws: {\n                                service: \"SecretsManager\",\n                                region: cluster.stack.region,\n                                auth: {\n                                    jwt: {\n                                        serviceAccountRef: {\n                                            name: \"external-secrets-sa\",\n                                            namespace: \"external-secrets\",\n                                        },\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        });\n\n        const databaseCredentialsSecret: ISecret | undefined = clusterInfo.getResource(this.props.databaseSecretResourceName);\n        if (databaseCredentialsSecret === undefined) {\n            throw new Error(\"Database Secret not found in context\");\n        }\n        const databaseInstanceCredentialsSecretName = databaseCredentialsSecret.secretName;\n        const externalSecret = new eks.KubernetesManifest(cluster.stack, \"BackstageDatabaseExternalSecret\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"external-secrets.io/v1beta1\",\n                    kind: \"ExternalSecret\",\n                    metadata: {\n                        name: \"external-backstage-db-secret\",\n                        namespace: this.props.namespace\n                    },\n                    spec: {\n                        secretStoreRef: {\n                            name: secretStoreName,\n                            kind: \"ClusterSecretStore\",\n                        },\n                        target: {\n                            name: this.props.databaseSecretTargetName,\n                        },\n                        data: [\n                            {\n                                secretKey: \"POSTGRES_PASSWORD\",\n                                remoteRef: {\n                                    key: databaseInstanceCredentialsSecretName,\n                                    property:  \"password\"\n                                }\n                            },\n                            {\n                                secretKey: \"POSTGRES_USER\",\n                                remoteRef: {\n                                    key: databaseInstanceCredentialsSecretName,\n                                    property:  \"username\"\n                                }\n                            },\n                        ],\n                    },\n                },\n            ],\n        });\n\n        externalSecret.node.addDependency(secretStore);\n        return Promise.resolve(secretStore);\n    }\n}\n"
  },
  {
    "path": "lib/backstage-construct/database-credentials.ts",
    "content": "import { Secret,ISecret } from 'aws-cdk-lib/aws-secretsmanager';\nimport { ResourceContext, ResourceProvider } from '@aws-quickstart/eks-blueprints';\n\nexport interface DatabaseInstanceCredentialsProviderProps {\n    /**\n     * The username for the database secret\n     */\n    username: string,\n}\n\nexport class DatabaseInstanceCredentialsProvider implements ResourceProvider<ISecret> {\n    readonly props: DatabaseInstanceCredentialsProviderProps;\n\n    constructor(props: DatabaseInstanceCredentialsProviderProps) {\n        this.props = props;\n    }\n\n    provide(context: ResourceContext): ISecret {\n        return new Secret(context.scope, \"database-secret\", {\n            generateSecretString: {\n                secretStringTemplate: JSON.stringify({\n                    username: this.props.username,\n                }),\n                excludePunctuation: true,\n                includeSpace: false,\n                generateStringKey: \"password\"\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/backstage-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { BackstageSecretAddOn, BackstageSecretAddOnProps } from './backstage-secret-addon';\nimport { DatabaseInstanceCredentialsProvider, DatabaseInstanceCredentialsProviderProps } from './database-credentials';\nimport * as databaseInstanceProvider from './rds-database-instance';\n\nexport class BackstageConstruct extends Construct {\n    constructor(scope: Construct, id: string) {\n        super(scope, id);\n\n        const props = {\n            account: process.env.CDK_DEFAULT_ACCOUNT,\n            region: process.env.CDK_DEFAULT_REGION,\n            namespace: blueprints.utils.valueFromContext(scope, \"backstage.namespace.name\", \"backstage\"),\n            backstageImageRegistry: blueprints.utils.valueFromContext(scope, \"backstage.image.registry.name\", \"youraccount.dkr.ecr.yourregion.amazonaws.com\"),\n            backstageImageRepository: blueprints.utils.valueFromContext(scope, \"backstage.image.repository.name\", \"backstage\"),\n            backstageImageTag: blueprints.utils.valueFromContext(scope, \"backstage.image.tag.name\", \"latest\"),\n            parentDomain: blueprints.utils.valueFromContext(scope, \"backstage.parent.domain.name\", \"example.com\"),\n            backstageLabel: blueprints.utils.valueFromContext(scope, \"backstage.subdomain.label\", \"backstage\"),\n            hostedZoneId: blueprints.utils.valueFromContext(scope, \"backstage.hosted.zone.id\", \"1234\"),\n            certificateResourceName: blueprints.utils.valueFromContext(scope, \"backstage.certificate.resource.name\", \"backstage-certificate\"),\n            databaseResourceName: blueprints.utils.valueFromContext(scope, \"backstage.database.resource.name\", \"backstage-database\"),\n            databaseInstancePort: blueprints.utils.valueFromContext(scope, \"backstage.database.instance.port\", 5432),\n            databaseSecretResourceName: blueprints.utils.valueFromContext(scope, \"backstage.database.secret.resource.name\", \"backstage-database-credentials\"),\n            username: blueprints.utils.valueFromContext(scope, \"backstage.database.username\", \"postgres\"),\n            databaseSecretTargetName: blueprints.utils.valueFromContext(scope, \"backstage.database.secret.target.name\", \"backstage-database-secret\"),\n        };\n\n        const subdomain = props.backstageLabel+\".\"+props.parentDomain;\n    \n        const databaseInstanceCredentialsProviderProps = {\n            username: props.username\n        } as DatabaseInstanceCredentialsProviderProps;\n\n        const databaseInstanceProps = {\n            vpcResourceName: blueprints.GlobalResources.Vpc,\n            databaseInstancePort: props.databaseInstancePort,\n            databaseSecretResourceName: props.databaseSecretResourceName\n        } as databaseInstanceProvider.DatabaseInstanceProviderProps;\n\n        const backstageSecretAddOnProps = {\n            namespace: props.namespace,\n            databaseSecretResourceName: props.databaseSecretResourceName,\n            databaseSecretTargetName: props.databaseSecretTargetName\n        } as BackstageSecretAddOnProps;\n\n        const backstageAddOnProps = {\n            namespace: props.namespace,\n            subdomain: subdomain,\n            certificateResourceName: props.certificateResourceName,\n            imageRegistry: props.backstageImageRegistry,\n            imageRepository: props.backstageImageRepository,\n            imageTag: props.backstageImageTag,\n            databaseResourceName: props.databaseResourceName,\n            databaseSecretTargetName: props.databaseSecretTargetName\n        } as blueprints.BackstageAddOnProps;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.CalicoOperatorAddOn(),\n            new blueprints.ClusterAutoScalerAddOn(),\n            new blueprints.AwsLoadBalancerControllerAddOn(),\n            new blueprints.VpcCniAddOn(),\n            new blueprints.CoreDnsAddOn(),\n            new blueprints.KubeProxyAddOn(),\n            new blueprints.ExternalDnsAddOn({\n                hostedZoneResources: [blueprints.GlobalResources.HostedZone]\n            }),\n            new blueprints.addons.ExternalsSecretsAddOn({}),\n            new blueprints.addons.SSMAgentAddOn(),\n            new BackstageSecretAddOn(backstageSecretAddOnProps),\n            new blueprints.BackstageAddOn(backstageAddOnProps)\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(props.account)\n            .region(props.region)\n            .resourceProvider(blueprints.GlobalResources.Vpc, new blueprints.VpcProvider())\n            .resourceProvider(blueprints.GlobalResources.HostedZone, new blueprints.ImportHostedZoneProvider(props.hostedZoneId, props.parentDomain))\n            .resourceProvider(props.certificateResourceName, new blueprints.CreateCertificateProvider(\"elb-certificate\", subdomain, blueprints.GlobalResources.HostedZone))\n            .resourceProvider(props.databaseSecretResourceName, new DatabaseInstanceCredentialsProvider(databaseInstanceCredentialsProviderProps))\n            .resourceProvider(props.databaseResourceName, new databaseInstanceProvider.DatabaseInstanceProvider(databaseInstanceProps))\n            .addOns(...addOns)\n            .version('auto')\n            .teams()\n            .build(scope, props.backstageLabel+\"-blueprint\");\n    }\n}\n"
  },
  {
    "path": "lib/backstage-construct/rds-database-instance.ts",
    "content": "import * as rds from 'aws-cdk-lib/aws-rds';\nimport { ISecret } from 'aws-cdk-lib/aws-secretsmanager';\nimport { IVpc, Peer, SecurityGroup, SubnetType, Port } from 'aws-cdk-lib/aws-ec2';\nimport { ResourceContext, ResourceProvider } from '@aws-quickstart/eks-blueprints';\n\nexport interface DatabaseInstanceProviderProps {\n    /**\n     * Name of the VPC registered as a resource\n     */\n    vpcResourceName: string,\n\n    /**\n     * Port to be used by the database\n     */\n    databaseInstancePort: number,\n\n    /**\n     * The name of the Secret registered as a resource\n     */\n    databaseSecretResourceName: string\n}\n\nexport class DatabaseInstanceProvider implements ResourceProvider<rds.IDatabaseInstance> {\n    readonly props: DatabaseInstanceProviderProps;\n\n    constructor(props: DatabaseInstanceProviderProps) {\n        this.props = props;\n    }\n\n    provide(context: ResourceContext): rds.IDatabaseInstance {\n        const id = context.scope.node.id;\n\n        const databaseCredentialsSecret = context.get<ISecret>(this.props.databaseSecretResourceName);\n        if (databaseCredentialsSecret === undefined) {\n            throw new Error(\"Database Secret not found in context\");\n        }\n\n        const vpc = context.get<IVpc>(this.props.vpcResourceName);\n        if (vpc === undefined) {\n            throw new Error(\"VPC not found in context\");\n        }\n\n        const dbSecurityGroup = new SecurityGroup(context.scope, id+\"-security-group\", {\n            vpc: vpc\n        });\n        \n        dbSecurityGroup.addIngressRule(Peer.ipv4(vpc.vpcCidrBlock), Port.tcp(this.props.databaseInstancePort), \"Connect from within VPC\");\n\n        const rdsConfig: rds.DatabaseInstanceProps = {\n            engine: rds.DatabaseInstanceEngine.POSTGRES,\n            vpc,\n            vpcSubnets: {\n                subnetType: SubnetType.PRIVATE_WITH_EGRESS,\n            },\n            securityGroups: [dbSecurityGroup],\n            credentials: rds.Credentials.fromSecret(databaseCredentialsSecret),\n        };\n      \n        return new rds.DatabaseInstance(context.scope, id+\"-database-instance\", rdsConfig);\n    }\n}\n"
  },
  {
    "path": "lib/bottlerocket-construct/index.ts",
    "content": "import * as eks from 'aws-cdk-lib/aws-eks';\nimport { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as team from '../teams';\n\n/**\n * Bottlerocket pattern shows how to specify the OS for the node group\n * and leverage container-optimized Bottlerocket OS: https://aws.amazon.com/bottlerocket/\n */\nexport default class BottlerocketConstruct {\n    \n    build(scope: Construct, id: string) {\n \n        const stackID = `${id}-blueprint`;\n        const accountID = process.env.CDK_DEFAULT_ACCOUNT!;\n        const platformTeam = new team.TeamPlatform(accountID);\n \n        const clusterProvider = new blueprints.MngClusterProvider({\n            version: eks.KubernetesVersion.V1_25,\n            amiType: eks.NodegroupAmiType.BOTTLEROCKET_X86_64\n        });\n        \n        blueprints.EksBlueprint.builder()\n            .account(accountID)\n            .region('us-east-1')\n            .clusterProvider(clusterProvider)\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.CertManagerAddOn,\n                new blueprints.AdotCollectorAddOn,\n                new blueprints.AppMeshAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.ArgoCDAddOn,\n                new blueprints.CalicoOperatorAddOn,\n                new blueprints.MetricsServerAddOn,\n                new blueprints.CloudWatchAdotAddOn,\n                new blueprints.SecretsStoreAddOn\n            )\n            .teams(platformTeam)\n            .build(scope, stackID);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/cloudwatch-monitoring/index.ts",
    "content": "import { Construct } from 'constructs';\n\n// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { cloudWatchDeploymentMode } from '@aws-quickstart/eks-blueprints';\n\n// Team implementation\nimport * as team from '../teams/multi-account-monitoring';\n\n/**\n * Demonstration of how to use CloudWatch Adot add-on.\n */\nexport default class CloudWatchMonitoringConstruct {\n    \n    build(scope: Construct, id: string, account?: string, region?: string ) {\n        // Setup platform team\n        const accountID = account ?? process.env.CDK_DEFAULT_ACCOUNT! ;\n        const awsRegion =  region ?? process.env.CDK_DEFAULT_REGION! ;\n \n        const stackID = `${id}-blueprint`;\n        this.create(scope, accountID, awsRegion)\n            .build(scope, stackID);\n    }\n\n    create(scope: Construct, account?: string, region?: string ) {\n        // Setup platform team\n        const accountID = account ?? process.env.CDK_DEFAULT_ACCOUNT! ;\n        const awsRegion =  region ?? process.env.CDK_DEFAULT_REGION! ;\n\n        const cloudWatchAdotAddOn = new blueprints.addons.CloudWatchAdotAddOn({\n            deploymentMode: cloudWatchDeploymentMode.DEPLOYMENT,\n            namespace: 'default',\n            name: 'adot-collector-cloudwatch',\n            metricsNameSelectors: ['apiserver_request_.*', 'container_memory_.*', 'container_threads', 'otelcol_process_.*', 'ho11y*'],\n            podLabelRegex: 'frontend|downstream(.*)' \n        });\n\n        return blueprints.EksBlueprint.builder()\n            .account(accountID)\n            .region(awsRegion)\n            .version('auto')\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.CertManagerAddOn,\n                new blueprints.KubeStateMetricsAddOn,\n                new blueprints.PrometheusNodeExporterAddOn,\n                new blueprints.AdotCollectorAddOn,\n                cloudWatchAdotAddOn,\n                new blueprints.XrayAdotAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n                new blueprints.SecretsStoreAddOn\n            )\n            .teams(new team.TeamGeordi, new team.CorePlatformTeam);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/common/construct-utils.ts",
    "content": "import { utils } from \"@aws-quickstart/eks-blueprints\";\nimport { HelmAddOn } from '@aws-quickstart/eks-blueprints';\nimport * as cdk from 'aws-cdk-lib';\n\nexport const logger = utils.logger;\n\nexport function errorHandler(app: cdk.App, message: string, error?: Error) {\n    logger.info(message);\n    if(error){\n        logger.error(error.name, error.message, error.stack);\n    }\n    new EmptyStack(app);\n}\n\nexport function configureApp(logLevel? : number): cdk.App {\n    logger.settings.minLevel = logLevel ?? 2; // debug., 3 info\n    logger.settings.hideLogPositionForProduction = true;\n    utils.userLog.info(\"=== Run make compile before each run, if any code modification was made. === \\n\\n\");\n\n    const account = process.env.CDK_DEFAULT_ACCOUNT!;\n    const region = process.env.CDK_DEFAULT_REGION!;\n    \n    HelmAddOn.validateHelmVersions = true;\n    \n    return new cdk.App({context: { account, region }});\n}\n\nexport async function prevalidateSecrets(pattern: string, region?: string, ...secrets: string[]) {\n    for(let secret of secrets) {\n        try {\n            await utils.validateSecret(secret, region ?? process.env.CDK_DEFAULT_REGION!);\n        }\n        catch(error) {\n            throw new Error(`${secret} secret must be setup for the ${pattern} pattern to work`);\n        }\n    }\n}\n\nexport function getJsonSecret(secretString: string, key?: string): string {\n    const parsed = JSON.parse(secretString);\n    return key ? parsed[key] : parsed;\n}\n\nexport class EmptyStack extends cdk.Stack {\n    constructor(scope: cdk.App, ...message: string[]) {\n        super(scope, \"empty-error-stack\");\n        if(message) {\n            message.forEach(m => logger.info(m));\n        }\n    }\n}\n\n\n"
  },
  {
    "path": "lib/common/default-main.ts",
    "content": "import { EmptyStack, configureApp } from \"./construct-utils\";\n\nconst app = configureApp();\n\nnew EmptyStack(app, \"To work with patterns use:\",\n    \"$ make list # to list all patterns\",\n    \"$ make pattern <pattern-name> <list | deploy | synth | destroy>\",\n    \"Example:\",\n    \"$ make pattern fargate deploy\");"
  },
  {
    "path": "lib/crossplane-argocd-gitops/custom-addons/crossplane-helm-provider-addon.ts",
    "content": "import 'source-map-support/register';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from 'constructs';\nimport { dependable } from '@aws-quickstart/eks-blueprints/dist/utils';\nimport { UpboundCrossplaneAddOn } from './upbound-crossplane-addon';\n\nexport class CrossplaneHelmProviderAddon implements blueprints.ClusterAddOn {\n    id?: string | undefined;\n    readonly helmProviderVersion: string;\n    constructor(helmProviderVersion: string) {\n        this.helmProviderVersion = helmProviderVersion;\n    }\n\n    @dependable(UpboundCrossplaneAddOn.name)\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n\n        const roleBinding = {\n            apiVersion: \"rbac.authorization.k8s.io/v1\",\n            kind: \"ClusterRoleBinding\",\n            metadata: { \n                name: \"helm-provider\"             \n            },\n            subjects: [\n                {\n                    kind: \"ServiceAccount\",\n                    name: \"helm-provider\",\n                    namespace: \"upbound-system\"\n                }\n            ],\n            roleRef: {\n                kind: \"ClusterRole\",\n                name: \"cluster-admin\",\n                apiGroup: \"rbac.authorization.k8s.io\"\n            }\n        };\n\n        const runtimeConfig = {\n            apiVersion: \"pkg.crossplane.io/v1beta1\",\n            kind: \"DeploymentRuntimeConfig\",\n            metadata: { \n                name: \"helm-runtime-config\"               \n            },\n            spec: {\n                deploymentTemplate: {\n                    spec: { \n                        replicas: 1,\n                        selector: {},\n                        template: {}\n                    }\n                },\n                serviceAccountTemplate: { \n                    metadata: { name: \"helm-provider\" } \n                }\n            }\n        };\n\n        const provider = {\n            apiVersion: \"pkg.crossplane.io/v1\",\n            kind: \"Provider\",\n            metadata: { name: \"helm-provider\" },\n            spec: {\n                package: 'xpkg.upbound.io/crossplane-contrib/provider-helm:'+this.helmProviderVersion,\n                runtimeConfigRef: {\n                    name: \"helm-runtime-config\"\n                }\n            }\n        };\n\n        const runtimeHelmConfig = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"runtimeHelmConfig\", {\n            cluster: cluster,\n            manifest: [roleBinding, runtimeConfig]\n        });\n\n        const awsHelmProvider = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"providerHelmResource\", {\n            cluster: cluster,\n            manifest: [provider]\n        });\n\n        awsHelmProvider.node.addDependency(runtimeHelmConfig);\n        return Promise.resolve(runtimeHelmConfig);\n    }\n}\n"
  },
  {
    "path": "lib/crossplane-argocd-gitops/custom-addons/crossplane-k8s-provider-addon.ts",
    "content": "import 'source-map-support/register';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from 'constructs';\nimport { dependable } from '@aws-quickstart/eks-blueprints/dist/utils';\nimport { UpboundCrossplaneAddOn } from './upbound-crossplane-addon';\n\nexport class CrossplaneK8sProviderAddon implements blueprints.ClusterAddOn {\n    id?: string | undefined;\n    readonly k8sProviderVersion: string;\n    constructor(k8sProviderVersion: string) {\n        this.k8sProviderVersion = k8sProviderVersion;\n    }\n\n    @dependable(UpboundCrossplaneAddOn.name)\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n\n        const roleBinding = {\n            apiVersion: \"rbac.authorization.k8s.io/v1\",\n            kind: \"ClusterRoleBinding\",\n            metadata: { name: \"kubernetes-provider\" },\n            subjects: [\n                {\n                    kind: \"ServiceAccount\",\n                    name: \"kubernetes-provider\",\n                    namespace: \"upbound-system\"\n                }\n            ],\n            roleRef: {\n                kind: \"ClusterRole\",\n                name: \"cluster-admin\",\n                apiGroup: \"rbac.authorization.k8s.io\"\n            }\n        };\n\n        const runtimeConfig = {\n            apiVersion: \"pkg.crossplane.io/v1beta1\",\n            kind: \"DeploymentRuntimeConfig\",\n            metadata: { \n                name: \"kubernetes-runtime-config\"              \n            },\n            spec: {\n                deploymentTemplate: {\n                    spec: { \n                        replicas: 1,\n                        selector: {},\n                        template: {}\n                    }\n                },\n                serviceAccountTemplate: { \n                    metadata: { name: \"kubernetes-provider\" } \n                }\n            }\n        };\n\n        const providerK8sResource = {\n            apiVersion: \"pkg.crossplane.io/v1\",\n            kind: \"Provider\",\n            metadata: { name: \"kubernetes-provider\" },\n            spec: {\n                package: 'xpkg.upbound.io/crossplane-contrib/provider-kubernetes:'+this.k8sProviderVersion,\n                runtimeConfigRef: {\n                    name: \"kubernetes-runtime-config\"\n                }\n            }\n        };\n\n        const runtimeK8sConfig = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"runtimeK8sConfig\", {\n            cluster: cluster,\n            manifest: [roleBinding, runtimeConfig]\n        });\n\n        const awsK8sProvider = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"awsK8sProvider\", {\n            cluster: cluster,\n            manifest: [providerK8sResource]\n        });\n\n        awsK8sProvider.node.addDependency(runtimeK8sConfig);\n\n        return Promise.resolve(runtimeK8sConfig);\n    }\n}\n"
  },
  {
    "path": "lib/crossplane-argocd-gitops/custom-addons/custom-iam-role-creator.ts",
    "content": "\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport { IManagedPolicy } from 'aws-cdk-lib/aws-iam';\n\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n\nexport class CreateNamedRoleProvider implements blueprints.ResourceProvider<iam.Role> {\n\n    /**\n     * Constructor to create role provider.\n     * @param roleId role id\n     * @param assumedBy @example  new iam.ServicePrincipal('ec2.amazonaws.com') \n     * @param policies \n     */\n    constructor(private roleId: string, private roleName: string, private assumedBy: iam.IPrincipal, private policies?: IManagedPolicy[]){}\n\n    provide(context: blueprints.ResourceContext): iam.Role {\n        return new iam.Role(context.scope, this.roleId, {\n            assumedBy: this.assumedBy,\n            managedPolicies: this.policies,\n            roleName: this.roleName\n        });\n    }\n}\n"
  },
  {
    "path": "lib/crossplane-argocd-gitops/custom-addons/upbound-crossplane-addon.ts",
    "content": "import 'source-map-support/register';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { Construct } from 'constructs';\nimport { Values } from \"@aws-quickstart/eks-blueprints/dist/spi\";\nimport { merge } from \"ts-deepmerge\";\nimport { createNamespace } from '@aws-quickstart/eks-blueprints/dist/utils';\nimport { Policy, PolicyDocument} from 'aws-cdk-lib/aws-iam';\nimport * as cdk from 'aws-cdk-lib';\n\n/**\n * User provided options for the Helm Chart\n */\nexport interface UpboundCrossplaneAddOnProps extends blueprints.HelmAddOnUserProps {\n    /**\n     * To Create Namespace using CDK\n     */\n    createNamespace?: boolean;\n  }\n\nconst defaultProps: blueprints.HelmAddOnProps = {\n    name: 'uxp',\n    release: 'blueprints-addon-uxp',\n    namespace: 'upbound-system',\n    chart: 'universal-crossplane',\n    version: '1.14.5-up.1',\n    repository: 'https://charts.upbound.io/stable',\n    values: {},\n};\n\nexport class UpboundCrossplaneAddOn extends blueprints.HelmAddOn {\n\n    readonly options: UpboundCrossplaneAddOnProps;\n\n    constructor( props?: UpboundCrossplaneAddOnProps) {\n        super({...defaultProps, ...props});\n\n        this.options = this.props as UpboundCrossplaneAddOnProps;\n    }\n\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n\n        // Create the `upbound-system` namespace.\n        const ns = createNamespace(this.options.namespace!, cluster, true);\n\n        // Create the CrossPlane AWS Provider IRSA.\n        const serviceAccountName = \"provider-aws\";\n        const sa = cluster.addServiceAccount(serviceAccountName, {\n            name: serviceAccountName,\n            namespace: this.options.namespace!,\n        });\n        sa.node.addDependency(ns);\n        sa.role.attachInlinePolicy(new Policy(cluster.stack, 'eks-connect-policy',  {\n            document: PolicyDocument.fromJson({\n                \"Version\": \"2012-10-17\",\n                \"Statement\": [\n                    {\n                        \"Effect\": \"Allow\",\n                        \"Action\": [\"sts:AssumeRole\"],\n                        \"Resource\": `arn:aws:iam::${cluster.stack.account}:role/eks-workload-connector-role`\n                    },\n                    {\n                        \"Effect\": \"Allow\",\n                        \"Action\": [\"eks:*\"],\n                        \"Resource\": `*`\n                    }                \n                ]\n            })}));\n\n        clusterInfo.addAddOnContext(UpboundCrossplaneAddOn.name, {\n            arn: sa.role.roleArn\n        });\n\n        new cdk.CfnOutput(cluster.stack, 'providerawssaiamrole',\n            {\n                value: sa.role.roleArn,\n                description: 'provider AWS IAM role',\n                exportName : 'providerawssaiamrole'\n            });\n\n        let values: Values = this.options.values ?? {};\n        values = merge(values, values);\n\n        const chart = this.addHelmChart(clusterInfo, values, false, true);\n        chart.node.addDependency(sa);\n        return Promise.resolve(chart);\n    }\n}\n"
  },
  {
    "path": "lib/crossplane-argocd-gitops/custom-addons/upbound-crossplane-eks-provider-addon.ts",
    "content": "import 'source-map-support/register';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from 'constructs';\nimport { dependable } from '@aws-quickstart/eks-blueprints/dist/utils';\nimport { UpboundCrossplaneAddOn } from './upbound-crossplane-addon';\nimport { Policy, PolicyDocument} from 'aws-cdk-lib/aws-iam';\n\nexport class UpboundCrossplaneEKSProviderAddOn implements blueprints.ClusterAddOn {\n    id?: string | undefined;\n    readonly UpboundEKSProviderVersion: string;\n    constructor(UpboundEKSProviderVersion: string) {\n        this.UpboundEKSProviderVersion = UpboundEKSProviderVersion;\n    }    \n    @dependable(UpboundCrossplaneAddOn.name)\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n\n        // Create the CrossPlane EKS Provider IRSA.\n        const serviceAccountName = \"provider-aws-eks\";\n        const upboundNamespace = \"upbound-system\";\n        const sa = cluster.addServiceAccount(serviceAccountName, {\n            name: serviceAccountName,\n            namespace: upboundNamespace,\n        });\n        sa.role.attachInlinePolicy(new Policy(cluster.stack, 'eks-workload-connector-policy',  {\n            document: PolicyDocument.fromJson({\n                \"Version\": \"2012-10-17\",\n                \"Statement\": [\n                    {\n                        \"Effect\": \"Allow\",\n                        \"Action\": [\"sts:AssumeRole\"],\n                        \"Resource\": `arn:aws:iam::${cluster.stack.account}:role/eks-workload-connector-role`\n                    },\n                    {\n                        \"Effect\": \"Allow\",\n                        \"Action\": [\"eks:*\"],\n                        \"Resource\": `*`\n                    }                \n                ]\n            })}));\n\n        // clusterInfo.addAddOnContext(UpboundCrossplaneEKSProviderAddOn.name, {\n        //     arn: sa.role.roleArn\n        // });\n\n        const runtimeConfig = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"runtimeConfig\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"pkg.crossplane.io/v1beta1\",\n                    kind: \"DeploymentRuntimeConfig\",\n                    metadata: { \n                        name: \"aws-eks-runtime-config\"               \n                    },\n                    spec: {\n                        deploymentTemplate: {\n                            spec: { \n                                replicas: 1,\n                                selector: {},\n                                template: {}\n                            }\n                        },\n                        serviceAccountTemplate: { \n                            metadata: { name: \"provider-aws-eks\" } \n                        }\n                    }\n                },\n            ],\n        });\n\n        const awsEksProvider = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"EKSProvider\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"pkg.crossplane.io/v1\",\n                    kind: \"Provider\",\n                    metadata: {\n                        name: \"provider-aws-eks\",\n                    },\n                    spec: {\n                        package: 'xpkg.upbound.io/upbound/provider-aws-eks:'+this.UpboundEKSProviderVersion,\n                        runtimeConfigRef: {\n                            name: \"aws-eks-runtime-config\"\n                        }                        \n                    },\n                },\n            ],\n        });\n        \n        // runtimeConfig.node.addDependency(sa);\n        awsEksProvider.node.addDependency(runtimeConfig);\n        return Promise.resolve(runtimeConfig);\n    }\n}"
  },
  {
    "path": "lib/crossplane-argocd-gitops/management-cluster-builder.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from 'aws-cdk-lib/aws-eks';\nimport { ObservabilityBuilder } from '@aws-quickstart/eks-blueprints';\nimport { UpboundCrossplaneAddOn } from './custom-addons/upbound-crossplane-addon';\nimport  { UpboundCrossplaneEKSProviderAddOn } from './custom-addons/upbound-crossplane-eks-provider-addon';\nimport  { CrossplaneK8sProviderAddon } from './custom-addons/crossplane-k8s-provider-addon';\nimport  { CrossplaneHelmProviderAddon } from './custom-addons/crossplane-helm-provider-addon';\n// import { TeamSpoc } from './custom-addons/secret-provider-secret';\n\n\nconst gitUrl = 'https://github.com/ajpaws/eks-blueprints-workloads.git';\nconst k8sProviderVersion = 'v0.13.0';\nconst UpboundEKSProviderVersion = 'v1.1.0';\nconst helmProviderVersion = 'v0.19.0';\n\n\nexport default class ManagementClusterBuilder {\n    readonly account: string;\n    readonly region: string;\n\n    constructor(account: string,region: string) {\n        this.account = account;\n        this.region = region;\n    }\n\n    create(scope: Construct, id: string, mngProps: blueprints.MngClusterProviderProps) {\n        blueprints.HelmAddOn.validateHelmVersions = false;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.addons.ExternalsSecretsAddOn,\n            new UpboundCrossplaneAddOn,\n            new UpboundCrossplaneEKSProviderAddOn(UpboundEKSProviderVersion),\n            new CrossplaneK8sProviderAddon(k8sProviderVersion),\n            new CrossplaneHelmProviderAddon(helmProviderVersion),\n            new blueprints.SecretsStoreAddOn,\n            new blueprints.ArgoCDAddOn({\n                bootstrapRepo: {\n                    repoUrl: gitUrl,\n                    path: `./crossplane-argocd-gitops/envs/dev`,\n                    targetRevision: 'main'\n                },\n                bootstrapValues: {\n                    clusterA: {\n                        clusterName: 'workload-amd-1-29-blueprint'\n                    },\n                    clusterB: {\n                        clusterName: 'workload-arm-1-29-blueprint'\n                    },\n                    common: {\n                        providerConfigAWSName: 'common-provider-config-aws',\n                        eksConnectorRoleName: 'eks-workload-connector-role',\n                        accountId: `${process.env.CDK_DEFAULT_ACCOUNT}`, \n                        region: `${process.env.CDK_DEFAULT_REGION}`,\n                        crossplaneNamespace: 'upbound-system'                     \n                    }                \n                },\n            }),\n        ];\n\n        const clusterProvider = new blueprints.MngClusterProvider({...mngProps,\n            clusterName:id\n        });\n\n        return ObservabilityBuilder.builder()\n            .clusterProvider(clusterProvider)\n            .version(eks.KubernetesVersion.V1_29)\n            .enableNativePatternAddOns()\n            .enableControlPlaneLogging()\n            .addOns(...addOns);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/crossplane-argocd-gitops/multi-cluster-options.ts",
    "content": "import {CapacityType, KubernetesVersion} from \"aws-cdk-lib/aws-eks\";\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport * as eks from \"aws-cdk-lib/aws-eks\";\n\nexport const K8S_VERSIONS_PROD :  KubernetesVersion[] = [KubernetesVersion.V1_27, KubernetesVersion.V1_28, KubernetesVersion.V1_29]; \n\nexport const K8S_VERSIONS_DEV :  KubernetesVersion[] = [ KubernetesVersion.of(\"1.29\")];\n\n\nexport interface MultiClusterOptions {\n    readonly account: string;\n    readonly region: string;\n    minSize?: number;\n    maxSize?: number;\n    desiredSize?: number;\n    gitHubSecret?: string;\n    nodeGroupCapacityType: CapacityType;\n    instanceTypes?: ec2.InstanceType[];\n    amiType?: eks.NodegroupAmiType;\n    k8sVersions: KubernetesVersion[];\n}\n"
  },
  {
    "path": "lib/crossplane-argocd-gitops/multi-cluster-pipeline.ts",
    "content": "import { Construct } from \"constructs\";\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport {K8S_VERSIONS_DEV, MultiClusterOptions} from \"./multi-cluster-options\";\nimport {CapacityType, KubernetesVersion} from \"aws-cdk-lib/aws-eks\";\nimport {NodegroupAmiType} from \"aws-cdk-lib/aws-eks\";\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport ManagementClusterBuilder from \"./management-cluster-builder\";\nimport {GenericClusterProvider, LookupRoleProvider} from \"@aws-quickstart/eks-blueprints\";\nimport {IRole} from \"aws-cdk-lib/aws-iam\";\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport {ManagedNodeGroup} from \"@aws-quickstart/eks-blueprints/dist/cluster-providers/types\";\nimport { prevalidateSecrets } from \"../common/construct-utils\";\nimport {CreateNamedRoleProvider} from \"./custom-addons/custom-iam-role-creator\";\n\n// const account = process.env.CDK_DEFAULT_ACCOUNT ?? \"\";\nconst account = process.env.CDK_DEFAULT_ACCOUNT!;\n//const region = process.env.CDK_DEFAULT_REGION ?? \"us-east-1\";\nconst region = process.env.CDK_DEFAULT_REGION!;\nconst minSize  =  parseInt(process.env.NODEGROUP_MIN ?? \"1\");\nconst maxSize  =  parseInt(process.env.NODEGROUP_MAX ?? \"3\");\nconst desiredSize  =  parseInt(process.env.NODEGROUP_DESIRED ?? \"1\");\nconst gitHubSecret = process.env.GITHUB_SECRET ?? \"cdk_blueprints_github_secret\";\n\nconst props : MultiClusterOptions = {\n    account,\n    region,\n    minSize,\n    maxSize,\n    desiredSize,\n    gitHubSecret,\n    nodeGroupCapacityType: CapacityType.ON_DEMAND,\n    k8sVersions: K8S_VERSIONS_DEV // K8S_VERSIONS_PROD for full deploy\n};\n\n\nconst mngProps: blueprints.MngClusterProviderProps = {\n    version: KubernetesVersion.V1_29,\n    instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.XLARGE2)],\n    amiType: eks.NodegroupAmiType.AL2_X86_64,\n    desiredSize: 2,\n    maxSize: 3,\n};\n\nconsole.info(\"Running CDK with id: crossplane-argocd-gitops\" );\nconsole.info(\"Running CDK with: \" + JSON.stringify(props));\n\nexport default class MultiClusterPipelineConstruct {\n    async buildAsync(scope: Construct, id: string) {\n        const k8sVersions = props.k8sVersions ?? K8S_VERSIONS_DEV;\n        const region :string = props.region;\n        const account : string = props.account;\n\n        const gitProps = {\n            owner :'aws-samples',\n            secretName : props.gitHubSecret ?? 'cdk_blueprints_github_secret',\n            repoName : 'cdk-eks-blueprints-patterns',\n            revision : 'main' // use this to target a certain branch for deployment\n        };\n\n\n        await prevalidateSecrets(gitProps.secretName, region);\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.ExternalsSecretsAddOn({\n                namespace: \"external-secrets\",\n                values: { webhook: { port: 9443 } }\n            })\n        ];\n\n        const clusterProps: blueprints.MngClusterProviderProps = {\n            minSize: props.minSize,\n            maxSize: props.maxSize,\n            desiredSize: props.desiredSize,\n            nodeGroupCapacityType: props.nodeGroupCapacityType,\n        };\n\n        const stages : blueprints.StackStage[] = [];\n        const vpcProvider= new blueprints.VpcProvider();\n\n        const eksConnectorRole = new CreateNamedRoleProvider(\"eks-workload-connector-role\", \"eks-workload-connector-role\", new iam.AccountPrincipal(account),\n            [\n                iam.ManagedPolicy.fromAwsManagedPolicyName(\"AdministratorAccess\")\n            ]);\n\n        const baseBlueprintARM = blueprints.EksBlueprint.builder()\n            .resourceProvider(blueprints.GlobalResources.Vpc, vpcProvider)\n            .resourceProvider('eks-workload-connector-role',  eksConnectorRole)                  \n            .account(account)\n            .addOns(...addOns)\n            .useDefaultSecretEncryption(true);\n\n        const baseBlueprintAMD = blueprints.EksBlueprint.builder()\n            .resourceProvider(blueprints.GlobalResources.Vpc, vpcProvider)\n            .resourceProvider('eks-workload-connector-role',  new LookupRoleProvider('eks-workload-connector-role'))              \n            .account(account)\n            .addOns(...addOns)\n            .useDefaultSecretEncryption(true);\n\n            \n        const mgmtCluster = new ManagementClusterBuilder(account, region)\n            .create(scope, 'eks-mgmt-cluster', mngProps)\n            .account(account)\n            .region(region)\n            .resourceProvider(blueprints.GlobalResources.Vpc, vpcProvider);\n\n        const mgmtStage = [{id: `mgmt-cluster-stage` , stackBuilder: mgmtCluster}];\n\n        for(const k8sVersion of k8sVersions) {\n            baseBlueprintARM.version(k8sVersion);\n\n            const blueprintAMD = baseBlueprintAMD\n                .clusterProvider(\n                    new GenericClusterProvider( {\n                        version: k8sVersion,\n                        mastersRole: blueprints.getNamedResource('eks-workload-connector-role') as IRole,                      \n                        managedNodeGroups : [addManagedNodeGroup( 'amd-tst-ng',{...clusterProps,\n                            amiType : NodegroupAmiType.AL2_X86_64,\n                            instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.XLARGE)]})]\n                    })\n                );\n            stages.push({\n                id: `workload-amd-` + k8sVersion.version.replace(\".\", \"-\"),\n                stackBuilder : blueprintAMD.clone(props.region).id(`amd-` + k8sVersion.version.replace(\".\", \"-\"))\n            });\n\n            const blueprintARM = baseBlueprintARM\n                .clusterProvider(\n                    new GenericClusterProvider( {\n                        version: k8sVersion,\n                        mastersRole: blueprints.getNamedResource('eks-workload-connector-role') as IRole,\n                        managedNodeGroups : [addManagedNodeGroup('arm-tst-ng',{...clusterProps,\n                            amiType : NodegroupAmiType.AL2_ARM_64,\n                            instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.M7G, ec2.InstanceSize.XLARGE)]})]\n                    })\n                );\n            stages.push({\n                id: `workload-arm-` + k8sVersion.version.replace(\".\", \"-\"),\n                stackBuilder : blueprintARM.clone(props.region).id(`arm-` + k8sVersion.version.replace(\".\", \"-\"))\n            });\n        }\n\n        blueprints.CodePipelineStack.builder()\n            .application('npx ts-node bin/crossplane-argocd-gitops.ts')\n            .name(id)\n            .owner(gitProps.owner)\n            .codeBuildPolicies(\n                ([\n                    new iam.PolicyStatement({\n                        resources: [\"*\"],\n                        actions: [\n                            \"codebuild:*\",\n                            \"sts:AssumeRole\",\n                            \"secretsmanager:GetSecretValue\",\n                            \"secretsmanager:ListSecrets\",\n                            \"secretsmanager:DescribeSecret\",\n                            \"cloudformation:*\"\n                        ]\n                    })\n                ])\n            )\n            .repository({\n                targetRevision : gitProps.revision,\n                credentialsSecretName: gitProps.secretName,\n                repoUrl: gitProps.repoName,\n                trigger: blueprints.GitHubTrigger.POLL\n            }\n            )\n            .wave({ id: `mgmt-cluster-stage`, stages: mgmtStage })\n            .wave({ id: `${id}-wave`, stages })\n            .build(scope, id, { env: { account, region } });\n    }\n}\n\nfunction addManagedNodeGroup(id: string, clusterProps: blueprints.MngClusterProviderProps): ManagedNodeGroup {\n    return  {\n        id,\n        minSize: clusterProps.minSize,\n        maxSize: clusterProps.maxSize,\n        amiType: clusterProps.amiType,\n        instanceTypes: clusterProps.instanceTypes,\n        desiredSize: clusterProps.desiredSize\n    };\n}\n"
  },
  {
    "path": "lib/custom-networking-ipv4-construct/index.ts",
    "content": "\nimport { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport { KubernetesVersion, NodegroupAmiType } from 'aws-cdk-lib/aws-eks';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\n\nexport default class CustomNetworkingIPv4Construct {\n    constructor(scope: Construct, id: string) {\n        const stackId = `${id}-blueprint`;\n\n        const mngProps = {\n            version: KubernetesVersion.V1_25,\n            endpointAccess: eks.EndpointAccess.PUBLIC_AND_PRIVATE,\n            instanceTypes: [new ec2.InstanceType('m5.large')],\n            amiType: NodegroupAmiType.AL2_X86_64,\n            desiredSize: 2,\n            maxSize: 3,\n            vpcSubnets: [{ subnetType: ec2.SubnetType.PUBLIC }]\n        };\n\n\n        const clusterProvider = new blueprints.MngClusterProvider(mngProps);\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(new blueprints.VpcCniAddOn({\n                customNetworkingConfig: {\n                    subnets: [\n                        blueprints.getNamedResource(\"secondary-cidr-subnet-0\"),\n                        blueprints.getNamedResource(\"secondary-cidr-subnet-1\"),\n                        blueprints.getNamedResource(\"secondary-cidr-subnet-2\"),\n                    ]\n                },\n                awsVpcK8sCniCustomNetworkCfg: true,\n                eniConfigLabelDef: 'topology.kubernetes.io/zone'\n            }),\n            new blueprints.AwsLoadBalancerControllerAddOn(),\n            new blueprints.CoreDnsAddOn(),\n            new blueprints.KubeProxyAddOn(),\n            )\n            .resourceProvider(blueprints.GlobalResources.Vpc, new blueprints.VpcProvider(undefined, {\n                primaryCidr: \"10.2.0.0/16\",\n                secondaryCidr: \"100.64.0.0/16\",\n                secondarySubnetCidrs: [\"100.64.0.0/24\", \"100.64.1.0/24\", \"100.64.2.0/24\"]\n            }))\n\n            .clusterProvider(clusterProvider)\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/datadog-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { DatadogAddOn } from '@datadog/datadog-eks-blueprints-addon';\nimport { prevalidateSecrets } from '../common/construct-utils';\n\nconst SECRET_API_KEY = 'datadog-api-key';\n\nexport default class DatadogConstruct {\n    \n    async buildAsync(scope: Construct, id: string) {\n\n        await prevalidateSecrets(DatadogConstruct.name, process.env.CDK_DEFAULT_REGION!, SECRET_API_KEY);\n\n        const stackID = `${id}-blueprint`;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new DatadogAddOn({\n                apiKeyAWSSecret: SECRET_API_KEY\n            })\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION!)\n            .version('auto')\n            .addOns(...addOns)\n            .build(scope, stackID);\n    }\n}\n"
  },
  {
    "path": "lib/dynatrace-construct/index.ts",
    "content": "import { EksBlueprint } from '@aws-quickstart/eks-blueprints';\nimport { DynatraceAddOn } from '@dynatrace/dynatrace-eks-blueprints-addon';\nimport * as cdk from 'aws-cdk-lib';\nimport { prevalidateSecrets } from '../common/construct-utils';\n\nexport default class DynatraceOperatorConstruct {\n\n    async buildAsync(scope: cdk.App, id: string) {\n        await prevalidateSecrets(DynatraceOperatorConstruct.name, undefined, 'dynatrace-tokens');\n        // AddOns for the cluster\n        const stackId = `${id}-blueprint`;\n\n        const DynatraceOperator = new DynatraceAddOn();\n\n        EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .version('auto')\n            .addOns(DynatraceOperator)\n            .build(scope, stackId);\n    }\n\n}\n"
  },
  {
    "path": "lib/emr-eks/index.ts",
    "content": "import { \n    EksBlueprint, \n    AwsLoadBalancerControllerAddOn, \n    CertManagerAddOn, \n    ClusterAutoScalerAddOn, \n    CoreDnsAddOn, \n    EbsCsiDriverAddOn, \n    EmrEksAddOn, \n    EmrEksTeam, \n    KubeProxyAddOn, \n    MetricsServerAddOn, \n    VpcCniAddOn \n} from '@aws-quickstart/eks-blueprints';\n\nimport * as cdk from 'aws-cdk-lib';\n\nexport default class EmrEksConstruct {\n\n    build(scope: cdk.App, id: string, teams: EmrEksTeam[]) {\n        \n        const stackId = `${id}-blueprint`;\n\n        EksBlueprint.builder().addOns(\n            new AwsLoadBalancerControllerAddOn,\n            new VpcCniAddOn(),\n            new CoreDnsAddOn(),\n            new MetricsServerAddOn,\n            new ClusterAutoScalerAddOn,\n            new CertManagerAddOn,\n            new EbsCsiDriverAddOn,\n            new KubeProxyAddOn,\n            new EmrEksAddOn\n        ).teams(\n            ...teams\n        )\n            .version('auto')\n            .build(scope, stackId);\n    }\n\n}"
  },
  {
    "path": "lib/fargate-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as eks from 'aws-cdk-lib/aws-eks';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as team from '../teams';\n\n/**\n * Demonstrates how to use Fargate cluster provider.\n * Along with the specified profiles, Fargate cluster automatically creates\n * a default profile with selectors for the default namespace.\n */\nexport default class FargateConstruct {\n    constructor(scope: Construct, id: string) {\n        // Setup platform team\n        const accountID = process.env.CDK_DEFAULT_ACCOUNT!;\n        const platformTeam = new team.TeamPlatform(accountID);\n       \n        const fargateProfiles: Map<string, eks.FargateProfileOptions> = new Map([\n            [\"team1\", { selectors: [{ namespace: \"team1\" }] }]\n        ]);\n\n        const stackID = `${id}-blueprint`;\n        const clusterProvider = new blueprints.FargateClusterProvider({\n            fargateProfiles,\n            version: eks.KubernetesVersion.V1_25\n        });\n\n        blueprints.EksBlueprint.builder()\n            .account(accountID)\n            .clusterProvider(clusterProvider)\n            .teams(platformTeam)\n            .addOns(\n                new blueprints.VpcCniAddOn(),\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.AppMeshAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.ArgoCDAddOn,\n                new blueprints.MetricsServerAddOn\n            )\n            .build(scope, stackID);\n    }\n}\n\n\n\n"
  },
  {
    "path": "lib/generative-ai-showcase/deployment/showcase-deployment.ytpl",
    "content": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: bedrock-showcase-model\n namespace: \"{{namespace}}\"\n labels:\n   app: bedrock-showcase-model\nspec:\n replicas: 1\n selector:\n   matchLabels:\n     app: bedrock-showcase-model\n template:\n   metadata:\n     labels:\n       app: bedrock-showcase-model\n   spec:\n     serviceAccountName: bedrock-service-account\n     containers:\n     - name: bedrock-showcase-model\n       image: \"{{imageName}}:{{imageTag}}\"\n       imagePullPolicy: IfNotPresent\n       env:\n        - name: BWB_ENDPOINT_URL\n          value: \"https://bedrock.{{region}}.amazonaws.com/\"\n        - name: BWB_PROFILE_NAME\n          value: \"default\"\n        - name: BWB_REGION_NAME\n          value: \"{{region}}\"\n       ports:\n       - containerPort: 8501\n       volumeMounts:\n         - mountPath: /dev/shm\n           name: dshm\n     volumes:\n     - emptyDir:\n         sizeLimit: 1Gi\n         medium: Memory\n       name: dshm\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: bedrock-showcase-model-service\n  namespace: \"{{namespace}}\"\nspec:\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 8501\n  type: NodePort\n  selector:\n    app: bedrock-showcase-model\n---\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n  name: bedrock-showcase-model-ingress\n  namespace: \"{{namespace}}\"\n  annotations:\n    alb.ingress.kubernetes.io/scheme: internet-facing\n    alb.ingress.kubernetes.io/target-type: ip\nspec:\n  ingressClassName: alb\n  rules:\n    - http:\n        paths:\n        - path: /\n          pathType: Prefix\n          backend:\n            service:\n              name: bedrock-showcase-model-service\n              port:\n                number: 80\n"
  },
  {
    "path": "lib/generative-ai-showcase/index.ts",
    "content": "import { ApplicationTeam, BedrockBuilder, ClusterInfo } from \"@aws-quickstart/eks-blueprints\";\nimport * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport * as spi from '@aws-quickstart/eks-blueprints/dist/spi';\nimport { Construct } from \"constructs\";\nimport { loadYaml, readYamlDocument } from \"@aws-quickstart/eks-blueprints/dist/utils\";\nimport { KubectlProvider, ManifestDeployment } from \"@aws-quickstart/eks-blueprints/dist/addons/helm-addon/kubectl-provider\";\n\nexport default class GenAIShowcase {\n    constructor(scope: Construct, id: string) {\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = process.env.CDK_DEFAULT_REGION!;\n        const stackID = `${id}-blueprint`;\n\n        const bedrockTeamProps: blueprints.teams.BedrockTeamProps = {\n            name: blueprints.utils.valueFromContext(scope, \"bedrock.pattern.name\", \"showcase\"),\n            namespace: blueprints.utils.valueFromContext(scope, \"bedrock.pattern.namespace\", \"bedrock\"),\n            createNamespace: true,\n            serviceAccountName: 'bedrock-service-account',\n            extensionFunction: extensionFunction\n        }; \n\n        BedrockBuilder.builder()\n            .account(account)\n            .region(region)\n            .version('auto')\n            .addBedrockTeam(bedrockTeamProps)\n            .build(scope, stackID);\n    }\n}\n\nfunction extensionFunction(team: ApplicationTeam, clusterInfo: ClusterInfo) {\n    const values: spi.Values = {\n        namespace: team.teamProps.namespace,\n        imageName: blueprints.utils.valueFromContext(clusterInfo.cluster, \"bedrock.pattern.image.name\", undefined),\n        imageTag: blueprints.utils.valueFromContext(clusterInfo.cluster, \"bedrock.pattern.image.tag\", undefined),\n        region: clusterInfo.cluster.stack.region\n    };\n\n    // Apply manifest\n    const doc = readYamlDocument(__dirname + '/deployment/showcase-deployment.ytpl');\n    const manifest = doc.split(\"---\").map((e: any) => loadYaml(e));\n\n    const manifestDeployment: ManifestDeployment = {\n        name: team.teamProps.name,\n        namespace: team.teamProps.namespace!,\n        manifest,\n        values\n    };\n    const manifestConstruct = new KubectlProvider(clusterInfo).addManifest(manifestDeployment);\n    manifestConstruct.node.addDependency(team.serviceAccount);\n}"
  },
  {
    "path": "lib/generative-ai-showcase/python/Dockerfile",
    "content": "FROM python:3.9.15\nWORKDIR /opt\nRUN  apt-get update \\\n  && apt-get install -y unzip \\\n  && apt-get install -y curl\nCOPY requirements.txt .\nRUN pip install -r requirements.txt\nRUN curl https://d2eo22ngex1n9g.cloudfront.net/Documentation/SDK/bedrock-python-sdk.zip --output bedrock-python-sdk.zip\nRUN unzip bedrock-python-sdk.zip -d bedrock-python-sdk\nRUN pip install ./bedrock-python-sdk/botocore-*-py3-none-any.whl\nRUN pip install ./bedrock-python-sdk/boto3-*-py3-none-any.whl\nRUN pip install ./bedrock-python-sdk/awscli-*-py3-none-any.whl\nCOPY *.py /opt/\nEXPOSE 8501\nENTRYPOINT [\"streamlit\", \"run\"]\nCMD [\"/opt/showcase_app.py\"]\n"
  },
  {
    "path": "lib/generative-ai-showcase/python/requirements.txt",
    "content": "langchain\nstreamlit\n"
  },
  {
    "path": "lib/generative-ai-showcase/python/showcase_app.py",
    "content": "import streamlit as st\nimport showcase_lib as glib\nimport showcase_examples as examples\n\nst.set_page_config(page_title=\"Demo Showcase\", layout=\"wide\")\n\nst.title(\"Demo Showcase\")\n\ncol1, col2, col3 = st.columns(3)\n\n\nwith col1:\n    st.subheader(\"Prompt template\")\n    \n    prompts_keys = list(examples.prompts)\n\n    prompt_selection = st.selectbox(\"Select a prompt template:\", prompts_keys)\n    \n    with st.expander(\"View prompt\"):\n\n        selected_prompt_template_text = examples.prompts[prompt_selection]\n\n        prompt_text = st.text_area(\"Prompt template text:\", value=selected_prompt_template_text, height=350)\n    \n    \nwith col2:\n    st.subheader(\"User input\")\n    inputs_keys = list(examples.inputs)\n    \n    input_selection = st.selectbox(\"Select an input example:\", inputs_keys)\n    \n    selected_input_template_text = examples.inputs[input_selection]\n\n    input_text = st.text_area(\"Input text:\", value=selected_input_template_text, height=350)\n    \n    process_button = st.button(\"Run\", type=\"primary\")\n\n\n\nwith col3:\n    st.subheader(\"Result\")\n    \n    if process_button:\n        with st.spinner(\"Running...\"):\n            response_content = glib.get_text_response(user_input=input_text, template=prompt_text)\n\n            st.write(response_content)\n"
  },
  {
    "path": "lib/generative-ai-showcase/python/showcase_examples.py",
    "content": "#################################################################################################################################\n\nprompts = {} #pre-defined prompt templatess, include \"{user_input}\" to merge input content\ninputs = {} #used to merge into prompt templates, merged into the \"{user_input}\" placeholder\ndefaults = {} #used for default values in simple examples\n\n#################################################################################################################################\n# PROMPTS\n#################################################################################################################################\n\nprompts[\"Reply Template\"] = \"\"\"\n{user_input}\n\nPlease write a reply to the above text:\n\"\"\"\n\n#################################################################################################################################\n\nprompts[\"Summarize\"] = \"\"\"\n{user_input}\n\nPlease summarize the above content:\n\"\"\"\n\n#################################################################################################################################\n\nprompts[\"Sentiment\"] = \"\"\"\n{user_input}\n\nSentiment of the above content (Positive or negative):\n\"\"\"\n\n#################################################################################################################################\n\nprompts[\"Recommendation\"] = \"\"\"\n{user_input}\n\nRecommended next step based on the above content:\n\"\"\"\n\n#################################################################################################################################\n# INPUTS\n#################################################################################################################################\n\ninputs[\"Complementary Customer Email\"] = \"\"\"\nDear Acme Investments,\nI am writing to compliment one of your customer service representatives, Shirley Scarry. I recently had the pleasure of speaking with Shirley regarding my loan. Shirley was extremely helpful and knowledgeable, and went above and beyond to ensure that all of my questions were answered. Shirley also had Robert Herbford join the call, who wasn't quite as helpful. My wife, Clara Bradford, didn't like him at all.\nShirley's professionalism and expertise were greatly appreciated, and I would be happy to recommend Acme Investments to others based on my experience.\nSincerely,\n\nCarson Bradford\n\"\"\"\n\n#################################################################################################################################\n\ninputs[\"Ethics Complaint Email\"] = \"\"\"\nDear Acme Investments,\nI am writing to bring to your attention a situation that I believe to be unethical on the part of one of your account managers, Roger Longbottom.\nI recently met with Roger to discuss my investment portfolio and was deeply concerned to hear that he suggested I invest in a certain stock. When I asked him why he thought this was a good investment, he stated that the stock was currently undervalued and was likely to increase in value in the near future.\nHowever, upon further research, I have discovered that the stock in question has a questionable reputation. It has been the subject of multiple lawsuits and has been found to have engaged in questionable business practices.\nI believe Roger was aware of these facts, but failed to disclose them to me. As a result, I feel I was misled into making an unwise investment decision.\nI therefore urge you to investigate whether Roger has acted unethically and take appropriate action if necessary.\nYours sincerely,\nCarson Bradford\n\"\"\"\n\n"
  },
  {
    "path": "lib/generative-ai-showcase/python/showcase_lib.py",
    "content": "import os\nfrom langchain.llms.bedrock import Bedrock\nfrom langchain import PromptTemplate\n\n\ndef get_llm():\n    \n    model_kwargs =  { \n        \"maxTokenCount\": 1024, \n        \"stopSequences\": [], \n        \"temperature\": 0, \n        \"topP\": 0.9 \n    }\n    \n    llm = Bedrock(\n        # credentials_profile_name=os.environ.get(\"BWB_PROFILE_NAME\"), #sets the profile name to use for AWS credentials (if not the default)\n        region_name=os.environ.get(\"BWB_REGION_NAME\"), #sets the region name (if not the default)\n        endpoint_url=os.environ.get(\"BWB_ENDPOINT_URL\"), #sets the endpoint URL (if necessary)\n        model_id=\"amazon.titan-tg1-large\", #use the Anthropic Claude model\n        model_kwargs=model_kwargs) #configure the properties for Claude\n    \n    return llm\n\n\ndef get_prompt(user_input, template):\n    \n    prompt_template = PromptTemplate.from_template(template) #this will automatically identify the input variables for the template\n\n    prompt = prompt_template.format(user_input=user_input)\n    \n    return prompt\n\n\ndef get_text_response(user_input, template): #text-to-text client function\n    llm = get_llm()\n    \n    prompt = get_prompt(user_input, template)\n    \n    return llm.predict(prompt) #return a response to the prompt\n\n"
  },
  {
    "path": "lib/generic-cluster-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport * as eks from 'aws-cdk-lib/aws-eks';\n\n// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n\n// Team implementations\nimport * as team from '../teams';\n\n/**\n * Demonstrates how to leverage more than one node group along with Fargate profiles.\n */\nexport default class GenericClusterConstruct {\n    build(scope: Construct, id: string) {\n        // Setup platform team\n        const accountID = process.env.CDK_DEFAULT_ACCOUNT!;\n        const platformTeam = new team.TeamPlatform(accountID);\n \n        const stackID = `${id}-blueprint`;\n        \n        const clusterProvider = new blueprints.GenericClusterProvider({\n            version: eks.KubernetesVersion.V1_25,\n            managedNodeGroups: [\n                {\n                    id: \"mng-ondemand\",\n                    amiType: eks.NodegroupAmiType.AL2_X86_64,\n                    instanceTypes: [new ec2.InstanceType('m5.2xlarge')]\n                },\n                {\n                    id: \"mng2-spot\",\n                    instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM)],\n                    nodeGroupCapacityType: eks.CapacityType.SPOT\n                }\n            ],\n            fargateProfiles: {\n                \"fp1\": {\n                    fargateProfileName: \"fp1\",\n                    selectors:  [{ namespace: \"serverless1\" }] \n                },\n                \"fp2\": {\n                    fargateProfileName: \"fp2\",\n                    selectors:  [{ namespace: \"serverless2\" }] \n                }\n            }\n        });\n\n        blueprints.EksBlueprint.builder()\n            .account(accountID)\n            .region(process.env.CDK_DEFAULT_REGION!)\n            .clusterProvider(clusterProvider)\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.CertManagerAddOn,\n                new blueprints.AdotCollectorAddOn,\n                new blueprints.AppMeshAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.ArgoCDAddOn,\n                new blueprints.CalicoOperatorAddOn,\n                new blueprints.MetricsServerAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n                new blueprints.CloudWatchAdotAddOn,\n                new blueprints.XrayAdotAddOn,\n                new blueprints.SecretsStoreAddOn\n            )\n            .teams(platformTeam)\n            .version('auto')\n            .build(scope, stackID);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/gmaestro-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as gmaestroAddOn from '@granulate/gmaestro-eks-blueprints-addon';\nimport * as cdk from 'aws-cdk-lib';\nimport {prevalidateSecrets} from \"../common/construct-utils\";\n\n\nexport default class GmaestroConstruct {\n    async buildAsync(scope: cdk.App, id: string) {\n        const clientIdSecretName = process.env.MAESTRO_SECRET_NAME;\n        if (clientIdSecretName === undefined) {\n            throw new Error(\"secret must be setup for the gMaestro pattern pattern to work\");\n        }\n        await prevalidateSecrets(GmaestroConstruct.name, process.env.CDK_DEFAULT_REGION!, clientIdSecretName);\n\n        const clusterName = blueprints.utils.valueFromContext(scope, \"clusterName\", undefined);\n        const namespace = blueprints.utils.valueFromContext(scope, \"namespace\", undefined);\n        if (clusterName === undefined || namespace === undefined) {\n            throw new Error(\"clusterName and namespace must be setup for the gMaestro pattern pattern to work\");\n        }\n\n        const stackId = `${id}-blueprint`;\n\n        let gmaestroAddOnProps = {\n            clientIdSecretName: clientIdSecretName,\n            clusterName: clusterName,\n            createNamespace: true,\n            namespace: namespace,\n        } as gmaestroAddOn.GmaestroAddOnProps;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.MetricsServerAddOn(),\n            new blueprints.addons.ClusterAutoScalerAddOn(),\n            new gmaestroAddOn.GmaestroAddOn(gmaestroAddOnProps)\n        ];\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(...addOns)\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/gpu-construct/index.ts",
    "content": "import * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from \"constructs\";\nimport { GpuBuilder, GpuOptions } from \"@aws-quickstart/eks-blueprints\";\n\nexport default class GpuConstruct {\n    build(scope: Construct, id: string) {\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = process.env.CDK_DEFAULT_REGION!;\n        const stackID = `${id}-eks-blueprint`;\n\n        const options: GpuOptions = {\n            kubernetesVersion: eks.KubernetesVersion.of(\"1.27\"),\n            instanceClass: ec2.InstanceClass.G5,\n            instanceSize: ec2.InstanceSize.XLARGE12\n        };\n\n        const values = {\n            driver: {\n                enabled: true\n            },\n            mig: {\n                strategy: 'mixed'\n            },\n            devicePlugin: {\n                enabled: true,\n                version: 'v0.13.0'\n            },\n            migManager: {\n                enabled: true,\n                WITH_REBOOT: true\n            },\n            toolkit: {\n                version: 'v1.13.1-centos7'\n            },\n            operator: {\n                defaultRuntime: 'containerd'\n            },\n            gfd: {\n                version: 'v0.8.0'\n            }\n        };\n\n        GpuBuilder.builder(options)\n            .account(account)\n            .region(region)\n            .enableGpu({values})\n            .build(scope, stackID);\n    }\n}"
  },
  {
    "path": "lib/graviton-construct/index.ts",
    "content": "import * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport { GravitonBuilder } from \"@aws-quickstart/eks-blueprints\";\nimport { CfnWorkspace } from \"aws-cdk-lib/aws-aps\";\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from \"constructs\";\n\nexport default class GravitonConstruct {\n    build(scope: Construct, id: string) {\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = process.env.CDK_DEFAULT_REGION!;\n        const stackID = `${id}-blueprint`;\n\n        const ampWorkspaceName = \"graviton-amp-workspaces\";\n        const ampWorkspace: CfnWorkspace =\n            blueprints.getNamedResource(ampWorkspaceName);\n\n        const options: Partial<blueprints.MngClusterProviderProps> = {\n            version: eks.KubernetesVersion.of(\"1.27\"),\n            instanceTypes: [ec2.InstanceType.of(ec2.InstanceClass.M7G, ec2.InstanceSize.XLARGE)],\n            desiredSize: 3,\n            minSize: 2,\n            maxSize: 5,\n        };\n\n        GravitonBuilder.builder(options)\n            .account(account)\n            .region(region)\n            .resourceProvider(\n                blueprints.GlobalResources.Vpc,\n                new blueprints.VpcProvider()\n            )\n            .resourceProvider(\n                \"efs-file-system\",\n                new blueprints.CreateEfsFileSystemProvider({\n                    name: \"efs-file-systems\",\n                })\n            )\n            .resourceProvider(\n                ampWorkspaceName,\n                new blueprints.CreateAmpProvider(\n                    ampWorkspaceName,\n                    ampWorkspaceName\n                )\n            )\n            .addOns(\n                new blueprints.addons.IstioBaseAddOn(),\n                new blueprints.addons.IstioControlPlaneAddOn(),\n                new blueprints.addons.KubeStateMetricsAddOn(),\n                new blueprints.addons.MetricsServerAddOn(),\n                new blueprints.addons.PrometheusNodeExporterAddOn(),\n                new blueprints.addons.ExternalsSecretsAddOn(),\n                new blueprints.addons.SecretsStoreAddOn(),\n                new blueprints.addons.CalicoOperatorAddOn(),\n                new blueprints.addons.CertManagerAddOn(),\n                new blueprints.addons.AdotCollectorAddOn(),\n                new blueprints.addons.AmpAddOn({\n                    ampPrometheusEndpoint: ampWorkspace.attrPrometheusEndpoint\n                }),\n                new blueprints.addons.CloudWatchLogsAddon({\n                    logGroupPrefix: \"/aws/eks/graviton-blueprint\",\n                }),\n                new blueprints.addons.EfsCsiDriverAddOn(),\n                new blueprints.addons.FluxCDAddOn(),\n                new blueprints.addons.GrafanaOperatorAddon(),\n                new blueprints.addons.XrayAdotAddOn()\n            )\n            .build(scope, stackID);\n    }\n}"
  },
  {
    "path": "lib/import-cluster/index.ts",
    "content": "import { Construct } from \"constructs\";\nimport * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport { GlobalResources } from \"@aws-quickstart/eks-blueprints\";\nimport { TeamRikerSetup, TeamScan } from \"../teams\";\n\n\n\nexport class ImportClusterConstruct {\n\n    /**\n     * Create a blueprint that imports an existing cluster. \n     * @param scope stack scope\n     */\n    async build(scope: Construct) {\n\n        /* \n         *Modify these constants for your use case.\n        */\n        const clusterName = \"quickstart-cluster\";\n        const kubectlRoleName = \"awsqs-kubernetes-helm\"; \n        const region = process.env.CDK_DEFAULT_REGION!;\n\n        const sdkCluster = await blueprints.describeCluster(clusterName, region);\n\n        /**\n         * Assumes the supplied role is registered in the target cluster for kubectl access.\n         */\n        const importClusterProvider = blueprints.ImportClusterProvider.fromClusterAttributes(sdkCluster, blueprints.getResource(context =>\n            new blueprints.LookupRoleProvider(kubectlRoleName).provide(context)));\n        \n        const vpcId = sdkCluster.resourcesVpcConfig?.vpcId;\n\n        blueprints.EksBlueprint.builder()\n            .clusterProvider(importClusterProvider)\n            .resourceProvider(GlobalResources.Vpc, new blueprints.VpcProvider(vpcId)) // Important! register cluster VPC\n            .addOns(new blueprints.AppMeshAddOn())\n            .teams(new TeamRikerSetup(scope, \"./lib/teams/team-riker/\"))\n            .teams(new TeamScan())\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region('us-east-2')\n            .build(scope, \"imported-cluster\");\n    }\n}"
  },
  {
    "path": "lib/instana-construct/index.ts",
    "content": "import { loadYaml } from \"@aws-quickstart/eks-blueprints/dist/utils\";\nimport * as cdk from \"aws-cdk-lib\";\nimport { InstanaOperatorAddon } from \"@instana/aws-eks-blueprint-addon\";\nimport { EksBlueprint, utils } from \"@aws-quickstart/eks-blueprints\";\nimport { prevalidateSecrets } from \"../common/construct-utils\";\n\nexport const instanaProps: { [key: string]: any } = {};\n\nexport default class InstanaConstruct {\n    async buildAsync(scope: cdk.App, id: string) {\n        try {\n            await prevalidateSecrets(InstanaConstruct.name, undefined, 'instana-secret-params');\n\n            const secretParamName: string = utils.valueFromContext(scope, \"secretParamName\", undefined);\n            //console.log(`secretParamName is ${secretParamName}`);\n            if(secretParamName != undefined) {\n                instanaProps.secretParamName = secretParamName;\n            }\n            const yamlObject = loadYaml(JSON.stringify(instanaProps));\n            //console.log(`instanaProps is ${yamlObject}`);\n            const stackId = `${id}-blueprint`;\n            const addOns = new InstanaOperatorAddon(yamlObject);\n            EksBlueprint.builder()\n                .account(process.env.CDK_DEFAULT_ACCOUNT!)\n                .region(process.env.CDK_DEFAULT_REGION!)\n                .addOns(addOns)\n                .version('auto')\n                .build(scope, stackId);\n            console.log(\"Blueprint built successfully.\");\n        } catch (error) {\n            console.error(\"Error:\", error);\n            throw new Error(`environment variables must be setup for the instana-operator pattern to work`);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/ipv6-construct/index.ts",
    "content": "import * as cdk from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { Construct } from \"constructs\";\nimport { IpFamily } from 'aws-cdk-lib/aws-eks';\n\nexport default class IpV6Construct {\n    build(scope: Construct, id: string) {\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = process.env.CDK_DEFAULT_REGION!;\n        const stackID = `${id}-blueprint`;\n\n        const ipFamily = IpFamily.IP_V6; //IpFamily.IP_V6 is equivalent to \"ipv6\"\n        // AddOns for the cluster. For ipv6 cluster, we haven't tested with all the addons except for the below addons.\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.addons.VpcCniAddOn(),\n            new blueprints.addons.KarpenterAddOn(),\n            new blueprints.addons.SecretsStoreAddOn()\n        ];\n        blueprints.EksBlueprint.builder()\n            .account(account)\n            .region(region)\n            .version('auto')\n            .ipFamily(ipFamily)\n            .addOns(...addOns)\n            .build(scope, stackID);\n    }\n}"
  },
  {
    "path": "lib/jupyterhub-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n\nimport * as cdk from 'aws-cdk-lib';\n\nexport default class JupyterHubConstruct {\n    constructor(scope: Construct, id: string, props: cdk.StackProps) {\n        const stackId = `${id}-blueprint`;  \n\n        blueprints.EksBlueprint.builder()\n            .account(props.env!.account!)\n            .region(props.env!.region!)\n            .version('auto')\n            .addOns(\n                new blueprints.EfsCsiDriverAddOn({replicaCount: 1}),\n                new blueprints.VpcCniAddOn(),\n                new blueprints.KubeProxyAddOn(),\n                new blueprints.ClusterAutoScalerAddOn(),\n                new blueprints.JupyterHubAddOn({\n                    efsConfig:{\n                        removalPolicy: cdk.RemovalPolicy.DESTROY,\n                        pvcName: \"efs-persist\",\n                        capacity: \"120Gi\",\n                    },\n                    oidcConfig: {\n                        callbackUrl: blueprints.utils.valueFromContext(scope, \"callbackUrl\", \"https://www.example.com/hub/oauth_callback\"),\n                        authUrl: blueprints.utils.valueFromContext(scope, \"authUrl\", \"https://yourid.oidcprovider.com/authorize\"),\n                        tokenUrl: blueprints.utils.valueFromContext(scope, \"tokenUrl\", \"https://yourid.oidcprovider.com/oauth/token\"),\n                        userDataUrl: blueprints.utils.valueFromContext(scope, \"userDataUrl\", \"https://yourid.oidcprovider.com/userinfo\"),\n                        clientId: blueprints.utils.valueFromContext(scope, \"clientId\", \"yourClientIdString\"),\n                        clientSecret: blueprints.utils.valueFromContext(scope, \"clientSecret\", \"yourClientSecretString\"),\n                        scope: blueprints.utils.valueFromContext(scope, \"scope\",[\"openid\",\"name\",\"profile\",\"email\"]),\n                        usernameKey: blueprints.utils.valueFromContext(scope, \"usernameKey\", \"name\"),\n                    },\n                    serviceType: blueprints.JupyterHubServiceType.CLUSTERIP,\n                    values: { \n                        prePuller: { \n                            hook: { enabled: false },\n                        }\n                    }\n                })\n            )\n            .build(scope, stackId); \n    }\n}\n  "
  },
  {
    "path": "lib/karpenter-construct/index.ts",
    "content": "import { EksBlueprint } from \"@aws-quickstart/eks-blueprints\";\nimport * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport { Construct } from \"constructs\";\nexport default class KarpenterConstruct {\n    constructor(scope: Construct, id: string) {\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = process.env.CDK_DEFAULT_REGION!;\n        const stackID = `${id}-blueprint`;\n\n        const karpenterAddOn = new blueprints.addons.KarpenterAddOn({\n            version: 'v0.33.1',\n            nodePoolSpec: {\n                requirements: [\n                    { key: 'node.kubernetes.io/instance-type', operator: 'In', values: ['m5.large'] },\n                    { key: 'topology.kubernetes.io/zone', operator: 'In', values: [`${region}a`,`${region}b`, `${region}c`]},\n                    { key: 'kubernetes.io/arch', operator: 'In', values: ['amd64','arm64']},\n                    { key: 'karpenter.sh/capacity-type', operator: 'In', values: ['on-demand']},\n                ],\n                disruption: {\n                    consolidationPolicy: \"WhenUnderutilized\",\n                    expireAfter: \"259200s\"\n                },\n                weight: 20,\n\n            },\n            ec2NodeClassSpec:{\n                subnetSelectorTerms: [\n                    {\n                        tags: { \"Name\": `${stackID}/${stackID}-vpc/*` }\n                    }\n                ],\n                securityGroupSelectorTerms: [\n                    {\n                        tags: { [`kubernetes.io/cluster/${stackID}`]: \"owned\" }\n                    }\n                ],\n\n                amiFamily: \"AL2\"\n            },\n            interruptionHandling: true,\n        });\n\n        EksBlueprint.builder()\n            .account(account)\n            .region(region)\n            .version('auto')\n            .addOns(\n                new blueprints.addons.AwsLoadBalancerControllerAddOn(),\n                new blueprints.addons.VpcCniAddOn(),\n                new blueprints.addons.CoreDnsAddOn(),\n                new blueprints.addons.KubeProxyAddOn(),\n                new blueprints.addons.CertManagerAddOn(),\n                new blueprints.addons.KubeStateMetricsAddOn(),\n                new blueprints.addons.SSMAgentAddOn(),\n                new blueprints.addons.MetricsServerAddOn(),\n                karpenterAddOn,\n            )\n            .build(scope, stackID);\n\n    }\n}\n"
  },
  {
    "path": "lib/kasten-k10-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { KastenK10AddOn } from '@kastenhq/kasten-eks-blueprints-addon';\n\nexport default class KastenK10Construct {\n    constructor(scope: Construct, id: string) {\n        const stackId = `${id}-blueprint`;  \n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .version('auto')\n            .addOns(new blueprints.ClusterAutoScalerAddOn, new KastenK10AddOn)\n            .build(scope, stackId); \n    }\n}\n  "
  },
  {
    "path": "lib/keptn-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport { EksBlueprint } from '@aws-quickstart/eks-blueprints';\nimport { KeptnControlPlaneAddOn } from '@keptn/keptn-controlplane-eks-blueprints-addon';\n\nexport default class KeptnControlPlaneConstruct {\n\n    constructor(scope: Construct, id: string) {\n        // AddOns for the cluster\n        const stackId = `${id}-blueprint`;\n\n        const keptnControlPlane = new KeptnControlPlaneAddOn({\n            // uncomment after you setup the ssm secret keptn-secrets.\n            // ssmSecretName: 'keptn-secrets'\n        });\n\n        EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(keptnControlPlane)\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/komodor-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { KomodorAddOn } from '@komodor/komodor-eks-blueprints-addon';\nimport { Construct } from \"constructs\";\n\nexport default class KomodorConstruct extends Construct {\n    constructor(scope: Construct, id: string) {\n        super(scope, id);\n\n        const stackId = `${id}-blueprint`;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new KomodorAddOn({\n                clusterName: stackId,\n                apiKey: \"<your-api-key>\", //replace with your API key\n                values: {} // add any custom Helm values\n            })\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(...addOns)\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/konveyor-construct/index.ts",
    "content": "import { StackProps } from \"aws-cdk-lib\";\nimport { Construct } from \"constructs\";\nimport * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport {\n    KonveyorAddOn,\n    OlmAddOn,\n} from \"@claranet-ch/konveyor-eks-blueprint-addon\";\n\n\nexport interface KonveyorConstructProps extends StackProps {\n    /**\n     * The AWS Account ID\n     */\n    account: string;\n    /**\n     * Region where AddOn will be deployed\n     */\n    region: string;\n    /**\n     * Parent domain name where the subdomain will be assigned\n     */\n    parentDomain: string;\n    /**\n     * Subdomain name to be assigned to the loadbalancer\n     */\n    konveyorLabel: string;\n    /**\n     * Hosted Zone ID\n     */\n    hostedZoneId: string;\n    /**\n     * Name of the SSL certificate to be attached to the load balancer\n     */\n    certificateResourceName: string;\n}\n\nexport class KonveyorConstruct extends Construct {\n    constructor(scope: Construct, id: string) {\n        super(scope, id);\n\n        // Definition of the add-on's properties\n        const props = {\n            account: process.env.CDK_DEFAULT_ACCOUNT,\n            region: process.env.CDK_DEFAULT_REGION,\n            namespace: blueprints.utils.valueFromContext(\n                scope,\n                \"konveyor.namespace.name\",\n                \"konveyor\"\n            ),\n            parentDomain: blueprints.utils.valueFromContext(\n                scope,\n                \"konveyor.parent.domain.name\",\n                \"example.com\"\n            ),\n            konveyorLabel: blueprints.utils.valueFromContext(\n                scope,\n                \"konveyor.subdomain.label\",\n                \"konveyor\"\n            ),\n            hostedZoneId: blueprints.utils.valueFromContext(\n                scope,\n                \"konveyor.hosted.zone.id\",\n                \"1234567890\"\n            ),\n            certificateResourceName: blueprints.utils.valueFromContext(\n                scope,\n                \"konveyor.certificate.resource.name\",\n                \"konveyor-certificate\"\n            ),\n        };\n\n        const subdomain = props.konveyorLabel + \".\" + props.parentDomain;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.AwsLoadBalancerControllerAddOn(),\n            new blueprints.VpcCniAddOn(),\n            new blueprints.CoreDnsAddOn(),\n            new blueprints.KubeProxyAddOn(),\n            new blueprints.ExternalDnsAddOn({\n                hostedZoneResources: [blueprints.GlobalResources.HostedZone],\n            }),\n            new blueprints.EbsCsiDriverAddOn(),\n            new OlmAddOn(),\n            new KonveyorAddOn({\n                certificateResourceName: props.certificateResourceName,\n                subdomain,\n                featureAuthRequired: \"true\",\n            }),\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(props.account)\n            .region(props.region)\n            .resourceProvider(\n                blueprints.GlobalResources.HostedZone,\n                new blueprints.ImportHostedZoneProvider(\n                    props.hostedZoneId,\n                    props.parentDomain\n                )\n            )\n            .resourceProvider(\n                props.certificateResourceName,\n                new blueprints.CreateCertificateProvider(\n                    \"elb-certificate\",\n                    subdomain,\n                    blueprints.GlobalResources.HostedZone\n                )\n            )\n            .version('auto')\n            .addOns(...addOns)\n            .build(scope, props.konveyorLabel + \"-cluster\");\n    }\n}"
  },
  {
    "path": "lib/kubecost-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport { EksBlueprint } from '@aws-quickstart/eks-blueprints';\nimport { KubecostAddOn } from '@kubecost/kubecost-eks-blueprints-addon';\n\n\nexport default class KubecostConstruct {\n    constructor(scope: Construct, id: string) {\n        // AddOns for the cluster\n        const stackId = `${id}-blueprint`;\n\n        EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(new KubecostAddOn())\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/kubeflow-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { KubeflowAddOn } from 'eks-blueprints-cdk-kubeflow-ext';\nimport * as amp from 'aws-cdk-lib/aws-aps';\nimport * as eks from 'aws-cdk-lib/aws-eks';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\n\nexport default class KubeflowConstruct {\n    constructor(scope: Construct, id: string) {\n        const stackId = `${id}-blueprint`;\n        const ampWorkspaceName = \"kubeflow-monitoring\";\n        const ampPrometheusEndpoint = (blueprints.getNamedResource(ampWorkspaceName) as unknown as amp.CfnWorkspace).attrPrometheusEndpoint;\n\n        const mngProps: blueprints.MngClusterProviderProps = {\n            version: eks.KubernetesVersion.V1_29,\n            instanceTypes: [new ec2.InstanceType(\"m5.2xlarge\")],\n            amiType: eks.NodegroupAmiType.AL2_X86_64,\n            desiredSize: 2,\n            maxSize: 3, \n        };\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .resourceProvider(ampWorkspaceName, new blueprints.CreateAmpProvider(ampWorkspaceName, ampWorkspaceName))\n            .addOns( \n                new blueprints.AwsLoadBalancerControllerAddOn(),\n                new blueprints.ClusterAutoScalerAddOn(),\n                new blueprints.VpcCniAddOn(),\n                new blueprints.CoreDnsAddOn(),\n                new blueprints.KubeProxyAddOn(),\n                new blueprints.EbsCsiDriverAddOn(),\n                new blueprints.CertManagerAddOn(),\n                new blueprints.KubeStateMetricsAddOn(),\n                new blueprints.MetricsServerAddOn(),\n                new blueprints.PrometheusNodeExporterAddOn(),\n                new blueprints.addons.IstioBaseAddOn({\n                    version: \"1.18.2\"\n                }),\n                new blueprints.addons.IstioControlPlaneAddOn({\n                    version: \"1.18.2\"\n                }),\n                new blueprints.addons.IstioIngressGatewayAddon({\n                    version: \"1.18.2\"\n                }),\n                new blueprints.addons.IstioCniAddon({\n                    version: \"1.18.2\"\n                }),\n                new blueprints.AdotCollectorAddOn(),\n                new blueprints.addons.AmpAddOn({\n                    ampPrometheusEndpoint: ampPrometheusEndpoint,\n                }),\n                new KubeflowAddOn({\n                    namespace: 'kubeflow-pipelines'\n                })\n            )\n            .clusterProvider(new blueprints.MngClusterProvider(mngProps))\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/kubeshark-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport { EksBlueprint } from '@aws-quickstart/eks-blueprints';\nimport { KubesharkAddOn } from 'kubeshark';\n\n\nexport default class KubesharkConstruct {\n    constructor(scope: Construct, id: string) {\n        // AddOns for the cluster\n        const stackId = `${id}-blueprint`;\n\n        EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(new KubesharkAddOn({repository: \"https://helm.kubeshark.com\"}))\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/multi-account-monitoring/amg-iam-setup.ts",
    "content": "import { Construct } from 'constructs';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as cdk from 'aws-cdk-lib';\n\n/**\n * Defines properties for the AMG IAM setup. \n */\nexport interface AmgIamSetupStackProps extends cdk.StackProps {\n    /**\n     * Role to create for the AMG stack that grants access to the specified accounts for AMP and CloudWatch metrics.\n     */\n    roleName: string,\n\n    /**\n     * Monitored accounts. These contain ampPrometheusDataSourceRole and cloudwatchPrometheusDataSourceRole roles \n     * with trust relationship to the monitoring (AMG) account.\n     */\n    accounts: string[]\n} \n\n/**\n * Stack provisions IAM in the moniitoring account with turst relationship to the monitored account for metrics. \n */\nexport class AmgIamSetupStack extends cdk.Stack {\n  \n    constructor(scope: Construct, id: string, props: AmgIamSetupStackProps) {\n        super(scope, id, props);\n\n        const role = new iam.Role(this, 'amg-iam-role', {\n            roleName: props.roleName,\n            assumedBy: new iam.ServicePrincipal('grafana.amazonaws.com'),\n            description: 'Service Role for Amazon Managed Grafana',\n        });\n        \n        for (let i = 0; i < props.accounts.length; i++) {\n            role.addToPolicy(new iam.PolicyStatement({\n                actions: [\n                    \"sts:AssumeRole\"\n                ],\n                resources: [`arn:aws:iam::${props.accounts[i]}:role/ampPrometheusDataSourceRole`,\n                    `arn:aws:iam::${props.accounts[i]}:role/cloudwatchDataSourceRole`\n                ],\n            }));\n        }\n\n        new cdk.CfnOutput(this, 'AMGRole', { value: role ? role.roleArn : \"none\" });\n    }\n}"
  },
  {
    "path": "lib/multi-account-monitoring/amp-iam-setup.ts",
    "content": "import { NestedStack, NestedStackProps } from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { Construct } from 'constructs';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as cdk from 'aws-cdk-lib';\n\n/**\n * Stack the creates the role with trust relationship to the monitoring account to \n * get AMP metrics.\n */\nexport class AmpIamSetupStack extends NestedStack {\n\n    public static builder(roleName: string, trustAccount: string): blueprints.NestedStackBuilder {\n        return {\n            build(scope: Construct, id: string, props: NestedStackProps) {\n                return new AmpIamSetupStack(scope, id, props, roleName, trustAccount);\n            }\n        };\n    }\n\n    constructor(scope: Construct, id: string, props: NestedStackProps, roleName: string, trustAccount: string) {\n        super(scope, id, props);\n\n        const role = new iam.Role(this, 'amp-iam-trust-role', {\n            roleName: roleName,\n            assumedBy: new iam.AccountPrincipal(trustAccount),\n            description: 'AMP role to assume from central account',\n        });\n\n        role.addToPolicy(new iam.PolicyStatement({\n            actions: [\n                \"aps:ListWorkspaces\",\n                \"aps:DescribeWorkspace\",\n                \"aps:QueryMetrics\",\n                \"aps:GetLabels\",\n                \"aps:GetSeries\",\n                \"aps:GetMetricMetadata\",\n                \"xray:PutTraceSegments\",\n                \"xray:PutTelemetryRecords\",\n                \"xray:GetSamplingRules\",\n                \"xray:GetSamplingTargets\",\n                \"xray:GetSamplingStatisticSummaries\",\n                \"xray:BatchGetTraces\",\n                \"xray:GetServiceGraph\",\n                \"xray:GetTraceGraph\",\n                \"xray:GetTraceSummaries\",\n                \"xray:GetGroups\",\n                \"xray:GetGroup\",\n                \"xray:ListTagsForResource\",\n                \"xray:GetTimeSeriesServiceStatistics\",\n                \"xray:GetInsightSummaries\",\n                \"xray:GetInsight\",\n                \"xray:GetInsightEvents\",\n                \"xray:GetInsightImpactGraph\",\n                \"ssm:GetParameter\"\n            ],\n            resources: [\"*\"],\n        }));\n\n        new cdk.CfnOutput(this, 'AMPTrustRole', { value: role ? role.roleArn : \"none\" });\n    }\n}"
  },
  {
    "path": "lib/multi-account-monitoring/cloudwatch-iam-setup.ts",
    "content": "import { NestedStack, NestedStackProps } from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { Construct } from 'constructs';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as cdk from 'aws-cdk-lib';\n\n/**\n * Stack the creates the role with trust relationship to the monitoring account to \n * get CloudWatch metrics.\n */\nexport class CloudWatchIamSetupStack extends NestedStack {\n\n    public static builder(roleName: string, trustAccount: string): blueprints.NestedStackBuilder {\n        return {\n            build(scope: Construct, id: string, props: NestedStackProps) {\n                return new CloudWatchIamSetupStack(scope, id, props, roleName, trustAccount);\n            }\n        };\n    }\n\n    constructor(scope: Construct, id: string, props: NestedStackProps, roleName: string, trustAccount: string) {\n        super(scope, id, props);\n\n        const role = new iam.Role(this, 'cloudwatch-iam-trust-role', {\n            roleName: roleName,\n            assumedBy: new iam.AccountPrincipal(trustAccount),\n            description: 'CloudWatch role to assume from central account',\n        });\n\n        role.addToPolicy(new iam.PolicyStatement({\n            actions: [\n                \"cloudwatch:DescribeAlarmsForMetric\",\n                \"cloudwatch:DescribeAlarmHistory\",\n                \"cloudwatch:DescribeAlarms\",\n                \"cloudwatch:ListMetrics\",\n                \"cloudwatch:GetMetricStatistics\",\n                \"cloudwatch:GetMetricData\",\n                \"logs:DescribeLogGroups\",\n                \"logs:GetLogGroupFields\",\n                \"logs:StartQuery\",\n                \"logs:StopQuery\",\n                \"logs:GetQueryResults\",\n                \"logs:GetLogEvents\",\n                \"ec2:DescribeTags\",\n                \"ec2:DescribeInstances\",\n                \"ec2:DescribeRegions\",\n                \"tag:GetResources\",\n                \"xray:PutTraceSegments\",\n                \"xray:PutTelemetryRecords\",\n                \"xray:GetSamplingRules\",\n                \"xray:GetSamplingTargets\",\n                \"xray:GetSamplingStatisticSummaries\",\n                \"xray:BatchGetTraces\",\n                \"xray:GetServiceGraph\",\n                \"xray:GetTraceGraph\",\n                \"xray:GetTraceSummaries\",\n                \"xray:GetGroups\",\n                \"xray:GetGroup\",\n                \"xray:ListTagsForResource\",\n                \"xray:GetTimeSeriesServiceStatistics\",\n                \"xray:GetInsightSummaries\",\n                \"xray:GetInsight\",\n                \"xray:GetInsightEvents\",\n                \"xray:GetInsightImpactGraph\",\n                \"ssm:GetParameter\"\n            ],\n            resources: [\"*\"],\n        }));\n\n        new cdk.CfnOutput(this, 'CloudWatchTrustRole', { value: role ? role.roleArn : \"none\" });\n    }\n}"
  },
  {
    "path": "lib/multi-account-monitoring/index.ts",
    "content": "export { PipelineMultiEnvMonitoring } from './pipeline';"
  },
  {
    "path": "lib/multi-account-monitoring/pipeline.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as cdk from 'aws-cdk-lib';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport { Construct } from 'constructs';\nimport AmpMonitoringConstruct from '../amp-monitoring';\nimport CloudWatchMonitoringConstruct from '../cloudwatch-monitoring';\nimport { AmgIamSetupStack, AmgIamSetupStackProps } from './amg-iam-setup';\nimport { AmpIamSetupStack } from './amp-iam-setup';\nimport { CloudWatchIamSetupStack } from './cloudwatch-iam-setup';\n\nconst logger = blueprints.utils.logger;\n\n/**\n * Function relies on a secret called \"cdk-context\" defined in the target region (pipeline account must have it)\n * @returns \n */\nexport async function populateAccountWithContextDefaults(): Promise<PipelineMultiEnvMonitoringProps> {\n    // Populate Context Defaults for all the accounts\n    const cdkContext = JSON.parse(await blueprints.utils.getSecretValue('cdk-context', 'us-east-1'))['context'] as PipelineMultiEnvMonitoringProps;\n    logger.debug(`Retrieved CDK context ${JSON.stringify(cdkContext)}`);\n    return cdkContext;\n}\n\nexport interface PipelineMultiEnvMonitoringProps {\n    /**\n     * Production workload environment (account/region) #1 \n     */\n    prodEnv1: cdk.Environment;\n\n    /**\n     * Production workload environment (account/region) #2\n     */\n    prodEnv2: cdk.Environment;\n\n    /**\n     * Environment (account/region) where pipeline will be running (generally referred to as CICD account)\n     */\n    pipelineEnv: cdk.Environment;\n\n    /**\n     * Environment (account/region) where monitoring dashboards will be configured.\n     */\n    monitoringEnv: cdk.Environment;\n}\n\n/**\n * Main multi-account monitoring pipeline.\n */\nexport class PipelineMultiEnvMonitoring {\n\n    async buildAsync(scope: Construct) {\n        const context = await populateAccountWithContextDefaults();\n        // environments IDs consts\n        const PROD1_ENV_ID = `eks-prod1-${context.prodEnv1.region}`;\n        const PROD2_ENV_ID = `eks-prod2-${context.prodEnv2.region}`;\n        const MON_ENV_ID = `central-monitoring-${context.monitoringEnv.region}`;\n\n        const blueprintAmp = new AmpMonitoringConstruct().create(scope, context.prodEnv1.account, context.prodEnv1.region);\n        const blueprintCloudWatch = new CloudWatchMonitoringConstruct().create(scope, context.prodEnv2.account, context.prodEnv2.region);\n\n        // Argo configuration per environment\n        const prodArgoAddonConfig = createArgoAddonConfig('prod', 'https://github.com/aws-samples/eks-blueprints-workloads.git');\n\n        // const { gitOwner, gitRepositoryName } = await getRepositoryData();\n        const gitOwner = 'aws-samples';\n        const gitRepositoryName = 'cdk-eks-blueprints-patterns';\n\n        const amgIamSetupStackProps: AmgIamSetupStackProps = {\n            roleName: \"amgWorkspaceIamRole\",\n            accounts: [context.prodEnv1.account!, context.prodEnv2.account!],\n            env: {\n                account: context.monitoringEnv.account!,\n                region: context.monitoringEnv.region!\n            }\n        };\n\n        blueprints.CodePipelineStack.builder()\n            .application(\"npx ts-node bin/pipeline-multienv-monitoring.ts\")\n            .name(\"multi-account-central-pipeline\")\n            .owner(gitOwner)\n            .codeBuildPolicies([ \n                new iam.PolicyStatement({\n                    resources: [\"*\"],\n                    actions: [    \n                        \"sts:AssumeRole\",\n                        \"secretsmanager:GetSecretValue\",\n                        \"secretsmanager:DescribeSecret\",\n                        \"cloudformation:*\"\n                    ]\n                })\n            ])\n            .repository({\n                repoUrl: gitRepositoryName,\n                credentialsSecretName: 'github-token',\n                targetRevision: 'main',\n            })\n            .enableCrossAccountKeys()\n            .wave({\n                id: \"prod-test\",\n                stages: [\n                    {\n                        id: PROD1_ENV_ID,\n                        stackBuilder: blueprintAmp\n                            .clone(context.prodEnv1.region, context.prodEnv1.account)\n                            .addOns(new blueprints.NestedStackAddOn({\n                                builder: AmpIamSetupStack.builder(\"ampPrometheusDataSourceRole\", context.monitoringEnv.account!),\n                                id: \"amp-iam-nested-stack\"\n                            }))\n                            .addOns(\n                                prodArgoAddonConfig,\n                            )\n                    },\n                    {\n                        id: PROD2_ENV_ID,\n                        stackBuilder: blueprintCloudWatch\n                            .clone(context.prodEnv2.region, context.prodEnv2.account)\n                            .addOns(new blueprints.NestedStackAddOn({\n                                builder: CloudWatchIamSetupStack.builder(\"cloudwatchDataSourceRole\", context.monitoringEnv.account!),\n                                id: \"cloudwatch-iam-nested-stack\"\n                            }))\n                            .addOns(\n                                prodArgoAddonConfig,\n                            )\n                    },\n                    {\n                        id: MON_ENV_ID,\n                        stackBuilder: <blueprints.StackBuilder>{\n                            build(scope: Construct): cdk.Stack {\n                                return new AmgIamSetupStack(scope, \"amg-iam-setup\", amgIamSetupStackProps);\n                            }\n                        }\n                    },\n                ],\n            })\n            .build(scope, \"multi-account-central-pipeline\", {\n                env: context.pipelineEnv\n            });\n    }\n}\n\nfunction createArgoAddonConfig(environment: string, repoUrl: string): blueprints.ArgoCDAddOn {\n    return new blueprints.ArgoCDAddOn(\n        {\n            bootstrapRepo: {\n                repoUrl: repoUrl,\n                path: `envs/${environment}`,\n                targetRevision: 'main',\n            },\n            bootstrapValues: {\n                spec: {\n                    ingress: {\n                        host: 'teamblueprints.com',\n                    }\n                },\n            },\n        }\n    );\n}"
  },
  {
    "path": "lib/multi-cluster-construct/cluster-secret-store-addon.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from 'constructs';\nimport { dependable } from '@aws-quickstart/eks-blueprints/dist/utils';\n\nexport class ClusterSecretStoreAddon implements blueprints.ClusterAddOn {\n    id?: string | undefined;\n    @dependable(blueprints.addons.ExternalsSecretsAddOn.name)\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n\n        const clusterSecretStore = new eks.KubernetesManifest(clusterInfo.cluster, \"ClusterSecretStore\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"external-secrets.io/v1beta1\",\n                    kind: \"ClusterSecretStore\",\n                    metadata: {name: \"eksa-secret-store\"},\n                    spec: {\n                        provider: {\n                            aws: {\n                                service: \"SecretsManager\",\n                                region: clusterInfo.cluster.stack.region,\n                                auth: {\n                                    jwt: {\n                                        serviceAccountRef: {\n                                            name: \"external-secrets-sa\",\n                                            namespace: \"external-secrets\",\n                                        },\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        });\n        \n        const clusterConfigMapStore = new eks.KubernetesManifest(clusterInfo.cluster, \"ClusterConfigMap\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"external-secrets.io/v1beta1\",\n                    kind: \"ClusterSecretStore\",\n                    metadata: {name: \"eksa-configmap-store\"},\n                    spec: {\n                        provider: {\n                            aws: {\n                                service: \"ParameterStore\",\n                                region: clusterInfo.cluster.stack.region,\n                                auth: {\n                                    jwt: {\n                                        serviceAccountRef: {\n                                            name: \"external-secrets-sa\",\n                                            namespace: \"external-secrets\",\n                                        },\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        });\n\n        clusterConfigMapStore.node.addDependency(clusterSecretStore);\n        return Promise.resolve(clusterSecretStore);\n    }\n}"
  },
  {
    "path": "lib/multi-cluster-construct/clusterMapping.ts",
    "content": "import * as eks from 'aws-cdk-lib/aws-eks';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\n\n/**\n * Instance Mapping for fields such as chart, version, managed IAM policy.\n */\nexport interface InstanceMapping {\n    amiType: eks.NodegroupAmiType,\n    instanceType: ec2.InstanceType,\n}\n/**\n * List of all clusters deployed by conformitron\n */\nexport enum ClusterName {\n  ARM = \"arm\",\n  X86 = \"x86\",\n  BR_X86 = \"br-x86\",\n  BR_ARM = \"br-arm\",\n  MONITORING = \"grafana-monitoring\"\n}\n\n\nexport const clusterMappings : {[key in ClusterName]?: InstanceMapping } = {\n    [ClusterName.ARM]: {\n        amiType: eks.NodegroupAmiType.AL2_ARM_64,\n        instanceType: ec2.InstanceType.of(ec2.InstanceClass.M7G, ec2.InstanceSize.XLARGE2)\n    },\n    [ClusterName.X86]: {\n        amiType: eks.NodegroupAmiType.AL2_X86_64,\n        instanceType: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.XLARGE2)\n    },\n    [ClusterName.BR_ARM]: {\n        amiType: eks.NodegroupAmiType.BOTTLEROCKET_ARM_64,\n        instanceType: ec2.InstanceType.of(ec2.InstanceClass.M7G, ec2.InstanceSize.XLARGE2)\n    },\n    [ClusterName.BR_X86]: {\n        amiType: eks.NodegroupAmiType.BOTTLEROCKET_X86_64,\n        instanceType: ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.XLARGE2)\n    },\n    [ClusterName.MONITORING]: {\n        amiType: eks.NodegroupAmiType.AL2_X86_64,\n        instanceType:  ec2.InstanceType.of(ec2.InstanceClass.M5, ec2.InstanceSize.LARGE)\n    }\n};\n"
  },
  {
    "path": "lib/multi-cluster-construct/grafana-monitor-builder.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from 'aws-cdk-lib/aws-eks';\nimport { GrafanaOperatorSecretAddon } from './grafana-operator-secret-addon';\nimport * as fs from 'fs';\n\nexport class GrafanaMonitoringConstruct {\n\n    build(scope: Construct, id: string, contextAccount?: string, contextRegion?: string ) {\n\n        const stackId = `${id}-grafana-monitor`;\n\n        const account = contextAccount! || process.env.ACCOUNT_ID! || process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = contextRegion! || process.env.AWS_REGION! || process.env.CDK_DEFAULT_REGION!;\n\n        this.create(scope, account, region)\n            .build(scope, stackId);\n    }\n\n    create(scope: Construct, contextAccount?: string, contextRegion?: string ) {\n\n        const account = contextAccount! || process.env.ACCOUNT_ID! || process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = contextRegion! || process.env.AWS_REGION! || process.env.CDK_DEFAULT_REGION!;\n        \n        // TODO: CFN import https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.Fn.html#static-importwbrvaluesharedvaluetoimport\n        const ampWorkspaceName = \"conformitronWorkspace\";\n        const ampEndpoint = blueprints.utils.valueFromContext(scope, \"conformitron.amp.endpoint\", \"https://aps-workspaces.<region>.amazonaws.com/workspaces/<workspace-id>/\");\n        const ampWorkspaceArn = blueprints.utils.valueFromContext(scope, \"conformitron.amp.arn\", \"arn:aws:aps:<region>:<accountid>:workspace/<workspace-id>\");\n        \n        const ampAddOnProps: blueprints.AmpAddOnProps = {\n            ampPrometheusEndpoint: ampEndpoint,\n            ampRules: {\n                ampWorkspaceArn: ampWorkspaceArn,\n                ruleFilePaths: [\n                    __dirname + '/resources/amp-config/alerting-rules.yml',\n                    __dirname + '/resources/amp-config/recording-rules.yml'\n                ]\n            }\n        };\n\n        let doc = blueprints.utils.readYamlDocument(__dirname + '/resources/otel-collector-config.yml');\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableJavaMonJob }}\",\n            \"{{ stop enableJavaMonJob }}\",\n            false\n        );\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableNginxMonJob }}\",\n            \"{{ stop enableNginxMonJob }}\",\n            false\n        );\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableIstioMonJob }}\",\n            \"{{ stop enableIstioMonJob }}\",\n            false\n        );\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableAPIserverJob }}\",\n            \"{{ stop enableAPIserverJob }}\",\n            false\n        );\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableAdotMetricsCollectionJob}}\",\n            \"{{ stop enableAdotMetricsCollectionJob }}\",\n            false\n        );\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableAdotMetricsCollectionTelemetry }}\",\n            \"{{ stop enableAdotMetricsCollectionTelemetry }}\",\n            true\n        );\n\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableAdotContainerLogsReceiver }}\",\n            \"{{ stop enableAdotContainerLogsReceiver }}\",\n            true\n        );\n        doc = blueprints.utils.changeTextBetweenTokens(\n            doc,\n            \"{{ start enableAdotContainerLogsExporter }}\",\n            \"{{ stop enableAdotContainerLogsExporter }}\",\n            true\n        );\n\n        fs.writeFileSync(__dirname + '/resources/otel-collector-config-new.yml', doc);\n\n        ampAddOnProps.openTelemetryCollector = {\n            manifestPath: __dirname + '/resources/otel-collector-config-new.yml',\n            manifestParameterMap: {\n                logGroupName: `/aws/eks/conformitron/workspace`,\n                logStreamName: `$NODE_NAME`,\n                logRetentionDays: 30,\n                awsRegion: region \n            }\n        };\n\n        const fluxRepository: blueprints.FluxGitRepo = blueprints.utils.valueFromContext(scope, \"fluxRepository\", undefined);\n        fluxRepository.values!.AMG_AWS_REGION = region;\n        fluxRepository.values!.AMG_ENDPOINT_URL = blueprints.utils.valueFromContext(scope, \"conformitron.amg.endpoint\",\"https://<grafana-id>.grafana-workspace.<region>.amazonaws.com\"); \n\n        Reflect.defineMetadata(\"ordered\", true, blueprints.addons.GrafanaOperatorAddon); //sets metadata ordered to true for GrafanaOperatorAddon\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.addons.FluxCDAddOn({\"repositories\": [fluxRepository]}),\n            new GrafanaOperatorSecretAddon(),\n            new blueprints.addons.SSMAgentAddOn()\n        ];\n\n        return blueprints.ObservabilityBuilder.builder()\n            .account(account)\n            .region(region)\n            .version(eks.KubernetesVersion.V1_27)\n            .resourceProvider(ampWorkspaceName, new blueprints.CreateAmpProvider(ampWorkspaceName, ampWorkspaceName))\n            .withAmpProps(ampAddOnProps)\n            .enableOpenSourcePatternAddOns()\n            .addOns(\n                ...addOns\n            );\n    }\n}"
  },
  {
    "path": "lib/multi-cluster-construct/grafana-operator-secret-addon.ts",
    "content": "import 'source-map-support/register'; // to get better stack traces and debugging\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport { Construct } from 'constructs';\nimport { dependable } from '@aws-quickstart/eks-blueprints/dist/utils';\n\nexport class GrafanaOperatorSecretAddon implements blueprints.ClusterAddOn {\n    id?: string | undefined;\n    @dependable(blueprints.addons.ExternalsSecretsAddOn.name, blueprints.addons.GrafanaOperatorAddon.name)\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n        const secretStore = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"ClusterSecretStore\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"external-secrets.io/v1beta1\",\n                    kind: \"ClusterSecretStore\",\n                    metadata: {\n                        name: \"ssm-parameter-store\",\n                        namespace: \"default\"\n                    },\n                    spec: {\n                        provider: {\n                            aws: {\n                                service: \"ParameterStore\",\n                                region: clusterInfo.cluster.stack.region,\n                                auth: {\n                                    jwt: {\n                                        serviceAccountRef: {\n                                            name: \"external-secrets-sa\",\n                                            namespace: \"external-secrets\",\n                                        },\n                                    },\n                                },\n                            },\n                        },\n                    },\n                },\n            ],\n        });\n\n        const externalSecret = new eks.KubernetesManifest(clusterInfo.cluster.stack, \"ExternalSecret\", {\n            cluster: cluster,\n            manifest: [\n                {\n                    apiVersion: \"external-secrets.io/v1beta1\",\n                    kind: \"ExternalSecret\",\n                    metadata: {\n                        name: \"external-grafana-admin-credentials\",\n                        namespace: \"grafana-operator\"\n                    },\n                    spec: {\n                        secretStoreRef: {\n                            name: \"ssm-parameter-store\",\n                            kind: \"ClusterSecretStore\",\n                        },\n                        target: {\n                            name: \"grafana-admin-credentials\"\n                        },\n                        data: [\n                            {\n                                secretKey: \"GF_SECURITY_ADMIN_APIKEY\",\n                                remoteRef: {\n                                    key: \"/grafana-api-key\"\n                                },\n                            },\n                        ],\n                    },\n                },\n            ],\n        });\n        externalSecret.node.addDependency(secretStore);\n        return Promise.resolve(secretStore);\n    }\n}"
  },
  {
    "path": "lib/multi-cluster-construct/multi-cluster-builder.ts",
    "content": "import { Construct } from 'constructs';\n\n// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { ClusterSecretStoreAddon } from './cluster-secret-store-addon';\n\n\nexport default class MultiClusterBuilderConstruct {\n    build(scope: Construct, id: string, account?: string, region?: string ) {\n        // Setup platform team\n        const accountID = account ?? process.env.CDK_DEFAULT_ACCOUNT! ;\n        const awsRegion =  region ?? process.env.CDK_DEFAULT_REGION! ;\n \n        const stackID = `${id}-blueprint`;\n        this.create(scope, accountID, awsRegion)\n            .build(scope, stackID);\n    }\n    \n\n    create(scope: Construct, account?: string, region?: string ) {\n        // Setup platform team\n        const accountID = account ?? process.env.CDK_DEFAULT_ACCOUNT! ;\n        const awsRegion =  region ?? process.env.CDK_DEFAULT_REGION! ;\n        \n        const ampEndpoint = blueprints.utils.valueFromContext(scope, \"conformitron.amp.endpoint\", \"https://aps-workspaces.<region>.amazonaws.com/workspaces/<workspace-id>/\");\n\n        const ampAddOnProps: blueprints.AmpAddOnProps = {\n            ampPrometheusEndpoint: ampEndpoint,\n        };\n\n        ampAddOnProps.openTelemetryCollector = {\n            manifestPath: __dirname + '/resources/otel-collector-config-new.yml',\n            manifestParameterMap: {\n                logGroupName: `/aws/eks/conformitron/cluster`,\n                logStreamName: `$NODE_NAME`,\n                logRetentionDays: 30,\n                awsRegion: region \n            }\n        };\n        \n        \n\n        return blueprints.ObservabilityBuilder.builder()\n            .account(accountID)\n            .region(awsRegion)\n            .withAmpProps(ampAddOnProps)\n            .enableOpenSourcePatternAddOns()\n            .addOns(\n                new blueprints.addons.FluxCDAddOn({\n                    repositories:[{\n                        name: \"eks-cloud-addons-conformance\",\n                        namespace: \"flux-system\",\n                        repository: {\n                            repoUrl: 'https://github.com/aws-samples/eks-anywhere-addons',\n                            targetRevision: \"main\",\n                        },\n                        values: {\n                        },\n                        kustomizations: [\n                            {kustomizationPath: \"./eks-anywhere-common/Addons/Core/Botkube\"},\n                            {kustomizationPath: \"./eks-anywhere-common/Addons/Core/Kube-Observer\"},\n                            {kustomizationPath: \"./eks-anywhere-common/Testers/\"},\n                            {kustomizationPath: \"./eks-cloud/Testers\"},\n                            {kustomizationPath: \"./eks-anywhere-common/Addons/Partner\"}, \n                            {kustomizationPath: \"./eks-cloud/Partner\"}, \n                        ],\n                    }],\n                }),\n                new ClusterSecretStoreAddon(),\n                new blueprints.addons.EbsCsiDriverAddOn(),\n                new blueprints.addons.ClusterAutoScalerAddOn()\n            );\n    }\n}\n\n\n"
  },
  {
    "path": "lib/multi-cluster-construct/pipeline.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as eks from 'aws-cdk-lib/aws-eks';\nimport * as ec2 from 'aws-cdk-lib/aws-ec2';\nimport { Construct } from 'constructs';\nimport MultiClusterBuilderConstruct from './multi-cluster-builder';\nimport { GrafanaMonitoringConstruct } from './grafana-monitor-builder';\nimport { ClusterName, clusterMappings } from './clusterMapping';\n\n/**\n * Main multi-cluster deployment pipeline.\n */\nexport class PipelineMultiCluster {\n\n    async buildAsync(scope: Construct) {\n        const accountID = process.env.ACCOUNT_ID! || process.env.CDK_DEFAULT_ACCOUNT! ;\n        const region = process.env.AWS_REGION! || process.env.CDK_DEFAULT_REGION!;\n\n        const versions = blueprints.utils.valueFromContext(scope, \"conformitron.versions\", [\"1.28\",\"1.29\",\"1.30\"]);\n\n        const CLUSTER_VERSIONS = versions.map((v: string) => eks.KubernetesVersion.of(v));\n\n        // Stages in codepipeline\n        const stages : blueprints.StackStage[] = [];\n\n        const blueprintGrafanaConstruct = new GrafanaMonitoringConstruct();\n        const blueprintGrafana = blueprintGrafanaConstruct.create(scope, accountID, region);\n\n        stages.push({\n            id: ClusterName.MONITORING,\n            stackBuilder: blueprintGrafana\n                .clone(region, accountID)\n        });\n\n        /* TODO: Seperate region for clusters than infra account region, \n           trust policy is created when pipeline is bootstrapped.\n           It will be helpful for enterprise customers.\n           Similar to approach in multi-region-construct pattern\n        */\n\n        let clusterProps: blueprints.MngClusterProviderProps;\n\n        for(const version of CLUSTER_VERSIONS) {\n            const blueprintBuilderX86 = new MultiClusterBuilderConstruct().create(scope, accountID, region);\n            \n            clusterProps = this.buildClusterProps(\n                clusterMappings[ClusterName.X86]!.amiType,\n                clusterMappings[ClusterName.X86]!.instanceType\n            );\n            \n            const blueprintX86 = blueprintBuilderX86\n                .version(version)\n                .clusterProvider(new blueprints.MngClusterProvider(clusterProps))\n                .useDefaultSecretEncryption(true);\n    \n            stages.push({\n                id: ClusterName.X86 + \"-\" + version.version.replace(\".\", \"-\"),\n                stackBuilder : blueprintX86.clone(region)\n            });\n\n            const blueprintBuilderArm = new MultiClusterBuilderConstruct().create(scope, accountID, region);\n            clusterProps = this.buildClusterProps(\n                clusterMappings[ClusterName.ARM]!.amiType,\n                clusterMappings[ClusterName.ARM]!.instanceType\n            );\n            const blueprintARM = blueprintBuilderArm\n                .version(version)\n                .clusterProvider(new blueprints.MngClusterProvider(clusterProps))\n                .useDefaultSecretEncryption(true);\n                        \n            stages.push({\n                id: ClusterName.ARM  + \"-\" + version.version.replace(\".\", \"-\"),\n                stackBuilder : blueprintARM.clone(region)\n            });\n        }\n\n        // Only deploy lates kube version on BR Clusters\n        const LATEST_VERSION = CLUSTER_VERSIONS.at(CLUSTER_VERSIONS.length-1)!;\n    \n        const blueprintBuilderBrX86= new MultiClusterBuilderConstruct().create(scope, accountID, region);\n\n        clusterProps = this.buildClusterProps(\n            clusterMappings[ClusterName.BR_X86]!.amiType,\n            clusterMappings[ClusterName.BR_X86]!.instanceType\n        );\n\n        const blueprintBrX86 = blueprintBuilderBrX86\n            .version(LATEST_VERSION)\n            .clusterProvider(new blueprints.MngClusterProvider(clusterProps))\n            .useDefaultSecretEncryption(true);\n    \n        stages.push({\n            id: ClusterName.BR_X86 + \"-\" + LATEST_VERSION.version.replace(\".\", \"-\"),\n            stackBuilder : blueprintBrX86.clone(region)\n        });\n\n        const blueprintBuilderBrArm = new MultiClusterBuilderConstruct().create(scope, accountID, region);\n        \n        clusterProps = this.buildClusterProps(\n            clusterMappings[ClusterName.BR_ARM]!.amiType,\n            clusterMappings[ClusterName.BR_ARM]!.instanceType\n        );\n\n        const blueprintBottleRocketArm = blueprintBuilderBrArm\n            .version(LATEST_VERSION)\n            .clusterProvider(new blueprints.MngClusterProvider(clusterProps))\n            .useDefaultSecretEncryption(true);\n    \n        stages.push({\n            id: ClusterName.BR_ARM + \"-\" + LATEST_VERSION.version.replace(\".\", \"-\"),\n            stackBuilder : blueprintBottleRocketArm.clone(region)\n        });\n\n        const gitOwner = 'Howlla';\n        const gitRepositoryName = 'cdk-eks-blueprints-patterns';\n\n        blueprints.CodePipelineStack.builder()\n            .application('npx ts-node bin/multi-cluster-conformitron.ts')\n            .name('multi-cluster-central-pipeline')\n            .owner(gitOwner)\n            .codeBuildPolicies(blueprints.DEFAULT_BUILD_POLICIES)\n            .repository({\n                repoUrl: gitRepositoryName,\n                credentialsSecretName: 'github-token',\n                targetRevision: 'conformitronPipeline',\n                trigger: blueprints.GitHubTrigger.POLL\n            })\n            .wave({\n                id: \"prod-test\",\n                stages\n            })\n            .build(scope, \"multi-cluster-central-pipeline\", {\n                env: {\n                    account: process.env.CDK_DEFAULT_ACCOUNT,\n                    region: region,\n                }\n            });\n    }\n    buildClusterProps(amiType:eks.NodegroupAmiType,instanceType:ec2.InstanceType) : blueprints.MngClusterProviderProps{\n        let clusterProps : blueprints.MngClusterProviderProps = {\n            maxSize : 2,\n            minSize : 1,\n            desiredSize: 1,\n            diskSize: 100,\n            amiType: amiType,\n            instanceTypes:[instanceType]\n        };\n        return clusterProps;\n    }\n}\n"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/alerting-rules.yml",
    "content": "groups:\n  - name: infra-alerts-01\n    rules:\n      - alert: NodeNetworkInterfaceFlapping\n        expr: changes(node_network_up{device!~\"veth.+\",job=\"node-exporter\"}[2m]) > 2\n        for: 2m\n        labels:\n          severity: warning\n        annotations:\n          description: Network interface \"{{ $labels.device }}\" changing its up status often on node-exporter {{ $labels.namespace }}/{{ $labels.pod }}\n          summary: Network interface is often changing its status\n      - alert: NodeFilesystemSpaceFillingUp\n        expr: (node_filesystem_avail_bytes{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_size_bytes{fstype!=\"\",job=\"node-exporter\"} * 100 < 15 and predict_linear(node_filesystem_avail_bytes{fstype!=\"\",job=\"node-exporter\"}[6h], 24 * 60 * 60) < 0 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available space left and is filling up.\n          summary: Filesystem is predicted to run out of space within the next 24 hours.\n      - alert: NodeFilesystemSpaceFillingUp\n        expr: (node_filesystem_avail_bytes{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_size_bytes{fstype!=\"\",job=\"node-exporter\"} * 100 < 10 and predict_linear(node_filesystem_avail_bytes{fstype!=\"\",job=\"node-exporter\"}[6h], 4 * 60 * 60) < 0 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 1h\n        labels:\n          severity: critical\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available space left and is filling up fast.\n          summary: Filesystem is predicted to run out of space within the next 4 hours.\n      - alert: NodeFilesystemAlmostOutOfSpace\n        expr: (node_filesystem_avail_bytes{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_size_bytes{fstype!=\"\",job=\"node-exporter\"} * 100 < 3 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 30m\n        labels:\n          severity: warning\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available space left.\n          summary: Filesystem has less than 3% space left.\n      - alert: NodeFilesystemAlmostOutOfSpace\n        expr: (node_filesystem_avail_bytes{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_size_bytes{fstype!=\"\",job=\"node-exporter\"} * 100 < 5 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 30m\n        labels:\n          severity: critical\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available space left.\n          summary: Filesystem has less than 5% space left.\n      - alert: NodeFilesystemFilesFillingUp\n        expr: (node_filesystem_files_free{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_files{fstype!=\"\",job=\"node-exporter\"} * 100 < 40 and predict_linear(node_filesystem_files_free{fstype!=\"\",job=\"node-exporter\"}[6h], 24 * 60 * 60) < 0 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available inodes left and is filling up.\n          summary: Filesystem is predicted to run out of inodes within the next 24 hours.\n      - alert: NodeFilesystemFilesFillingUp\n        expr: (node_filesystem_files_free{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_files{fstype!=\"\",job=\"node-exporter\"} * 100 < 20 and predict_linear(node_filesystem_files_free{fstype!=\"\",job=\"node-exporter\"}[6h], 4 * 60 * 60) < 0 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 1h\n        labels:\n          severity: critical\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available inodes left and is filling up fast.\n          summary: Filesystem is predicted to run out of inodes within the next 4 hours.\n      - alert: NodeFilesystemAlmostOutOfFiles\n        expr: (node_filesystem_files_free{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_files{fstype!=\"\",job=\"node-exporter\"} * 100 < 5 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available inodes left.\n          summary: Filesystem has less than 5% inodes left.\n      - alert: NodeFilesystemAlmostOutOfFiles\n        expr: (node_filesystem_files_free{fstype!=\"\",job=\"node-exporter\"} / node_filesystem_files{fstype!=\"\",job=\"node-exporter\"} * 100 < 3 and node_filesystem_readonly{fstype!=\"\",job=\"node-exporter\"} == 0)\n        for: 1h\n        labels:\n          severity: critical\n        annotations:\n          description: Filesystem on {{ $labels.device }} at {{ $labels.instance }} has only {{ printf \"%.2f\" $value }}% available inodes left.\n          summary: Filesystem has less than 3% inodes left.\n      - alert: NodeNetworkReceiveErrs\n        expr: rate(node_network_receive_errs_total[2m]) / rate(node_network_receive_packets_total[2m]) > 0.01\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $labels.instance }} interface {{ $labels.device }} has encountered {{ printf \"%.0f\" $value }} receive errors in the last two minutes.\n          summary: Network interface is reporting many receive errors.\n      - alert: NodeNetworkTransmitErrs\n        expr: rate(node_network_transmit_errs_total[2m]) / rate(node_network_transmit_packets_total[2m]) > 0.01\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $labels.instance }} interface {{ $labels.device }} has encountered {{ printf \"%.0f\" $value }} transmit errors in the last two minutes.\n          summary: Network interface is reporting many transmit errors.\n      - alert: NodeHighNumberConntrackEntriesUsed\n        expr: (node_nf_conntrack_entries / node_nf_conntrack_entries_limit) > 0.75\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $value | humanizePercentage }} of conntrack entries are used.\n          summary: Number of conntrack are getting close to the limit.\n      - alert: NodeTextFileCollectorScrapeError\n        expr: node_textfile_scrape_error{job=\"node-exporter\"} == 1\n        labels:\n          severity: warning\n        annotations:\n          description: Node Exporter text file collector failed to scrape.\n          summary: Node Exporter text file collector failed to scrape.\n      - alert: NodeClockSkewDetected\n        expr: (node_timex_offset_seconds > 0.05 and deriv(node_timex_offset_seconds[5m]) >= 0) or (node_timex_offset_seconds < -0.05 and deriv(node_timex_offset_seconds[5m]) <= 0)\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          description: Clock on {{ $labels.instance }} is out of sync by more than 300s. Ensure NTP is configured correctly on this host.\n          summary: Clock skew detected.\n      - alert: NodeClockNotSynchronising\n        expr: min_over_time(node_timex_sync_status[5m]) == 0 and node_timex_maxerror_seconds >= 16\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          description: Clock on {{ $labels.instance }} is not synchronising. Ensure NTP is configured on this host.\n          summary: Clock not synchronising.\n      - alert: NodeRAIDDegraded\n        expr: node_md_disks_required - ignoring(state) (node_md_disks{state=\"active\"}) > 0\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: RAID array '{{ $labels.device }}' on {{ $labels.instance }} is in degraded state due to one or more disks failures. Number of spare drives is insufficient to fix issue automatically.\n          summary: RAID Array is degraded\n      - alert: NodeRAIDDiskFailure\n        expr: node_md_disks{state=\"failed\"} > 0\n        labels:\n          severity: warning\n        annotations:\n          description: At least one device in RAID array on {{ $labels.instance }} failed. Array '{{ $labels.device }}' needs attention and possibly a disk swap.\n          summary: Failed device in RAID array\n      - alert: NodeFileDescriptorLimit\n        expr: (node_filefd_allocated{job=\"node-exporter\"} * 100 / node_filefd_maximum{job=\"node-exporter\"} > 70)\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: File descriptors limit at {{ $labels.instance }} is currently at {{ printf \"%.2f\" $value }}%.\n          summary: Kernel is predicted to exhaust file descriptors limit soon.\n      - alert: NodeFileDescriptorLimit\n        expr: (node_filefd_allocated{job=\"node-exporter\"} * 100 / node_filefd_maximum{job=\"node-exporter\"} > 90)\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: File descriptors limit at {{ $labels.instance }} is currently at {{ printf \"%.2f\" $value }}%.\n          summary: Kernel is predicted to exhaust file descriptors limit soon.\n  - name: infra-alerts-02\n    rules:\n      - alert: KubeNodeNotReady\n        expr: kube_node_status_condition{condition=\"Ready\",job=\"kube-state-metrics\",status=\"true\"} == 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $labels.node }} has been unready for more than 15 minutes.\n          summary: Node is not ready.\n      - alert: KubeNodeUnreachable\n        expr: (kube_node_spec_taint{effect=\"NoSchedule\",job=\"kube-state-metrics\",key=\"node.kubernetes.io/unreachable\"} unless ignoring(key, value) kube_node_spec_taint{job=\"kube-state-metrics\",key=~\"ToBeDeletedByClusterAutoscaler|cloud.google.com/impending-node-termination|aws-node-termination-handler/spot-itn\"}) == 1\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $labels.node }} is unreachable and some workloads may be rescheduled.\n          summary: Node is unreachable.\n      - alert: KubeletTooManyPods\n        expr: count by(cluster, node) ((kube_pod_status_phase{job=\"kube-state-metrics\",phase=\"Running\"} == 1) * on(instance, pod, namespace, cluster) group_left(node) topk by(instance, pod, namespace, cluster) (1, kube_pod_info{job=\"kube-state-metrics\"})) / max by(cluster, node) (kube_node_status_capacity{job=\"kube-state-metrics\",resource=\"pods\"} != 1) > 0.95\n        for: 15m\n        labels:\n          severity: info\n        annotations:\n          description: Kubelet '{{ $labels.node }}' is running at {{ $value | humanizePercentage }} of its Pod capacity.\n          summary: Kubelet is running at capacity.\n      - alert: KubeNodeReadinessFlapping\n        expr: sum by(cluster, node) (changes(kube_node_status_condition{condition=\"Ready\",status=\"true\"}[15m])) > 2\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: The readiness status of node {{ $labels.node }} has changed {{ $value }} times in the last 15 minutes.\n          summary: Node readiness status is flapping.\n      - alert: KubeletPlegDurationHigh\n        expr: node_quantile:kubelet_pleg_relist_duration_seconds:histogram_quantile{quantile=\"0.99\"} >= 10\n        for: 5m\n        labels:\n          severity: warning\n        annotations:\n          description: The Kubelet Pod Lifecycle Event Generator has a 99th percentile duration of {{ $value }} seconds on node {{ $labels.node }}.\n          summary: Kubelet Pod Lifecycle Event Generator is taking too long to relist.\n      - alert: KubeletPodStartUpLatencyHigh\n        expr: histogram_quantile(0.99, sum by(cluster, instance, le) (rate(kubelet_pod_worker_duration_seconds_bucket{job=\"kubelet\"}[5m]))) * on(cluster, instance) group_left(node) kubelet_node_name{job=\"kubelet\"} > 60\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Kubelet Pod startup 99th percentile latency is {{ $value }} seconds on node {{ $labels.node }}.\n          summary: Kubelet Pod startup latency is too high.\n      - alert: KubeletClientCertificateExpiration\n        expr: kubelet_certificate_manager_client_ttl_seconds < 604800\n        labels:\n          severity: warning\n        annotations:\n          description: Client certificate for Kubelet on node {{ $labels.node }} expires in {{ $value | humanizeDuration }}.\n          summary: Kubelet client certificate is about to expire.\n      - alert: KubeletClientCertificateExpiration\n        expr: kubelet_certificate_manager_client_ttl_seconds < 86400\n        labels:\n          severity: critical\n        annotations:\n          description: Client certificate for Kubelet on node {{ $labels.node }} expires in {{ $value | humanizeDuration }}.\n          summary: Kubelet client certificate is about to expire.\n      - alert: KubeletServerCertificateExpiration\n        expr: kubelet_certificate_manager_server_ttl_seconds < 604800\n        labels:\n          severity: warning\n        annotations:\n          description: Server certificate for Kubelet on node {{ $labels.node }} expires in {{ $value | humanizeDuration }}.\n          summary: Kubelet server certificate is about to expire.\n      - alert: KubeletServerCertificateExpiration\n        expr: kubelet_certificate_manager_server_ttl_seconds < 86400\n        labels:\n          severity: critical\n        annotations:\n          description: Server certificate for Kubelet on node {{ $labels.node }} expires in {{ $value | humanizeDuration }}.\n          summary: Kubelet server certificate is about to expire.\n      - alert: KubeletClientCertificateRenewalErrors\n        expr: increase(kubelet_certificate_manager_client_expiration_renew_errors[5m]) > 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Kubelet on node {{ $labels.node }} has failed to renew its client certificate ({{ $value | humanize }} errors in the last 5 minutes).\n          summary: Kubelet has failed to renew its client certificate.\n      - alert: KubeletServerCertificateRenewalErrors\n        expr: increase(kubelet_server_expiration_renew_errors[5m]) > 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Kubelet on node {{ $labels.node }} has failed to renew its server certificate ({{ $value | humanize }} errors in the last 5 minutes).\n          summary: Kubelet has failed to renew its server certificate.\n      - alert: KubeletDown\n        expr: absent(up{job=\"kubelet\"} == 1)\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: Kubelet has disappeared from Prometheus target discovery.\n          summary: Target disappeared from Prometheus target discovery.\n      - alert: KubeVersionMismatch\n        expr: count by(cluster) (count by(git_version, cluster) (label_replace(kubernetes_build_info{job!~\"kube-dns|coredns\"}, \"git_version\", \"$1\", \"git_version\", \"(v[0-9]*.[0-9]*).*\"))) > 1\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: There are {{ $value }} different semantic versions of Kubernetes components running.\n          summary: Different semantic versions of Kubernetes components running.\n      - alert: KubeClientErrors\n        expr: (sum by(cluster, instance, job, namespace) (rate(rest_client_requests_total{code=~\"5..\"}[5m])) / sum by(cluster, instance, job, namespace) (rate(rest_client_requests_total[5m]))) > 0.01\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Kubernetes API server client '{{ $labels.job }}/{{ $labels.instance }}' is experiencing {{ $value | humanizePercentage }} errors.'\n          summary: Kubernetes API server client is experiencing errors.\n      - alert: KubeClientCertificateExpiration\n        expr: apiserver_client_certificate_expiration_seconds_count{job=\"apiserver\"} > 0 and on(job) histogram_quantile(0.01, sum by(job, le) (rate(apiserver_client_certificate_expiration_seconds_bucket{job=\"apiserver\"}[5m]))) < 604800\n        labels:\n          severity: warning\n        annotations:\n          description: A client certificate used to authenticate to kubernetes apiserver is expiring in less than 7.0 days.\n          summary: Client certificate is about to expire.\n      - alert: KubeClientCertificateExpiration\n        expr: apiserver_client_certificate_expiration_seconds_count{job=\"apiserver\"} > 0 and on(job) histogram_quantile(0.01, sum by(job, le) (rate(apiserver_client_certificate_expiration_seconds_bucket{job=\"apiserver\"}[5m]))) < 86400\n        labels:\n          severity: critical\n        annotations:\n          description: A client certificate used to authenticate to kubernetes apiserver is expiring in less than 24.0 hours.\n          summary: Client certificate is about to expire.\n      - alert: KubeAggregatedAPIErrors\n        expr: sum by(name, namespace, cluster) (increase(aggregator_unavailable_apiservice_total[10m])) > 4\n        labels:\n          severity: warning\n        annotations:\n          description: Kubernetes aggregated API {{ $labels.name }}/{{ $labels.namespace }} has reported errors. It has appeared unavailable {{ $value | humanize }} times averaged over the past 10m.\n          summary: Kubernetes aggregated API has reported errors.\n  - name: infra-alerts-03\n    rules:\n      - alert: KubeAggregatedAPIDown\n        expr: (1 - max by(name, namespace, cluster) (avg_over_time(aggregator_unavailable_apiservice[10m]))) * 100 < 85\n        for: 5m\n        labels:\n          severity: warning\n        annotations:\n          description: Kubernetes aggregated API {{ $labels.name }}/{{ $labels.namespace }} has been only {{ $value | humanize }}% available over the last 10m.\n          summary: Kubernetes aggregated API is down.\n      - alert: KubeAPIDown\n        expr: absent(up{job=\"kube-admin\"} == 1)\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: KubeAPI has disappeared from Prometheus target discovery.\n          summary: Target disappeared from Prometheus target discovery.\n      - alert: KubeAPITerminatedRequests\n        expr: sum(rate(apiserver_request_terminations_total{job=\"apiserver\"}[10m])) / (sum(rate(apiserver_request_total{job=\"apiserver\"}[10m])) + sum(rate(apiserver_request_terminations_total{job=\"apiserver\"}[10m]))) > 0.2\n        for: 5m\n        labels:\n          severity: warning\n        annotations:\n          description: The kubernetes apiserver has terminated {{ $value | humanizePercentage }} of its incoming requests.\n          summary: The kubernetes apiserver has terminated {{ $value | humanizePercentage }} of its incoming requests.\n      - alert: KubePersistentVolumeFillingUp\n        expr: (kubelet_volume_stats_available_bytes{job=\"kubelet\",namespace=~\".*\"} / kubelet_volume_stats_capacity_bytes{job=\"kubelet\",namespace=~\".*\"}) < 0.03 and kubelet_volume_stats_used_bytes{job=\"kubelet\",namespace=~\".*\"} > 0 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_access_mode{access_mode=\"ReadOnlyMany\"} == 1 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_labels{label_excluded_from_alerts=\"true\"} == 1\n        for: 1m\n        labels:\n          severity: critical\n        annotations:\n          description: The PersistentVolume claimed by {{ $labels.persistentvolumeclaim }} in Namespace {{ $labels.namespace }} is only {{ $value | humanizePercentage }} free.\n          summary: PersistentVolume is filling up.\n      - alert: KubePersistentVolumeFillingUp\n        expr: (kubelet_volume_stats_available_bytes{job=\"kubelet\",namespace=~\".*\"} / kubelet_volume_stats_capacity_bytes{job=\"kubelet\",namespace=~\".*\"}) < 0.15 and kubelet_volume_stats_used_bytes{job=\"kubelet\",namespace=~\".*\"} > 0 and predict_linear(kubelet_volume_stats_available_bytes{job=\"kubelet\",namespace=~\".*\"}[6h], 4 * 24 * 3600) < 0 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_access_mode{access_mode=\"ReadOnlyMany\"} == 1 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_labels{label_excluded_from_alerts=\"true\"} == 1\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: Based on recent sampling, the PersistentVolume claimed by {{ $labels.persistentvolumeclaim }} in Namespace {{ $labels.namespace }} is expected to fill up within four days.\n          summary: PersistentVolume is filling up.\n      - alert: KubePersistentVolumeInodesFillingUp\n        expr: (kubelet_volume_stats_inodes_free{job=\"kubelet\",namespace=~\".*\"} / kubelet_volume_stats_inodes{job=\"kubelet\",namespace=~\".*\"}) < 0.03 and kubelet_volume_stats_inodes_used{job=\"kubelet\",namespace=~\".*\"} > 0 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_access_mode{access_mode=\"ReadOnlyMany\"} == 1 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_labels{label_excluded_from_alerts=\"true\"} == 1\n        for: 1m\n        labels:\n          severity: critical\n        annotations:\n          description: The PersistentVolume claimed by {{ $labels.persistentvolumeclaim }} in Namespace {{ $labels.namespace }} only has {{ $value | humanizePercentage }} free inodes.\n          summary: PersistentVolumeInodes is filling up.\n      - alert: KubePersistentVolumeInodesFillingUp\n        expr: (kubelet_volume_stats_inodes_free{job=\"kubelet\",namespace=~\".*\"} / kubelet_volume_stats_inodes{job=\"kubelet\",namespace=~\".*\"}) < 0.15 and kubelet_volume_stats_inodes_used{job=\"kubelet\",namespace=~\".*\"} > 0 and predict_linear(kubelet_volume_stats_inodes_free{job=\"kubelet\",namespace=~\".*\"}[6h], 4 * 24 * 3600) < 0 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_access_mode{access_mode=\"ReadOnlyMany\"} == 1 unless on(namespace, persistentvolumeclaim) kube_persistentvolumeclaim_labels{label_excluded_from_alerts=\"true\"} == 1\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: Based on recent sampling, the PersistentVolume claimed by {{ $labels.persistentvolumeclaim }} in Namespace {{ $labels.namespace }} is expected to run out of inodes within four days. Currently {{ $value | humanizePercentage }} of its inodes are free.\n          summary: PersistentVolumeInodes are filling up.\n      - alert: KubePersistentVolumeErrors\n        expr: kube_persistentvolume_status_phase{job=\"kube-state-metrics\",phase=~\"Failed|Pending\"} > 0\n        for: 5m\n        labels:\n          severity: critical\n        annotations:\n          description: The persistent volume {{ $labels.persistentvolume }} has status {{ $labels.phase }}.\n          summary: PersistentVolume is having issues with provisioning.\n      - alert: KubeCPUOvercommit\n        expr: sum(namespace_cpu:kube_pod_container_resource_requests:sum) - (sum(kube_node_status_allocatable{resource=\"cpu\"}) - max(kube_node_status_allocatable{resource=\"cpu\"})) > 0 and (sum(kube_node_status_allocatable{resource=\"cpu\"}) - max(kube_node_status_allocatable{resource=\"cpu\"})) > 0\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          description: Cluster has overcommitted CPU resource requests for Pods by {{ $value }} CPU shares and cannot tolerate node failure.\n          summary: Cluster has overcommitted CPU resource requests.\n      - alert: KubeMemoryOvercommit\n        expr: sum(namespace_memory:kube_pod_container_resource_requests:sum) - (sum(kube_node_status_allocatable{resource=\"memory\"}) - max(kube_node_status_allocatable{resource=\"memory\"})) > 0 and (sum(kube_node_status_allocatable{resource=\"memory\"}) - max(kube_node_status_allocatable{resource=\"memory\"})) > 0\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          description: Cluster has overcommitted memory resource requests for Pods by {{ $value | humanize }} bytes and cannot tolerate node failure.\n          summary: Cluster has overcommitted memory resource requests.\n      - alert: KubeCPUQuotaOvercommit\n        expr: sum(min without(resource) (kube_resourcequota{job=\"kube-state-metrics\",resource=~\"(cpu|requests.cpu)\",type=\"hard\"})) / sum(kube_node_status_allocatable{job=\"kube-state-metrics\",resource=\"cpu\"}) > 1.5\n        for: 5m\n        labels:\n          severity: warning\n        annotations:\n          description: Cluster has overcommitted CPU resource requests for Namespaces.\n          summary: Cluster has overcommitted CPU resource requests.\n      - alert: KubeMemoryQuotaOvercommit\n        expr: sum(min without(resource) (kube_resourcequota{job=\"kube-state-metrics\",resource=~\"(memory|requests.memory)\",type=\"hard\"})) / sum(kube_node_status_allocatable{job=\"kube-state-metrics\",resource=\"memory\"}) > 1.5\n        for: 5m\n        labels:\n          severity: warning\n        annotations:\n          description: Cluster has overcommitted memory resource requests for Namespaces.\n          summary: Cluster has overcommitted memory resource requests.\n      - alert: KubeQuotaAlmostFull\n        expr: kube_resourcequota{job=\"kube-state-metrics\",type=\"used\"} / ignoring(instance, job, type) (kube_resourcequota{job=\"kube-state-metrics\",type=\"hard\"} > 0) > 0.9 < 1\n        for: 15m\n        labels:\n          severity: info\n        annotations:\n          description: Namespace {{ $labels.namespace }} is using {{ $value | humanizePercentage }} of its {{ $labels.resource }} quota.\n          summary: Namespace quota is going to be full.\n      - alert: KubeQuotaFullyUsed\n        expr: kube_resourcequota{job=\"kube-state-metrics\",type=\"used\"} / ignoring(instance, job, type) (kube_resourcequota{job=\"kube-state-metrics\",type=\"hard\"} > 0) == 1\n        for: 15m\n        labels:\n          severity: info\n        annotations:\n          description: Namespace {{ $labels.namespace }} is using {{ $value | humanizePercentage }} of its {{ $labels.resource }} quota.\n          summary: Namespace quota is fully used.\n      - alert: KubeQuotaExceeded\n        expr: kube_resourcequota{job=\"kube-state-metrics\",type=\"used\"} / ignoring(instance, job, type) (kube_resourcequota{job=\"kube-state-metrics\",type=\"hard\"} > 0) > 1\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Namespace {{ $labels.namespace }} is using {{ $value | humanizePercentage }} of its {{ $labels.resource }} quota.\n          summary: Namespace quota has exceeded the limits.\n      - alert: CPUThrottlingHigh\n        expr: sum by(container, pod, namespace) (increase(container_cpu_cfs_throttled_periods_total{container!=\"\"}[5m])) / sum by(container, pod, namespace) (increase(container_cpu_cfs_periods_total[5m])) > (25 / 100)\n        for: 15m\n        labels:\n          severity: info\n        annotations:\n          description: The {{ $value | humanizePercentage }} throttling of CPU in namespace {{ $labels.namespace }} for container {{ $labels.container }} in pod {{ $labels.pod }}.\n          summary: Processes experience elevated CPU throttling.\n      - alert: KubePodCrashLooping\n        expr: max_over_time(kube_pod_container_status_waiting_reason{job=\"kube-state-metrics\",namespace=~\".*\",reason=\"CrashLoopBackOff\"}[5m]) >= 1\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Pod {{ $labels.namespace }}/{{ $labels.pod }} ({{ $labels.container }}) is in waiting state (reason:\"CrashLoopBackOff\").\n          summary: Pod is crash looping.\n      - alert: KubePodNotReady\n        expr: sum by(namespace, pod, cluster) (max by(namespace, pod, cluster) (kube_pod_status_phase{job=\"kube-state-metrics\",namespace=~\".*\",phase=~\"Pending|Unknown\"}) * on(namespace, pod, cluster) group_left(owner_kind) topk by(namespace, pod, cluster) (1, max by(namespace, pod, owner_kind, cluster) (kube_pod_owner{owner_kind!=\"Job\"}))) > 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Pod {{ $labels.namespace }}/{{ $labels.pod }} ({{ $labels.container }}) has been in a non-ready state for longer than 15 minutes.\n          summary: Pod has been in a non-ready state for more than 15 minutes.\n      - alert: KubeDeploymentGenerationMismatch\n        expr: kube_deployment_status_observed_generation{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_deployment_metadata_generation{job=\"kube-state-metrics\",namespace=~\".*\"}\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Deployment generation for {{ $labels.namespace }}/{{ $labels.deployment }} does not match, this indicates that the Deployment has failed but has not been rolled back.\n          summary: Deployment generation mismatch due to possible roll-back\n      - alert: KubeDeploymentReplicasMismatch\n        expr: (kube_deployment_spec_replicas{job=\"kube-state-metrics\",namespace=~\".*\"} > kube_deployment_status_replicas_available{job=\"kube-state-metrics\",namespace=~\".*\"}) and (changes(kube_deployment_status_replicas_updated{job=\"kube-state-metrics\",namespace=~\".*\"}[10m]) == 0)\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Deployment {{ $labels.namespace }}/{{ $labels.deployment }} has not matched the expected number of replicas for longer than 15 minutes.\n          summary: Deployment has not matched the expected number of replicas.\n  - name: infra-alerts-04\n    rules:\n      - alert: KubeStatefulSetReplicasMismatch\n        expr: (kube_statefulset_status_replicas_ready{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_statefulset_status_replicas{job=\"kube-state-metrics\",namespace=~\".*\"}) and (changes(kube_statefulset_status_replicas_updated{job=\"kube-state-metrics\",namespace=~\".*\"}[10m]) == 0)\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: StatefulSet {{ $labels.namespace }}/{{ $labels.statefulset }} has not matched the expected number of replicas for longer than 15 minutes.\n          summary: Deployment has not matched the expected number of replicas.\n      - alert: KubeStatefulSetGenerationMismatch\n        expr: kube_statefulset_status_observed_generation{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_statefulset_metadata_generation{job=\"kube-state-metrics\",namespace=~\".*\"}\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: StatefulSet generation for {{ $labels.namespace }}/{{ $labels.statefulset }} does not match, this indicates that the StatefulSet has failed but has not been rolled back.\n          summary: StatefulSet generation mismatch due to possible roll-back\n      - alert: KubeStatefulSetUpdateNotRolledOut\n        expr: (max without(revision) (kube_statefulset_status_current_revision{job=\"kube-state-metrics\",namespace=~\".*\"} unless kube_statefulset_status_update_revision{job=\"kube-state-metrics\",namespace=~\".*\"}) * (kube_statefulset_replicas{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_statefulset_status_replicas_updated{job=\"kube-state-metrics\",namespace=~\".*\"})) and (changes(kube_statefulset_status_replicas_updated{job=\"kube-state-metrics\",namespace=~\".*\"}[5m]) == 0)\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: StatefulSet {{ $labels.namespace }}/{{ $labels.statefulset }} update has not been rolled out.\n          summary: StatefulSet update has not been rolled out.\n      - alert: KubeDaemonSetRolloutStuck\n        expr: ((kube_daemonset_status_current_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_daemonset_status_desired_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"}) or (kube_daemonset_status_number_misscheduled{job=\"kube-state-metrics\",namespace=~\".*\"} != 0) or (kube_daemonset_status_updated_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_daemonset_status_desired_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"}) or (kube_daemonset_status_number_available{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_daemonset_status_desired_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"})) and (changes(kube_daemonset_status_updated_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"}[5m]) == 0)\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: DaemonSet {{ $labels.namespace }}/{{ $labels.daemonset }} has not finished or progressed for at least 15 minutes.\n          summary: DaemonSet rollout is stuck.\n      - alert: KubeContainerWaiting\n        expr: sum by(namespace, pod, container, cluster) (kube_pod_container_status_waiting_reason{job=\"kube-state-metrics\",namespace=~\".*\"}) > 0\n        for: 1h\n        labels:\n          severity: warning\n        annotations:\n          description: Pod/{{ $labels.pod }} in namespace {{ $labels.namespace }} on container {{ $labels.container}} has been in waiting state for longer than 1 hour.\n          summary: Pod container waiting longer than 1 hour\n      - alert: KubeDaemonSetNotScheduled\n        expr: kube_daemonset_status_desired_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"} - kube_daemonset_status_current_number_scheduled{job=\"kube-state-metrics\",namespace=~\".*\"} > 0\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $value }} Pods of DaemonSet {{ $labels.namespace }}/{{ $labels.daemonset }} are not scheduled.\n          summary: DaemonSet pods are not scheduled.\n      - alert: KubeDaemonSetMisScheduled\n        expr: kube_daemonset_status_number_misscheduled{job=\"kube-state-metrics\",namespace=~\".*\"} > 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ $value }} Pods of DaemonSet {{ $labels.namespace }}/{{ $labels.daemonset }} are running where they are not supposed to run.\n          summary: DaemonSet pods are misscheduled.\n      - alert: KubeJobNotCompleted\n        expr: time() - max by(namespace, job_name, cluster) (kube_job_status_start_time{job=\"kube-state-metrics\",namespace=~\".*\"} and kube_job_status_active{job=\"kube-state-metrics\",namespace=~\".*\"} > 0) > 43200\n        labels:\n          severity: warning\n        annotations:\n          description: Job {{ $labels.namespace }}/{{ $labels.job_name }} is taking more than {{ \"43200\" | humanizeDuration }} to complete.\n          summary: Job did not complete in time\n      - alert: KubeJobFailed\n        expr: kube_job_failed{job=\"kube-state-metrics\",namespace=~\".*\"} > 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: Job {{ $labels.namespace }}/{{ $labels.job_name }} failed to complete. Removing failed job after investigation should clear this alert.\n          summary: Job failed to complete.\n      - alert: KubeHpaReplicasMismatch\n        expr: (kube_horizontalpodautoscaler_status_desired_replicas{job=\"kube-state-metrics\",namespace=~\".*\"} != kube_horizontalpodautoscaler_status_current_replicas{job=\"kube-state-metrics\",namespace=~\".*\"}) and (kube_horizontalpodautoscaler_status_current_replicas{job=\"kube-state-metrics\",namespace=~\".*\"} > kube_horizontalpodautoscaler_spec_min_replicas{job=\"kube-state-metrics\",namespace=~\".*\"}) and (kube_horizontalpodautoscaler_status_current_replicas{job=\"kube-state-metrics\",namespace=~\".*\"} < kube_horizontalpodautoscaler_spec_max_replicas{job=\"kube-state-metrics\",namespace=~\".*\"}) and changes(kube_horizontalpodautoscaler_status_current_replicas{job=\"kube-state-metrics\",namespace=~\".*\"}[15m]) == 0\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} has not matched the desired number of replicas for longer than 15 minutes.\n          summary: HPA has not matched descired number of replicas.\n      - alert: KubeHpaMaxedOut\n        expr: kube_horizontalpodautoscaler_status_current_replicas{job=\"kube-state-metrics\",namespace=~\".*\"} == kube_horizontalpodautoscaler_spec_max_replicas{job=\"kube-state-metrics\",namespace=~\".*\"}\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          description: HPA {{ $labels.namespace }}/{{ $labels.horizontalpodautoscaler }} has been running at max replicas for longer than 15 minutes.\n          summary: HPA is running at max replicas\n      - alert: KubeStateMetricsListErrors\n        expr: (sum(rate(kube_state_metrics_list_total{job=\"kube-state-metrics\",result=\"error\"}[5m])) / sum(rate(kube_state_metrics_list_total{job=\"kube-state-metrics\"}[5m]))) > 0.01\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: kube-state-metrics is experiencing errors at an elevated rate in list operations. This is likely causing it to not be able to expose metrics about Kubernetes objects or at all.\n          summary: kube-state-metrics is experiencing errors in list operations.\n      - alert: KubeStateMetricsWatchErrors\n        expr: (sum(rate(kube_state_metrics_watch_total{job=\"kube-state-metrics\",result=\"error\"}[5m])) / sum(rate(kube_state_metrics_watch_total{job=\"kube-state-metrics\"}[5m]))) > 0.01\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: kube-state-metrics is experiencing errors at an elevated rate in list operations. This is likely causing it to not be able to expose metrics about Kubernetes objects or at all.\n          summary: kube-state-metrics is experiencing errors in watch operations.\n      - alert: KubeStateMetricsShardingMismatch\n        expr: stdvar(kube_state_metrics_total_shards{job=\"kube-state-metrics\"}) != 0\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: kube-state-metrics pods are running with different --total-shards configuration, some Kubernetes objects may be exposed multiple times or not exposed at all.\n          summary: kube-state-metrics sharding is misconfigured.\n      - alert: KubeStateMetricsShardsMissing\n        expr: 2 ^ max(kube_state_metrics_total_shards{job=\"kube-state-metrics\"}) - 1 - sum(2 ^ max by(shard_ordinal) (kube_state_metrics_shard_ordinal{job=\"kube-state-metrics\"})) != 0\n        for: 15m\n        labels:\n          severity: critical\n        annotations:\n          description: kube-state-metrics shards are missing, some Kubernetes objects are not being exposed.\n          summary: kube-state-metrics shards are missing.\n      - alert: KubeAPIErrorBudgetBurn\n        expr: sum(apiserver_request:burnrate1h) > (14.4 * 0.01) and sum(apiserver_request:burnrate5m) > (14.4 * 0.01)\n        for: 2m\n        labels:\n          long: 1h\n          severity: critical\n          short: 5m\n        annotations:\n          description: The API server is burning too much error budget.\n          summary: The API server is burning too much error budget.\n      - alert: KubeAPIErrorBudgetBurn\n        expr: sum(apiserver_request:burnrate6h) > (6 * 0.01) and sum(apiserver_request:burnrate30m) > (6 * 0.01)\n        for: 15m\n        labels:\n          long: 6h\n          severity: critical\n          short: 30m\n        annotations:\n          description: The API server is burning too much error budget.\n          summary: The API server is burning too much error budget.\n      - alert: KubeAPIErrorBudgetBurn\n        expr: sum(apiserver_request:burnrate1d) > (3 * 0.01) and sum(apiserver_request:burnrate2h) > (3 * 0.01)\n        for: 1d\n        labels:\n          long: 1d\n          severity: warning\n          short: 2h\n        annotations:\n          description: The API server is burning too much error budget.\n          summary: The API server is burning too much error budget.\n      - alert: KubeAPIErrorBudgetBurn\n        expr: sum(apiserver_request:burnrate3d) > (1 * 0.01) and sum(apiserver_request:burnrate6h) > (1 * 0.01)\n        for: 3h\n        labels:\n          long: 3d\n          severity: warning\n          short: 6h\n        annotations:\n          description: The API server is burning too much error budget.\n          summary: The API server is burning too much error budget.\n      - alert: TargetDown\n        expr: 100 * (count by(job, namespace, service) (up == 0) / count by(job, namespace, service) (up)) > 10\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          description: The {{ printf \"%.4g\" $value }}% of the {{ $labels.job }}/{{ $labels.service }} targets in {{ $labels.namespace }} namespace are down.\n  - name: infra-alerts-05\n    rules:\n      - alert: Watchdog\n        expr: vector(1)\n        labels:\n          severity: none\n        annotations:\n          description: This is an alert meant to ensure that the entire alerting pipeline is functional. This alert is always firing, therefore it should always be firing in Alertmanager and always fire against a receiver. There are integrations with various notification mechanisms that send a notification when this alert is not firing. For example the \"DeadMansSnitch\" integration in PagerDuty.\n      - alert: InfoInhibitor\n        expr: ALERTS{severity=\"info\"} == 1 unless on(namespace) ALERTS{alertname!=\"InfoInhibitor\",alertstate=\"firing\",severity=~\"warning|critical\"} == 1\n        labels:\n          severity: none\n        annotations:\n          description: This is an alert that is used to inhibit info alerts. By themselves, the info-level alerts are sometimes very noisy, but they are relevant when combined with other alerts. This alert fires whenever there's a severity=\"info\" alert, and stops firing when another alert with a severity of 'warning' or 'critical' starts firing on the same namespace. This alert should be routed to a null receiver and configured to inhibit alerts with severity=\"info\".\n      - alert: etcdInsufficientMembers\n        expr: sum by(job) (up{job=~\".*etcd.*\"} == bool 1) < ((count by(job) (up{job=~\".*etcd.*\"}) + 1) / 2)\n        for: 3m\n        labels:\n          severity: critical\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":insufficient members ({{ $value }}).\n      - alert: etcdHighNumberOfLeaderChanges\n        expr: rate(etcd_server_leader_changes_seen_total{job=~\".*etcd.*\"}[15m]) > 3\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":instance {{ $labels.instance }} has seen {{ $value }} leader changes within the last hour.\n      - alert: etcdNoLeader\n        expr: etcd_server_has_leader{job=~\".*etcd.*\"} == 0\n        for: 1m\n        labels:\n          severity: critical\n        annotations:\n          message: message:etcd cluster \"{{ $labels.job }}\":member {{ $labels.instance }} has no leader.\n      - alert: etcdHighNumberOfFailedGRPCRequests\n        expr: 100 * sum by(job, instance, grpc_service, grpc_method) (rate(grpc_server_handled_total{grpc_code!=\"OK\",job=~\".*etcd.*\"}[5m])) / sum by(job, instance, grpc_service, grpc_method) (rate(grpc_server_handled_total{job=~\".*etcd.*\"}[5m])) > 1\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":{{ $value }}% of requests for {{ $labels.grpc_method }} failed on etcd instance {{ $labels.instance }}.\n      - alert: etcdGRPCRequestsSlow\n        expr: histogram_quantile(0.99, sum by(job, instance, grpc_service, grpc_method, le) (rate(grpc_server_handling_seconds_bucket{grpc_type=\"unary\",job=~\".*etcd.*\"}[5m]))) > 0.15\n        for: 10m\n        labels:\n          severity: critical\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":gRPC requests to {{ $labels.grpc_method }} are taking {{ $value }}s on etcd instance {{ $labels.instance }}.\n      - alert: etcdMemberCommunicationSlow\n        expr: histogram_quantile(0.99, rate(etcd_network_peer_round_trip_time_seconds_bucket{job=~\".*etcd.*\"}[5m])) > 0.15\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: message:etcd cluster \"{{ $labels.job }}\":member communication with {{ $labels.To }} is taking {{ $value }}s on etcd instance {{ $labels.instance }}.\n      - alert: etcdHighNumberOfFailedProposals\n        expr: rate(etcd_server_proposals_failed_total{job=~\".*etcd.*\"}[15m]) > 5\n        for: 15m\n        labels:\n          severity: warning\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":{{ $value }} proposal failures within the last hour on etcd instance {{ $labels.instance }}.\n      - alert: etcdHighFsyncDurations\n        expr: histogram_quantile(0.99, rate(etcd_disk_wal_fsync_duration_seconds_bucket{job=~\".*etcd.*\"}[5m])) > 0.5\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":99th percentile fync durations are {{ $value }}s on etcd instance {{ $labels.instance }}.\n      - alert: etcdHighCommitDurations\n        expr: histogram_quantile(0.99, rate(etcd_disk_backend_commit_duration_seconds_bucket{job=~\".*etcd.*\"}[5m])) > 0.25\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: etcd cluster \"{{ $labels.job }}\":99th percentile commit durations {{ $value }}s on etcd instance {{ $labels.instance }}.\n      - alert: etcdHighNumberOfFailedHTTPRequests\n        expr: sum by(method) (rate(etcd_http_failed_total{code!=\"404\",job=~\".*etcd.*\"}[5m])) / sum by(method) (rate(etcd_http_received_total{job=~\".*etcd.*\"}[5m])) > 0.01\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: The {{ $value }}% of requests for {{ $labels.method }} failed on etcd instance {{ $labels.instance }}\n      - alert: etcdHighNumberOfFailedHTTPRequests\n        expr: sum by(method) (rate(etcd_http_failed_total{code!=\"404\",job=~\".*etcd.*\"}[5m])) / sum by(method) (rate(etcd_http_received_total{job=~\".*etcd.*\"}[5m])) > 0.05\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: The {{ $value }}% of requests for {{ $labels.method }} failed on etcd instance {{ $labels.instance }}.\n      - alert: etcdHTTPRequestsSlow\n        expr: histogram_quantile(0.99, rate(etcd_http_successful_duration_seconds_bucket[5m])) > 0.15\n        for: 10m\n        labels:\n          severity: warning\n        annotations:\n          message: etcd instance {{ $labels.instance }} HTTP requests to {{ $labels.method }} are slow.\n"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/apiserver/recording-rules.yml",
    "content": "groups:\n  - name: apiserver-monitoring\n    rules:\n      - expr: sum by (cluster, code, verb) (increase(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET|POST|PUT|PATCH|DELETE\",code=~\"2..\"}[1h]))\n        record: code_verb:apiserver_request_total:increase1h\n      - expr: sum by (cluster, code, verb) (increase(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET|POST|PUT|PATCH|DELETE\",code=~\"3..\"}[1h]))\n        record: code_verb:apiserver_request_total:increase1h\n      - expr: sum by (cluster, code, verb) (increase(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET|POST|PUT|PATCH|DELETE\",code=~\"4..\"}[1h]))\n        record: code_verb:apiserver_request_total:increase1h\n      - expr: sum by (cluster, code, verb) (increase(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET|POST|PUT|PATCH|DELETE\",code=~\"5..\"}[1h]))\n        record: code_verb:apiserver_request_total:increase1h\n      - expr: sum by (cluster,code,resource) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[5m]))\n        labels:\n          verb: read\n        record: code_resource:apiserver_request_total:rate5m\n      - expr: sum by (cluster,code,resource) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))\n        labels:\n          verb: write\n        record: code_resource:apiserver_request_total:rate5m\n      - expr: sum by (cluster, verb, scope, le) (increase(apiserver_request_slo_duration_seconds_bucket[1h]))\n        record: cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase1h\n      - expr: sum by (cluster, verb, scope, le) (avg_over_time(cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase1h[30d])\n          * 24 * 30)\n        record: cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d\n      - expr: |-\n          1 - (\n            (\n              # write too slow\n              sum by (cluster) (cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase30d{verb=~\"POST|PUT|PATCH|DELETE\"})\n              -\n              sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"POST|PUT|PATCH|DELETE\",le=\"1\"})\n            ) +\n            (\n              # read too slow\n              sum by (cluster) (cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase30d{verb=~\"LIST|GET\"})\n              -\n              (\n                (\n                  sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"LIST|GET\",scope=~\"resource|\",le=\"1\"})\n                  or\n                  vector(0)\n                )\n                +\n                sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"LIST|GET\",scope=\"namespace\",le=\"5\"})\n                +\n                sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"LIST|GET\",scope=\"cluster\",le=\"30\"})\n              )\n            ) +\n            # errors\n            sum by (cluster) (code:apiserver_request_total:increase30d{code=~\"5..\"} or vector(0))\n          )\n          /\n          sum by (cluster) (code:apiserver_request_total:increase30d)\n        labels:\n          verb: all\n        record: apiserver_request:availability30d\n      - expr: |-\n          1 - (\n            sum by (cluster) (cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase30d{verb=~\"LIST|GET\"})\n            -\n            (\n              # too slow\n              (\n                sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"LIST|GET\",scope=~\"resource|\",le=\"1\"})\n                or\n                vector(0)\n              )\n              +\n              sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"LIST|GET\",scope=\"namespace\",le=\"5\"})\n              +\n              sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"LIST|GET\",scope=\"cluster\",le=\"30\"})\n            )\n            +\n            # errors\n            sum by (cluster) (code:apiserver_request_total:increase30d{verb=\"read\",code=~\"5..\"} or vector(0))\n          )\n          /\n          sum by (cluster) (code:apiserver_request_total:increase30d{verb=\"read\"})\n        labels:\n          verb: read\n        record: apiserver_request:availability30d\n      - expr: |-\n          1 - (\n            (\n              # too slow\n              sum by (cluster) (cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase30d{verb=~\"POST|PUT|PATCH|DELETE\"})\n              -\n              sum by (cluster) (cluster_verb_scope_le:apiserver_request_slo_duration_seconds_bucket:increase30d{verb=~\"POST|PUT|PATCH|DELETE\",le=\"1\"})\n            )\n            +\n            # errors\n            sum by (cluster) (code:apiserver_request_total:increase30d{verb=\"write\",code=~\"5..\"} or vector(0))\n          )\n          /\n          sum by (cluster) (code:apiserver_request_total:increase30d{verb=\"write\"})\n        labels:\n          verb: write\n        record: apiserver_request:availability30d\n      - expr: histogram_quantile(0.99, sum by (cluster, le, resource) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",verb=~\"LIST|GET\",subresource!~\"proxy|attach|log|exec|portforward\"}[5m])))\n          > 0\n        labels:\n          quantile: \"0.99\"\n          verb: read\n        record: cluster_quantile:apiserver_request_slo_duration_seconds:histogram_quantile\n      - expr: histogram_quantile(0.99, sum by (cluster, le, resource) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\",subresource!~\"proxy|attach|log|exec|portforward\"}[5m])))\n          > 0\n        labels:\n          quantile: \"0.99\"\n          verb: write\n        record: cluster_quantile:apiserver_request_slo_duration_seconds:histogram_quantile\n      - expr: |\n          histogram_quantile(0.9, sum(rate(apiserver_request_duration_seconds_bucket{job=\"apiserver\",subresource!=\"log\",verb!~\"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT\"}[5m])) without(instance, pod))\n        labels:\n          quantile: \"0.9\"\n        record: cluster_quantile:apiserver_request_duration_seconds:histogram_quantile"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/istio/alerting-rules.yml",
    "content": "  groups:\n    - name: \"istio.basic.alerting-rules\"\n      rules:\n        - alert: IngressTrafficMissing\n          annotations:\n            summary: 'ingress gateway traffic missing'\n            description: '[Critical]: ingress gateway traffic missing, likely other monitors are misleading, check client logs'\n          expr: >\n              absent(istio_requests_total{destination_service_namespace=~\"service-graph.*\",reporter=\"source\",source_workload=\"istio-ingressgateway\"})==1\n          for: 5m\n        - alert: IstioMetricsMissing\n          annotations:\n            summary: 'Istio Metrics missing'\n            description: '[Critical]: Check prometheus deployment or whether the prometheus filters are applied correctly'\n          expr: >\n            absent(istio_request_total)==1 or absent(istio_request_duration_milliseconds_bucket)==1\n          for: 5m\n    - name: \"istio.workload.alerting-rules\"\n      rules:\n        - alert: HTTP5xxRateHigh\n          annotations:\n            summary: '5xx rate too high'\n            description: 'The HTTP 5xx errors rate higher than 0.05 in 5 mins'\n          expr: >\n            sum(irate(istio_requests_total{reporter=\"destination\", response_code=~\"5.*\"}[5m])) / sum(irate(istio_requests_total{reporter=\"destination\"}[5m])) > 0.05\n          for: 5m\n        - alert: WorkloadLatencyP99High\n          expr: histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{source_workload=~\"svc.*\"}[5m])) by (source_workload,namespace, le)) > 160\n          for: 10m\n          annotations:\n            description: 'The workload request latency P99 > 160ms '\n            message:  \"Request duration has slowed down for workload: {{`{{$labels.source_workload}}`}} in namespace: {{`{{$labels.namespace}}`}}. Response duration is {{`{{$value}}`}} milliseconds\"\n        - alert: IngressLatencyP99High\n          expr: histogram_quantile(0.99, sum(irate(istio_request_duration_milliseconds_bucket{source_workload=~\"istio.*\"}[5m])) by (source_workload,namespace, le)) > 250\n          for: 10m\n          annotations:\n            description: 'The ingress latency P99 > 250ms '\n            message:  \"Request duration has slowed down for ingress: {{`{{$labels.source_workload}}`}} in namespace: {{`{{$labels.namespace}}`}}. Response duration is {{`{{$value}}`}} milliseconds\"\n    - name: \"istio.infra.alerting-rules\"\n      rules:\n        - alert: ProxyContainerCPUUsageHigh\n          expr: (sum(rate(container_cpu_usage_seconds_total{namespace!=\"kube-system\", container=~\"istio-proxy\", namespace!=\"\"}[5m])) BY (namespace, pod, container) * 100) > 80\n          for: 5m\n          annotations:\n            summary: \"Proxy Container CPU usage (namespace {{ $labels.namespace }}) (pod {{ $labels.pod }}) (container {{ $labels.container }})  VALUE = {{ $value }}\\n\"\n            description: \"Proxy Container CPU usage is above 80%\"\n        - alert: ProxyContainerMemoryUsageHigh\n          expr: (sum(container_memory_working_set_bytes{namespace!=\"kube-system\", container=~\"istio-proxy\", namespace!=\"\"}) BY (container, pod, namespace)  / (sum(container_spec_memory_limit_bytes{namespace!=\"kube-system\", container!=\"POD\"}) BY (container, pod, namespace) > 0)* 100) > 80\n          for: 5m\n          annotations:\n            summary: \"Proxy Container Memory usage (namespace {{ $labels.namespace }}) (pod {{ $labels.pod }}) (container {{ $labels.container }})  VALUE = {{ $value }}\\n\"\n            description: \"Proxy Container Memory usage is above 80%\"\n        - alert: IngressMemoryUsageIncreaseRateHigh\n          expr: avg(deriv(container_memory_working_set_bytes{container=~\"istio-proxy\",namespace=\"istio-system\"}[60m])) > 200\n          for: 180m\n          annotations:\n            summary: \"Ingress proxy Memory change rate, VALUE = {{ $value }}\\n\"\n            description: \"Ingress proxy Memory Usage increases more than 200 Bytes/sec\"\n        - alert: IstiodContainerCPUUsageHigh\n          expr: (sum(rate(container_cpu_usage_seconds_total{namespace=\"istio-system\", container=\"discovery\"}[5m])) BY (pod) * 100) > 80\n          for: 5m\n          annotations:\n            summary: \"Istiod Container CPU usage (namespace {{ $labels.namespace }}) (pod {{ $labels.pod }}) (container {{ $labels.container }}) VALUE = {{ $value }}\\n\"\n            description: \"Isitod Container CPU usage is above 80%\"\n        - alert: IstiodMemoryUsageHigh\n          expr: (sum(container_memory_working_set_bytes{namespace=\"istio-system\", container=\"discovery\"}) BY (pod)  / (sum(container_spec_memory_limit_bytes{namespace=\"istio-system\", container=\"discovery\"}) BY (pod) > 0)* 100) > 80\n          for: 5m\n          annotations:\n            summary: \"Istiod Container Memory usage (namespace {{ $labels.namespace }}) (pod {{ $labels.pod }}) (container {{ $labels.container }}) VALUE = {{ $value }}\\n\"\n            description: \"Istiod Container Memory usage is above 80%\"\n        - alert: IstiodMemoryUsageIncreaseRateHigh\n          expr: sum(deriv(container_memory_working_set_bytes{namespace=\"istio-system\",pod=~\"istiod-.*\"}[60m])) > 1000\n          for: 300m\n          annotations:\n            summary: \"Istiod Container Memory usage increase rate high, VALUE = {{ $value }}\\n\"\n            description: \"Istiod Container Memory usage increases more than 1k Bytes/sec\"\n    - name: \"istio.controlplane.alerting-rules\"\n      rules:\n        - alert: IstiodxdsPushErrorsHigh\n          annotations:\n            summary: 'istiod push errors is too high'\n            description: 'istiod push error rate is higher than 0.05'\n          expr: >\n            sum(irate(pilot_xds_push_errors{app=\"istiod\"}[5m])) / sum(irate(pilot_xds_pushes{app=\"istiod\"}[5m])) > 0.05\n          for: 5m\n        - alert: IstiodxdsRejectHigh\n          annotations:\n            summary: 'istiod rejects rate is too high'\n            description: 'istiod rejects rate is higher than 0.05'\n          expr: >\n            sum(irate(pilot_total_xds_rejects{app=\"istiod\"}[5m])) / sum(irate(pilot_xds_pushes{app=\"istiod\"}[5m])) > 0.05\n          for: 5m\n        - alert: IstiodContainerNotReady\n          annotations:\n            summary: 'istiod container not ready'\n            description: 'container: discovery not running'\n          expr: >\n            kube_pod_container_status_running{namespace=\"istio-system\", container=\"discovery\", component=\"\"} == 0\n          for: 5m\n        - alert: IstiodUnavailableReplica\n          annotations:\n            summary: 'Istiod unavailable pod'\n            description: 'Istiod unavailable replica > 0'\n          expr: >\n            kube_deployment_status_replicas_unavailable{deployment=\"istiod\", component=\"\"} > 0\n          for: 5m\n        - alert: Ingress200RateLow\n          annotations:\n            summary: 'ingress gateway 200 rate drops'\n            description: 'The expected rate is 100 per ns, the limit is set based on 15ns'\n          expr: >\n            sum(rate(istio_requests_total{reporter=\"source\", source_workload=\"istio-ingressgateway\",response_code=\"200\",destination_service_namespace=~\"service-graph.*\"}[5m])) < 1490\n          for: 30m"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/istio/recording-rules.yml",
    "content": " groups:\n  - name: \"istio.recording-rules\"\n    interval: 5s\n    rules:\n    - record: \"workload:istio_requests_total\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_requests_total)\n\n    - record: \"workload:istio_request_duration_milliseconds_count\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_request_duration_milliseconds_count)\n\n    - record: \"workload:istio_request_duration_milliseconds_sum\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_request_duration_milliseconds_sum)\n\n    - record: \"workload:istio_request_duration_milliseconds_bucket\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_request_duration_milliseconds_bucket)\n\n    - record: \"workload:istio_request_bytes_count\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_request_bytes_count)\n\n    - record: \"workload:istio_request_bytes_sum\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_request_bytes_sum)\n\n    - record: \"workload:istio_request_bytes_bucket\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_request_bytes_bucket)\n\n    - record: \"workload:istio_response_bytes_count\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_response_bytes_count)\n\n    - record: \"workload:istio_response_bytes_sum\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_response_bytes_sum)\n\n    - record: \"workload:istio_response_bytes_bucket\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_response_bytes_bucket)\n\n    - record: \"workload:istio_tcp_sent_bytes_total\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_tcp_sent_bytes_total)\n\n    - record: \"workload:istio_tcp_received_bytes_total\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_tcp_received_bytes_total)\n\n    - record: \"workload:istio_tcp_connections_opened_total\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_tcp_connections_opened_total)\n\n    - record: \"workload:istio_tcp_connections_closed_total\"\n      expr: |\n        sum without(instance, kubernetes_namespace, kubernetes_pod_name) (istio_tcp_connections_closed_total)"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/java/alerting-rules.yml",
    "content": "groups:\n  - name: default-alert\n    rules:\n      - alert: metric:alerting_rule\n        expr: jvm_memory_bytes_used{job=\"java\", area=\"heap\"} / jvm_memory_bytes_max * 100 > 80\n        for: 1m\n        labels:\n            severity: warning\n        annotations:\n            summary: \"JVM heap warning\"\n            description: \"JVM heap of instance `{{$labels.instance}}` from application `{{$labels.application}}` is above 80% for one minute. (current=`{{$value}}%`)\"\n"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/java/recording-rules.yml",
    "content": "groups:\n  - name: default-metric\n    rules:\n      - record: metric:recording_rule\n        expr: avg(rate(container_cpu_usage_seconds_total[5m]))\n"
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/nginx/alerting-rules.yml",
    "content": "groups:\n    - name: Nginx-HTTP-4xx-error-rate\n      rules:\n      - alert: metric:alerting_rule\n        expr: sum(rate(nginx_http_requests_total{status=~\"^4..\"}[1m])) / sum(rate(nginx_http_requests_total[1m])) * 100 > 5\n        for: 1m\n        labels:\n         severity: critical\n        annotations:\n         summary: Nginx high HTTP 4xx error rate (instance {{ $labels.instance }})\n         description: \"Too many HTTP requests with status 4xx (> 5%)\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\"\n    - name: Nginx-HTTP-5xx-error-rate\n      rules:\n      - alert: metric:alerting_rule\n        expr: sum(rate(nginx_http_requests_total{status=~\"^5..\"}[1m])) / sum(rate(nginx_http_requests_total[1m])) * 100 > 5\n        for: 1m\n        labels:\n         severity: critical\n        annotations:\n         summary: Nginx high HTTP 5xx error rate (instance {{ $labels.instance }})\n         description: \"Too many HTTP requests with status 5xx (> 5%)\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\"\n    - name: Nginx-high-latency\n      rules:\n      - alert: metric:alerting_rule\n        expr: histogram_quantile(0.99, sum(rate(nginx_http_request_duration_seconds_bucket[2m])) by (host, node)) > 3\n        for: 2m\n        labels:\n         severity: warning\n        annotations:\n         summary: Nginx latency high (instance {{ $labels.instance }})\n         description: \"Nginx p99 latency is higher than 3 seconds\\n  VALUE = {{ $value }}\\n  LABELS = {{ $labels }}\""
  },
  {
    "path": "lib/multi-cluster-construct/resources/amp-config/recording-rules.yml",
    "content": "groups:\n  - name: infra-rules-01\n    rules:\n      - record: \"node_namespace_pod:kube_pod_info:\"\n        expr: topk by(cluster, namespace, pod) (1, max by(cluster, node, namespace, pod) (label_replace(kube_pod_info{job=\"kube-state-metrics\",node!=\"\"}, \"pod\", \"$1\", \"pod\", \"(.*)\")))\n      - record: node:node_num_cpu:sum\n        expr: count by(cluster, node) (sum by(node, cpu) (node_cpu_seconds_total{job=\"node-exporter\"} * on(namespace, pod) group_left(node) topk by(namespace, pod) (1, node_namespace_pod:kube_pod_info:)))\n      - record: :node_memory_MemAvailable_bytes:sum\n        expr: sum by(cluster) (node_memory_MemAvailable_bytes{job=\"node-exporter\"} or (node_memory_Buffers_bytes{job=\"node-exporter\"} + node_memory_Cached_bytes{job=\"node-exporter\"} + node_memory_MemFree_bytes{job=\"node-exporter\"} + node_memory_Slab_bytes{job=\"node-exporter\"}))\n      - record: cluster:node_cpu:ratio_rate5m\n        expr: sum by (cluster) (rate(node_cpu_seconds_total{job=\"node-exporter\",mode!=\"idle\",mode!=\"iowait\",mode!=\"steal\"}[5m])) / count by (cluster) (sum by(cluster, instance, cpu) (node_cpu_seconds_total{job=\"node-exporter\"}))\n      - record: node_quantile:kubelet_pleg_relist_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.99, sum by(cluster, instance, le) (rate(kubelet_pleg_relist_duration_seconds_bucket[5m])) * on(cluster, instance) group_left(node) kubelet_node_name{job=\"kubelet\"})\n        labels:\n          quantile: 0.99\n      - record: node_quantile:kubelet_pleg_relist_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.9, sum by(cluster, instance, le) (rate(kubelet_pleg_relist_duration_seconds_bucket[5m])) * on(cluster, instance) group_left(node) kubelet_node_name{job=\"kubelet\"})\n        labels:\n          quantile: 0.9\n      - record: node_quantile:kubelet_pleg_relist_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.5, sum by(cluster, instance, le) (rate(kubelet_pleg_relist_duration_seconds_bucket[5m])) * on(cluster, instance) group_left(node) kubelet_node_name{job=\"kubelet\"})\n        labels:\n          quantile: 0.5\n      - record: instance:node_num_cpu:sum\n        expr: count without(cpu, mode) (node_cpu_seconds_total{job=\"node-exporter\",mode=\"idle\"})\n      - record: instance:node_cpu_utilisation:rate5m\n        expr: 1 - avg without(cpu) (sum without(mode) (rate(node_cpu_seconds_total{job=\"node-exporter\",mode=~\"idle|iowait|steal\"}[5m])))\n      - record: instance:node_load1_per_cpu:ratio\n        expr: (node_load1{job=\"node-exporter\"} / instance:node_num_cpu:sum{job=\"node-exporter\"})\n      - record: instance:node_memory_utilisation:ratio\n        expr: 1 - ((node_memory_MemAvailable_bytes{job=\"node-exporter\"} or (node_memory_Buffers_bytes{job=\"node-exporter\"} + node_memory_Cached_bytes{job=\"node-exporter\"} + node_memory_MemFree_bytes{job=\"node-exporter\"} + node_memory_Slab_bytes{job=\"node-exporter\"})) / node_memory_MemTotal_bytes{job=\"node-exporter\"})\n      - record: instance:node_vmstat_pgmajfault:rate5m\n        expr: rate(node_vmstat_pgmajfault{job=\"node-exporter\"}[5m])\n      - record: instance_device:node_disk_io_time_seconds:rate5m\n        expr: rate(node_disk_io_time_seconds_total{device=~\"mmcblk.p.+|.*nvme.+|rbd.+|sd.+|vd.+|xvd.+|dm-.+|dasd.+\",job=\"node-exporter\"}[5m])\n      - record: instance_device:node_disk_io_time_weighted_seconds:rate5m\n        expr: rate(node_disk_io_time_weighted_seconds_total{device=~\"mmcblk.p.+|.*nvme.+|rbd.+|sd.+|vd.+|xvd.+|dm-.+|dasd.+\",job=\"node-exporter\"}[5m])\n      - record: instance:node_network_receive_bytes_excluding_lo:rate5m\n        expr: sum without(device) (rate(node_network_receive_bytes_total{device!=\"lo\",job=\"node-exporter\"}[5m]))\n      - record: instance:node_network_transmit_bytes_excluding_lo:rate5m\n        expr: sum without(device) (rate(node_network_transmit_bytes_total{device!=\"lo\",job=\"node-exporter\"}[5m]))\n      - record: instance:node_network_receive_drop_excluding_lo:rate5m\n        expr: sum without(device) (rate(node_network_receive_drop_total{device!=\"lo\",job=\"node-exporter\"}[5m]))\n      - record: instance:node_network_transmit_drop_excluding_lo:rate5m\n        expr: sum without(device) (rate(node_network_transmit_drop_total{device!=\"lo\",job=\"node-exporter\"}[5m]))\n      - record: cluster_quantile:scheduler_e2e_scheduling_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.99, sum without(instance, pod) (rate(scheduler_e2e_scheduling_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.99\n      - record: cluster_quantile:scheduler_scheduling_algorithm_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.99, sum without(instance, pod) (rate(scheduler_scheduling_algorithm_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.99\n  - name: infra-rules-02\n    rules:\n      - record: cluster_quantile:scheduler_binding_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.99, sum without(instance, pod) (rate(scheduler_binding_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.99\n      - record: cluster_quantile:scheduler_e2e_scheduling_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.9, sum without(instance, pod) (rate(scheduler_e2e_scheduling_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.9\n      - record: cluster_quantile:scheduler_scheduling_algorithm_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.9, sum without(instance, pod) (rate(scheduler_scheduling_algorithm_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.9\n      - record: cluster_quantile:scheduler_binding_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.9, sum without(instance, pod) (rate(scheduler_binding_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.9\n      - record: cluster_quantile:scheduler_e2e_scheduling_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.5, sum without(instance, pod) (rate(scheduler_e2e_scheduling_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.5\n      - record: cluster_quantile:scheduler_scheduling_algorithm_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.5, sum without(instance, pod) (rate(scheduler_scheduling_algorithm_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.5\n      - record: cluster_quantile:scheduler_binding_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.5, sum without(instance, pod) (rate(scheduler_binding_duration_seconds_bucket{job=\"kube-scheduler\"}[5m])))\n        labels:\n          quantile: 0.5\n      - record: instance:node_cpu:rate:sum\n        expr: sum by(instance) (rate(node_cpu_seconds_total{mode!=\"idle\",mode!=\"iowait\",mode!=\"steal\"}[3m]))\n      - record: instance:node_network_receive_bytes:rate:sum\n        expr: sum by(instance) (rate(node_network_receive_bytes_total[3m]))\n      - record: instance:node_network_transmit_bytes:rate:sum\n        expr: sum by(instance) (rate(node_network_transmit_bytes_total[3m]))\n      - record: instance:node_cpu:ratio\n        expr: sum without(cpu, mode) (rate(node_cpu_seconds_total{mode!=\"idle\",mode!=\"iowait\",mode!=\"steal\"}[5m])) / on(instance) group_left() count by(instance) (sum by(instance, cpu) (node_cpu_seconds_total))\n      - record: cluster:node_cpu:sum_rate5m\n        expr: sum(rate(node_cpu_seconds_total{mode!=\"idle\",mode!=\"iowait\",mode!=\"steal\"}[5m]))\n      - record: cluster:node_cpu:ratio\n        expr: cluster:node_cpu:sum_rate5m / count(sum by(instance, cpu) (node_cpu_seconds_total))\n      - record: count:up1\n        expr: count without(instance, pod, node) (up == 1)\n      - record: count:up0\n        expr: count without(instance, pod, node) (up == 0)\n      - record: cluster_quantile:apiserver_request_slo_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.99, sum by(cluster, le, resource) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[5m]))) > 0\n        labels:\n          quantile: 0.99\n          verb: read\n      - record: cluster_quantile:apiserver_request_slo_duration_seconds:histogram_quantile\n        expr: histogram_quantile(0.99, sum by(cluster, le, resource) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))) > 0\n        labels:\n          quantile: 0.99\n          verb: write\n      - record: apiserver_request:burnrate1d\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[1d]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[1d]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate1h\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1h])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1h])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1h])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1h])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[1h]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[1h]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate2h\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[2h])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[2h])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[2h])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[2h])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[2h]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[2h]))\n        labels:\n          verb: read\n  - name: infra-rules-03\n    rules:\n      - record: apiserver_request:burnrate30m\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[30m])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[30m])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[30m])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[30m])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[30m]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[30m]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate3d\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[3d])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[3d])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[3d])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[3d])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[3d]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[3d]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate5m\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[5m])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[5m])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[5m])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[5m])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[5m]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[5m]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate6h\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[6h])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[6h])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[6h])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[6h])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[6h]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[6h]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate1d\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[1d])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[1d]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[1d]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[1d]))\n        labels:\n          verb: read\n      - record: apiserver_request:burnrate1d\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])) - ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",scope=~\"resource|\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])) or vector(0)) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"5\",scope=\"namespace\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])) + sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"30\",scope=\"cluster\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"LIST|GET\"}[1d])))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"LIST|GET\"}[1d]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[1d]))\n        labels:\n          verb: write\n      - record: apiserver_request:burnrate1h\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[1h])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[1h]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[1h]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[1h]))\n        labels:\n          verb: write\n      - record: apiserver_request:burnrate2h\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[2h])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[2h]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[2h]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[2h]))\n        labels:\n          verb: write\n      - record: apiserver_request:burnrate30m\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[30m])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[30m]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[30m]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[30m]))\n        labels:\n          verb: write\n      - record: apiserver_request:burnrate3d\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[3d])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[3d]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[3d]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[3d]))\n        labels:\n          verb: write\n      - record: apiserver_request:burnrate5m\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))\n        labels:\n          verb: write\n      - record: apiserver_request:burnrate6h\n        expr: ((sum by(cluster) (rate(apiserver_request_slo_duration_seconds_count{job=\"apiserver\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[6h])) - sum by(cluster) (rate(apiserver_request_slo_duration_seconds_bucket{job=\"apiserver\",le=\"1\",subresource!~\"proxy|attach|log|exec|portforward\",verb=~\"POST|PUT|PATCH|DELETE\"}[6h]))) + sum by(cluster) (rate(apiserver_request_total{code=~\"5..\",job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[6h]))) / sum by(cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[6h]))\n        labels:\n          verb: write\n      - record: code_verb:apiserver_request_total:increase30d\n        expr: avg_over_time(code_verb:apiserver_request_total:increase1h[30d]) * 24 * 30\n      - record: code:apiserver_request_total:increase30d\n        expr: sum by(cluster, code) (code_verb:apiserver_request_total:increase30d{verb=~\"LIST|GET\"})\n        labels:\n          verb: read\n      - record: code:apiserver_request_total:increase30d\n        expr: sum by(cluster, code) (code_verb:apiserver_request_total:increase30d{verb=~\"POST|PUT|PATCH|DELETE\"})\n        labels:\n          verb: write\n      - record: cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase1h\n        expr: sum by(cluster, verb, scope) (increase(apiserver_request_slo_duration_seconds_count[1h]))\n      - record: cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase30d\n        expr: sum by(cluster, verb, scope) (avg_over_time(cluster_verb_scope:apiserver_request_slo_duration_seconds_count:increase1h[30d]) * 24 * 30)\n      - record: node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate\n        expr: sum by(cluster, namespace, pod, container) (irate(container_cpu_usage_seconds_total{image!=\"\",job=\"kubelet\"}[5m])) * on(cluster, namespace, pod) group_left(node) topk by(cluster, namespace, pod) (1, max by(cluster, namespace, pod, node) (kube_pod_info{node!=\"\"}))\n      - record: node_namespace_pod_container:container_memory_working_set_bytes\n        expr: container_memory_working_set_bytes{image!=\"\",job=\"kubelet\"} * on(namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))\n      - record: node_namespace_pod_container:container_memory_rss\n        expr: container_memory_rss{image!=\"\",job=\"kubelet\"} * on(namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))\n  - name: infra-rules-04\n    rules:\n      - record: node_namespace_pod_container:container_memory_cache\n        expr: container_memory_cache{image!=\"\",job=\"kubelet\"} * on(namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))\n      - record: node_namespace_pod_container:container_memory_swap\n        expr: container_memory_swap{image!=\"\",job=\"kubelet\"} * on(namespace, pod) group_left(node) topk by(namespace, pod) (1, max by(namespace, pod, node) (kube_pod_info{node!=\"\"}))\n      - record: cluster:namespace:pod_memory:active:kube_pod_container_resource_requests\n        expr: kube_pod_container_resource_requests{job=\"kube-state-metrics\",resource=\"memory\"} * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))\n      - record: namespace_memory:kube_pod_container_resource_requests:sum\n        expr: sum by(namespace, cluster) (sum by(namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_requests{job=\"kube-state-metrics\",resource=\"memory\"}) * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))\n      - record: cluster:namespace:pod_cpu:active:kube_pod_container_resource_requests\n        expr: kube_pod_container_resource_requests{job=\"kube-state-metrics\",resource=\"cpu\"} * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))\n      - record: namespace_cpu:kube_pod_container_resource_requests:sum\n        expr: sum by(namespace, cluster) (sum by(namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_requests{job=\"kube-state-metrics\",resource=\"cpu\"}) * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))\n      - record: cluster:namespace:pod_memory:active:kube_pod_container_resource_limits\n        expr: kube_pod_container_resource_limits{job=\"kube-state-metrics\",resource=\"memory\"} * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))\n      - record: namespace_memory:kube_pod_container_resource_limits:sum\n        expr: sum by(namespace, cluster) (sum by(namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_limits{job=\"kube-state-metrics\",resource=\"memory\"}) * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))\n      - record: cluster:namespace:pod_cpu:active:kube_pod_container_resource_limits\n        expr: kube_pod_container_resource_limits{job=\"kube-state-metrics\",resource=\"cpu\"} * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) ((kube_pod_status_phase{phase=~\"Pending|Running\"} == 1))\n      - record: namespace_cpu:kube_pod_container_resource_limits:sum\n        expr: sum by(namespace, cluster) (sum by(namespace, pod, cluster) (max by(namespace, pod, container, cluster) (kube_pod_container_resource_limits{job=\"kube-state-metrics\",resource=\"cpu\"}) * on(namespace, pod, cluster) group_left() max by(namespace, pod, cluster) (kube_pod_status_phase{phase=~\"Pending|Running\"} == 1)))\n      - record: namespace_workload_pod:kube_pod_owner:relabel\n        expr: max by(cluster, namespace, workload, pod) (label_replace(label_replace(kube_pod_owner{job=\"kube-state-metrics\",owner_kind=\"ReplicaSet\"}, \"replicaset\", \"$1\", \"owner_name\", \"(.*)\") * on(replicaset, namespace) group_left(owner_name) topk by(replicaset, namespace) (1, max by(replicaset, namespace, owner_name) (kube_replicaset_owner{job=\"kube-state-metrics\"})), \"workload\", \"$1\", \"owner_name\", \"(.*)\"))\n        labels:\n          workload_type: deployment\n      - record: namespace_workload_pod:kube_pod_owner:relabel\n        expr: max by(cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job=\"kube-state-metrics\",owner_kind=\"DaemonSet\"}, \"workload\", \"$1\", \"owner_name\", \"(.*)\"))\n        labels:\n          workload_type: daemonset\n      - record: namespace_workload_pod:kube_pod_owner:relabel\n        expr: max by(cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job=\"kube-state-metrics\",owner_kind=\"StatefulSet\"}, \"workload\", \"$1\", \"owner_name\", \"(.*)\"))\n        labels:\n          workload_type: statefulset\n      - record: namespace_workload_pod:kube_pod_owner:relabel\n        expr: max by(cluster, namespace, workload, pod) (label_replace(kube_pod_owner{job=\"kube-state-metrics\",owner_kind=\"Job\"}, \"workload\", \"$1\", \"owner_name\", \"(.*)\"))\n        labels:\n          workload_type: job"
  },
  {
    "path": "lib/multi-cluster-construct/resources/cost-optimization/scaleDownEksToZero.yml",
    "content": "schemaVersion: '0.3'\ndescription: |-\n  ---\n  # Scale down all conformitron EKS cluster to 0\nassumeRole: arn:aws:iam::ACCOUNT_ID:role/SsmEksRole\nmainSteps:\n  - name: scaleEKSClusterToZero\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_1\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: arm-1-26-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_1\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_2\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: arm-1-27-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_2\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_3\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: arm-1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_3\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_4\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: br-ARM1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_4\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_5\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: br-X861-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_5\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_6\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: x86-1-26-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_6\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToZero_7\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: x86-1-27-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n  - name: scaleEKSClusterToZero_7\n    action: aws:executeAwsApi\n    isEnd: true\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: x86-1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 0\n        maxSize: 1\n        desiredSize: 0\n"
  },
  {
    "path": "lib/multi-cluster-construct/resources/cost-optimization/scaleUpEksToOne.yml",
    "content": "schemaVersion: '0.3'\ndescription: |-\n  ---\n  # Scale down all conformitron EKS cluster to1\nassumeRole: arn:aws:iam::ACCOUNT_ID:role/SsmEksRole\nmainSteps:\n  - name: scaleEKSClusterToOne\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_1\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: arm-1-26-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_1\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_2\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: arm-1-27-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_2\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_3\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: arm-1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_3\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_4\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: br-arm-1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_4\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_5\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: br-x86-1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_5\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_6\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: x86-1-26-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_6\n    action: aws:executeAwsApi\n    nextStep: scaleEKSClusterToOne_7\n    isEnd: false\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: x86-1-27-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n  - name: scaleEKSClusterToOne_7\n    action: aws:executeAwsApi\n    isEnd: true\n    inputs:\n      Service: eks\n      Api: UpdateNodegroupConfig\n      clusterName: x86-1-28-blueprint\n      nodegroupName: eks-blueprints-mng\n      scalingConfig:\n        minSize: 1\n        maxSize: 1\n        desiredSize: 1\n"
  },
  {
    "path": "lib/multi-cluster-construct/resources/otel-collector-config.yml",
    "content": "#\n# OpenTelemetry Collector configuration\n# Metrics pipeline with Prometheus Receiver and AWS Remote Write Exporter sending metrics to Amazon Managed Prometheus\n#\napiVersion: opentelemetry.io/v1alpha1\nkind: OpenTelemetryCollector\nmetadata:\n  name: otel-collector-amp\n  namespace: \"{{namespace}}\"\nspec:\n  mode: \"{{deploymentMode}}\"\n  image: public.ecr.aws/aws-observability/aws-otel-collector:v0.37.0\n  resources:\n    limits:\n      cpu: \"1\"\n      memory: \"2Gi\"\n    requests:\n      cpu: \"1\"\n      memory: \"2Gi\"\n  serviceAccount: adot-collector\n  podSecurityContext:\n    runAsGroup: 0\n    runAsUser: 0\n  volumeMounts:\n    - name: varlogpods\n      mountPath: /var/log/pods\n      readOnly: true\n  volumes:\n    - name: varlogpods\n      hostPath:\n        path: /var/log/pods\n  env:\n  - name: NODE_NAME\n    valueFrom:\n      fieldRef:\n        fieldPath: spec.nodeName\n  config: |\n    receivers:\n      prometheus:\n        config:\n          global:\n            scrape_interval: 15s\n            scrape_timeout: 10s\n            external_labels:\n              cluster: \"{{clusterName}}\"\n          scrape_configs:\n            {{ start enableAdotMetricsCollectionJob}}\n            - job_name: otel-collector-metrics\n              scrape_interval: 10s\n              static_configs:\n                - targets: ['localhost:8888']\n            {{ stop enableAdotMetricsCollectionJob }}\n            - job_name: 'kubernetes-kubelet'\n              scheme: https\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              kubernetes_sd_configs:\n              - role: node\n              relabel_configs:\n              - action: labelmap\n                regex: __meta_kubernetes_node_label_(.+)\n              - target_label: __address__\n                replacement: kubernetes.default.svc.cluster.local:443\n              - source_labels: [__meta_kubernetes_node_name]\n                regex: (.+)\n                target_label: __metrics_path__\n                replacement: /api/v1/nodes/$${1}/proxy/metrics\n            - job_name: 'kubelet'\n              scheme: https\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              kubernetes_sd_configs:\n              - role: node\n              relabel_configs:\n              - action: labelmap\n                regex: __meta_kubernetes_node_label_(.+)\n              - target_label: __address__\n                replacement: kubernetes.default.svc.cluster.local:443\n              - source_labels: [__meta_kubernetes_node_name]\n                regex: (.+)\n                target_label: __metrics_path__\n                replacement: /api/v1/nodes/$${1}/proxy/metrics/cadvisor\n            {{ start enableAPIserverJob }}  \n            - job_name: 'apiserver'\n              scheme: https\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              kubernetes_sd_configs:\n              - role: endpoints\n              relabel_configs:\n              - source_labels:\n                  [\n                    __meta_kubernetes_namespace,\n                    __meta_kubernetes_service_name,\n                    __meta_kubernetes_endpoint_port_name,\n                  ]\n                action: keep\n                regex: default;kubernetes;https\n              metric_relabel_configs:\n              - action: keep\n                source_labels: [__name__]\n              - source_labels: [__name__, le]\n                separator: ;\n                regex: apiserver_request_duration_seconds_bucket;(0.15|0.2|0.3|0.35|0.4|0.45|0.6|0.7|0.8|0.9|1.25|1.5|1.75|2|3|3.5|4|4.5|6|7|8|9|15|25|40|50)\n                replacement: $1\n                action: drop\n            {{ stop enableAPIserverJob }}\n            - job_name: serviceMonitor/default/kube-prometheus-stack-prometheus-node-exporter/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (prometheus-node-exporter);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_jobLabel]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-metrics\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - default\n            - job_name: serviceMonitor/default/kube-prometheus-stack-prometheus/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-prometheus);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_self_monitor, __meta_kubernetes_service_labelpresent_self_monitor]\n                separator: ;\n                regex: (true);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-web\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-web\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - default\n            - job_name: serviceMonitor/default/kube-prometheus-stack-operator/0\n              honor_labels: true\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: https\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-operator);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: https\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: https\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - default\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kubelet/2\n              honor_labels: true\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics/probes\n              scheme: https\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name, __meta_kubernetes_service_labelpresent_app_kubernetes_io_name]\n                separator: ;\n                regex: (kubelet);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_k8s_app, __meta_kubernetes_service_labelpresent_k8s_app]\n                separator: ;\n                regex: (kubelet);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: https-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_k8s_app]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: https-metrics\n                action: replace\n              - source_labels: [__metrics_path__]\n                separator: ;\n                regex: (.*)\n                target_label: metrics_path\n                replacement: $$1\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kubelet/1\n              honor_labels: true\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics/cadvisor\n              scheme: https\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name, __meta_kubernetes_service_labelpresent_app_kubernetes_io_name]\n                separator: ;\n                regex: (kubelet);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_k8s_app, __meta_kubernetes_service_labelpresent_k8s_app]\n                separator: ;\n                regex: (kubelet);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: https-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_k8s_app]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: https-metrics\n                action: replace\n              - source_labels: [__metrics_path__]\n                separator: ;\n                regex: (.*)\n                target_label: metrics_path\n                replacement: $$1\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kubelet/0\n              honor_labels: true\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: https\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                insecure_skip_verify: true\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name, __meta_kubernetes_service_labelpresent_app_kubernetes_io_name]\n                separator: ;\n                regex: (kubelet);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_k8s_app, __meta_kubernetes_service_labelpresent_k8s_app]\n                separator: ;\n                regex: (kubelet);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: https-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_k8s_app]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: https-metrics\n                action: replace\n              - source_labels: [__metrics_path__]\n                separator: ;\n                regex: (.*)\n                target_label: metrics_path\n                replacement: $$1\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kube-state-metrics/0\n              honor_labels: true\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_instance, __meta_kubernetes_service_labelpresent_app_kubernetes_io_instance]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name, __meta_kubernetes_service_labelpresent_app_kubernetes_io_name]\n                separator: ;\n                regex: (kube-state-metrics);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - default\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kube-scheduler/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-kube-scheduler);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_jobLabel]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-metrics\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kube-proxy/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-kube-proxy);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_jobLabel]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-metrics\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kube-etcd/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-kube-etcd);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_jobLabel]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-metrics\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-kube-controller-manager/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-kube-controller-manager);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_jobLabel]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-metrics\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-coredns/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-coredns);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-metrics\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_jobLabel]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-metrics\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - kube-system\n            - job_name: serviceMonitor/default/kube-prometheus-stack-apiserver/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: https\n              authorization:\n                type: Bearer\n                credentials_file: /var/run/secrets/kubernetes.io/serviceaccount/token\n              tls_config:\n                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt\n                server_name: kubernetes\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_component, __meta_kubernetes_service_labelpresent_component]\n                separator: ;\n                regex: (kubernetes);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: https\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_component]\n                separator: ;\n                regex: (.+)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: https\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - default\n            - job_name: serviceMonitor/default/kube-prometheus-stack-alertmanager/0\n              honor_timestamps: true\n              scrape_interval: 30s\n              scrape_timeout: 10s\n              metrics_path: /metrics\n              scheme: http\n              follow_redirects: true\n              enable_http2: true\n              relabel_configs:\n              - source_labels: [job]\n                separator: ;\n                regex: (.*)\n                target_label: __tmp_prometheus_job_name\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_service_labelpresent_app]\n                separator: ;\n                regex: (kube-prometheus-stack-alertmanager);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_release, __meta_kubernetes_service_labelpresent_release]\n                separator: ;\n                regex: (kube-prometheus-stack);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_service_label_self_monitor, __meta_kubernetes_service_labelpresent_self_monitor]\n                separator: ;\n                regex: (true);true\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_port_name]\n                separator: ;\n                regex: http-web\n                replacement: $$1\n                action: keep\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Node;(.*)\n                target_label: node\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_endpoint_address_target_kind, __meta_kubernetes_endpoint_address_target_name]\n                separator: ;\n                regex: Pod;(.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_namespace]\n                separator: ;\n                regex: (.*)\n                target_label: namespace\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: service\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_name]\n                separator: ;\n                regex: (.*)\n                target_label: pod\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_pod_container_name]\n                separator: ;\n                regex: (.*)\n                target_label: container\n                replacement: $$1\n                action: replace\n              - source_labels: [__meta_kubernetes_service_name]\n                separator: ;\n                regex: (.*)\n                target_label: job\n                replacement: $$1\n                action: replace\n              - separator: ;\n                regex: (.*)\n                target_label: endpoint\n                replacement: http-web\n                action: replace\n              - source_labels: [__address__]\n                separator: ;\n                regex: (.*)\n                modulus: 1\n                target_label: __tmp_hash\n                replacement: $$1\n                action: hashmod\n              - source_labels: [__tmp_hash]\n                separator: ;\n                regex: \"0\"\n                replacement: $$1\n                action: keep\n              kubernetes_sd_configs:\n                - role: endpoints\n                  kubeconfig_file: \"\"\n                  follow_redirects: true\n                  enable_http2: true\n                  namespaces:\n                    own_namespace: false\n                    names:\n                    - default\n            - job_name: 'kube-state-metrics'\n              static_configs:\n                - targets: ['kube-state-metrics.kube-system.svc.cluster.local:8080']\n            - job_name: 'node-exporter'\n              kubernetes_sd_configs:\n                - role: endpoints\n              ec2_sd_configs:\n              relabel_configs:\n                - source_labels: [ __address__ ]\n                  action: keep\n                  regex: '.*:9100$'\n                - action: replace\n                  source_labels: [__meta_kubernetes_endpoint_node_name]\n                  target_label: nodename\n            {{ start enableJavaMonJob }}\n            - job_name: 'kubernetes-java-jmx'\n              sample_limit: {{javaScrapeSampleLimit}}\n              metrics_path: {{javaPrometheusMetricsEndpoint}}\n              kubernetes_sd_configs:\n                - role: pod\n              relabel_configs:\n                - source_labels: [ __address__ ]\n                  action: keep\n                  regex: '.*:9404$'\n                - action: labelmap\n                  regex: __meta_kubernetes_pod_label_(.+)\n                - action: replace\n                  source_labels: [ __meta_kubernetes_namespace ]\n                  target_label: Namespace\n                - source_labels: [ __meta_kubernetes_pod_name ]\n                  action: replace\n                  target_label: pod_name\n                - action: replace\n                  source_labels: [ __meta_kubernetes_pod_container_name ]\n                  target_label: container_name\n                - action: replace\n                  source_labels: [ __meta_kubernetes_pod_controller_kind ]\n                  target_label: pod_controller_kind\n                - action: replace\n                  source_labels: [ __meta_kubernetes_pod_phase ]\n                  target_label: pod_controller_phase\n              metric_relabel_configs:\n                - source_labels: [ __name__ ]\n                  regex: 'jvm_gc_collection_seconds.*'\n                  action: drop\n            {{ stop enableJavaMonJob }}\n\n            {{ start enableNginxMonJob }}\n            - job_name: 'kubernetes-nginx'\n              sample_limit: {{nginxScrapeSampleLimit}}\n              metrics_path: {{nginxPrometheusMetricsEndpoint}}\n              kubernetes_sd_configs:\n                - role: pod\n              relabel_configs:\n                - source_labels: [ __address__ ]\n                  action: keep\n                  regex: '.*:10254$'\n                - source_labels: [__meta_kubernetes_pod_container_name]\n                  target_label: container\n                  action: replace\n                - source_labels: [__meta_kubernetes_pod_node_name]\n                  target_label: host\n                  action: replace\n                - source_labels: [__meta_kubernetes_namespace]\n                  target_label: namespace\n                  action: replace\n              metric_relabel_configs:\n                - source_labels: [__name__]\n                  regex: 'go_memstats.*'\n                  action: drop\n                - source_labels: [__name__]\n                  regex: 'go_gc.*'\n                  action: drop\n                - source_labels: [__name__]\n                  regex: 'go_threads'\n                  action: drop\n                - regex: exported_host\n                  action: labeldrop\n            {{ stop enableNginxMonJob }}\n\n         {{ start enableIstioMonJob }}\n            - honor_labels: true\n              job_name: kubernetes-istio\n              kubernetes_sd_configs:\n              - role: pod\n              relabel_configs:\n              - action: keep\n                regex: true\n                source_labels:\n                - __meta_kubernetes_pod_annotation_prometheus_io_scrape\n              - action: drop\n                regex: true\n                source_labels:\n                - __meta_kubernetes_pod_annotation_prometheus_io_scrape_slow\n              - action: replace\n                regex: (https?)\n                source_labels:\n                - __meta_kubernetes_pod_annotation_prometheus_io_scheme\n                target_label: __scheme__\n              - action: replace\n                regex: (.+)\n                source_labels:\n                - __meta_kubernetes_pod_annotation_prometheus_io_path\n                target_label: __metrics_path__\n              - action: replace\n                regex: (\\d+);(([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4})\n                replacement: '[$$2]:$$1'\n                source_labels:\n                - __meta_kubernetes_pod_annotation_prometheus_io_port\n                - __meta_kubernetes_pod_ip\n                target_label: __address__\n              - action: replace\n                regex: (\\d+);((([0-9]+?)(\\.|$)){4})\n                replacement: $$2:$$1\n                source_labels:\n                - __meta_kubernetes_pod_annotation_prometheus_io_port\n                - __meta_kubernetes_pod_ip\n                target_label: __address__\n              - action: labelmap\n                regex: __meta_kubernetes_pod_annotation_prometheus_io_param_(.+)\n                replacement: __param_$1\n              - action: labelmap\n                regex: __meta_kubernetes_pod_label_(.+)\n              - action: replace\n                source_labels:\n                - __meta_kubernetes_namespace\n                target_label: namespace\n              - action: replace\n                source_labels:\n                - __meta_kubernetes_pod_name\n                target_label: pod\n              - action: keep\n                source_labels: [ __address__ ]\n                regex: '.*:15020$$'\n              - action: drop\n                regex: Pending|Succeeded|Failed|Completed\n                source_labels:\n                - __meta_kubernetes_pod_phase\n            {{ stop enableIstioMonJob }}\n      {{ start enableAdotContainerLogsReceiver }}\n      filelog:\n        include: [ /var/log/pods/*/*/*.log ]\n        include_file_name: false\n        include_file_path: true\n        start_at: end\n        operators:\n          # Find out which format is used by kubernetes\n          - type: router\n            id: get-format\n            routes:\n              - output: parser-docker\n                expr: 'body matches \"^\\\\{\"'\n              - output: parser-crio\n                expr: 'body matches \"^[^ Z]+ \"'\n              - output: parser-containerd\n                expr: 'body matches \"^[^ Z]+Z\"'\n          # Parse CRI-O format\n          - type: regex_parser\n            id: parser-crio\n            regex:\n              '^(?P<time>[^ Z]+) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*)\n              ?(?P<log>.*)$'\n            output: extract_metadata_from_filepath\n            timestamp:\n              parse_from: attributes.time\n              layout_type: gotime\n              layout: '2006-01-02T15:04:05.999999999Z07:00'\n          # Parse CRI-Containerd format\n          - type: regex_parser\n            id: parser-containerd\n            regex:\n              '^(?P<time>[^ ^Z]+Z) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*)\n              ?(?P<log>.*)$'\n            output: extract_metadata_from_filepath\n            timestamp:\n              parse_from: attributes.time\n              layout: '%Y-%m-%dT%H:%M:%S.%LZ'\n          # Parse Docker format\n          - type: json_parser\n            id: parser-docker\n            output: extract_metadata_from_filepath\n            timestamp:\n              parse_from: attributes.time\n              layout: '%Y-%m-%dT%H:%M:%S.%LZ'\n          - type: move\n            from: attributes.log\n            to: body\n          # Extract metadata from file path\n          - type: regex_parser\n            id: extract_metadata_from_filepath\n            regex: '^.*\\/(?P<namespace>[^_]+)_(?P<pod_name>[^_]+)_(?P<uid>[a-f0-9\\-]{36})\\/(?P<container_name>[^\\._]+)\\/(?P<restart_count>\\d+)\\.log$'\n            parse_from: attributes[\"log.file.path\"]\n            cache:\n              size: 128 # default maximum amount of Pods per Node is 110\n          # Rename attributes\n          - type: move\n            from: attributes.stream\n            to: attributes[\"log.iostream\"]\n          - type: move\n            from: attributes.container_name\n            to: resource[\"k8s.container.name\"]\n          - type: move\n            from: attributes.namespace\n            to: resource[\"k8s.namespace.name\"]\n          - type: move\n            from: attributes.pod_name\n            to: resource[\"k8s.pod.name\"]\n          - type: move\n            from: attributes.restart_count\n            to: resource[\"k8s.container.restart_count\"]\n          - type: move\n            from: attributes.uid\n            to: resource[\"k8s.pod.uid\"]\n      {{ stop enableAdotContainerLogsReceiver }}\n\n    processors:\n      k8sattributes:\n      batch:\n\n    exporters:\n      prometheusremotewrite:\n        endpoint: \"{{remoteWriteEndpoint}}\"\n        auth:\n          authenticator: sigv4auth\n      logging:\n        loglevel: info\n      {{ start enableAdotContainerLogsExporter }}\n      awscloudwatchlogs:\n        log_group_name: \"{{logGroupName}}\"\n        log_stream_name: \"{{logStreamName}}\"\n        region: \"{{awsRegion}}\"\n        log_retention: {{logRetentionDays}}\n        raw_log: false\n      {{ stop enableAdotContainerLogsExporter }}\n    extensions:\n      sigv4auth:\n        region: \"{{awsRegion}}\"\n        service: aps\n      health_check:\n      pprof:\n        endpoint: :1888\n      zpages:\n        endpoint: :55679\n    service:\n      extensions: [pprof, zpages, health_check, sigv4auth]\n      pipelines:\n        metrics:\n          receivers: [prometheus]\n          exporters: [logging, prometheusremotewrite]\n        logs:\n          receivers: [filelog]\n          processors: [batch,k8sattributes]\n          exporters: [awscloudwatchlogs]\n      {{ start enableAdotMetricsCollectionTelemetry }}\n      telemetry:\n        metrics:\n          address: 0.0.0.0:8888\n          level: basic\n      {{ stop enableAdotMetricsCollectionTelemetry }}\n"
  },
  {
    "path": "lib/multi-region-construct/index.ts",
    "content": "// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { utils } from '@aws-quickstart/eks-blueprints';\nimport { KubernetesVersion } from 'aws-cdk-lib/aws-eks';\nimport { Construct } from 'constructs';\n\n// Team implementations\nimport * as team from '../teams';\nconst burnhamManifestDir = './lib/teams/team-burnham/';\nconst rikerManifestDir = './lib/teams/team-riker/';\nconst teamManifestDirList = [burnhamManifestDir,rikerManifestDir];\n\nexport const SECRET_GIT_SSH_KEY = 'github-ssh-key';\nexport const SECRET_ARGO_ADMIN_PWD = 'argo-admin-secret';\n\n\n/**\n * This pattern demonstrates how to roll out a platform across multiple regions and multiple stages.\n * Each region represents a stage in the development process, i.e. dev, test, prod. \n * To use this pattern as is you need to create the following secrets in us-east-1 and replicate them to us-east-2 and us-west-2:\n * - github-ssh-test - containing SSH key for github authentication (plaintext in AWS Secrets manager)\n * - argo-admin-secret - containing the initial admin secret for ArgoCD (e.g. CLI and UI access)\n */\nexport default class MultiRegionConstruct {\n\n    async buildAsync(scope: Construct, id: string) : Promise<blueprints.EksBlueprint[]> {\n        // Setup platform team\n        const accountID = process.env.CDK_DEFAULT_ACCOUNT!;\n        const gitUrl = 'https://github.com/aws-samples/eks-blueprints-workloads.git';\n        const gitSecureUrl = 'git@github.com:aws-samples/eks-blueprints-workloads.git';\n\n        await prevalidateSecrets(); // this checks if required secrets exist in the target regions\n        \n        const blueprint = blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .version('auto')\n            .clusterProvider(new blueprints.MngClusterProvider({\n                version: KubernetesVersion.V1_25,\n                desiredSize: 2,\n                maxSize: 3\n            }))\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.CertManagerAddOn,\n                new blueprints.AdotCollectorAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.CalicoOperatorAddOn,\n                new blueprints.MetricsServerAddOn,\n                new blueprints.VpcCniAddOn,\n                new blueprints.KarpenterAddOn,\n                new blueprints.CloudWatchAdotAddOn,\n                new blueprints.XrayAdotAddOn,\n                new blueprints.SecretsStoreAddOn )\n            .teams( new team.TeamPlatform(accountID),\n                new team.TeamTroiSetup,\n                new team.TeamRikerSetup(scope, teamManifestDirList[1]),\n                new team.TeamBurnhamSetup(scope,teamManifestDirList[0]));\n\n        const devBootstrapArgo = new blueprints.ArgoCDAddOn({\n            bootstrapRepo: {\n                repoUrl: gitUrl,\n                path: 'envs/dev'\n            }\n        });\n\n        const testBootstrapArgo = new blueprints.ArgoCDAddOn({\n            bootstrapRepo: {\n                repoUrl: gitSecureUrl,\n                path: 'envs/test',\n                credentialsSecretName: SECRET_GIT_SSH_KEY,\n                credentialsType: 'SSH'\n            },\n        });\n\n        const prodBootstrapArgo = new blueprints.ArgoCDAddOn({\n            bootstrapRepo: {\n                repoUrl: gitSecureUrl,\n                path: 'envs/prod',\n                credentialsSecretName: SECRET_GIT_SSH_KEY,\n                credentialsType: 'SSH'\n            },\n            adminPasswordSecretName: SECRET_ARGO_ADMIN_PWD,\n        });\n        \n        const east1 = await blueprint.clone('us-east-1')\n            .addOns(devBootstrapArgo)\n            .buildAsync(scope,  `${id}-us-east-1`);\n        \n        const east2 = await blueprint.clone('us-east-2')\n            .addOns(testBootstrapArgo)\n            .buildAsync(scope, `${id}-us-east-2`);\n        \n        const west2 = await blueprint.clone('us-west-2')\n            .addOns(prodBootstrapArgo)\n            .buildAsync(scope, `${id}-us-west-2`);\n\n        return [ east1, east2, west2 ];\n    }\n}\n\nasync function prevalidateSecrets() {\n    try {\n        await utils.validateSecret(SECRET_GIT_SSH_KEY, 'us-east-2');\n        await utils.validateSecret(SECRET_ARGO_ADMIN_PWD, 'us-west-2');\n    }\n    catch(error) {\n        throw new Error(\"Both github-ssh-key and argo-admin-secret secrets must be setup for the multi-region pattern to work.\");\n    }\n}\n\n\n"
  },
  {
    "path": "lib/multi-team-construct/index.ts",
    "content": "import { Construct } from 'constructs';\n// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n\n// Team implementations\nimport * as team from '../teams';\nconst burnhamManifestDir = './lib/teams/team-burnham/';\nconst rikerManifestDir = './lib/teams/team-riker/';\nconst teamManifestDirList = [burnhamManifestDir,rikerManifestDir];\n\nexport default class MultiTeamConstruct {\n    constructor(scope: Construct, id: string) {\n        \n        // Setup platform team\n        const accountID = process.env.CDK_DEFAULT_ACCOUNT!;\n        const platformTeam = new team.TeamPlatform(accountID);\n\n        // Teams for the cluster.\n        const teams: Array<blueprints.Team> = [\n            platformTeam,\n            new team.TeamTroiSetup,\n            new team.TeamRikerSetup(scope, teamManifestDirList[1]),\n            new team.TeamBurnhamSetup(scope, teamManifestDirList[0])\n        ];\n\n        // AddOns for the cluster.\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.AwsLoadBalancerControllerAddOn,\n            new blueprints.CertManagerAddOn,\n            new blueprints.AdotCollectorAddOn,\n            new blueprints.AppMeshAddOn,\n            new blueprints.NginxAddOn,\n            new blueprints.ArgoCDAddOn,\n            new blueprints.CalicoOperatorAddOn,\n            new blueprints.MetricsServerAddOn,\n            new blueprints.ClusterAutoScalerAddOn,\n            new blueprints.CloudWatchAdotAddOn,\n            new blueprints.XrayAddOn,\n            new blueprints.SecretsStoreAddOn\n        ];\n\n        const stackID = `${id}-blueprint`;\n        new blueprints.EksBlueprint(scope, { id: stackID, addOns, teams, version: 'auto' }, {\n            env: {\n                region: 'us-east-2',\n            },\n        });\n    }\n}\n\n\n"
  },
  {
    "path": "lib/newrelic-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { NewRelicAddOn } from '@newrelic/newrelic-eks-blueprints-addon';\nimport { Construct } from \"constructs\";\n\n\nexport default class NewRelicConstruct extends Construct {\n    constructor(scope: Construct, id: string) {\n        super(scope, id);\n\n        const stackId = `${id}-blueprint`;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.addons.SecretsStoreAddOn(),\n            new NewRelicAddOn({\n                version: \"4.2.0-beta\",\n                newRelicClusterName: id,\n                // Uncomment \"awsSecretName\" after you create your secret in AWS Secrets Manager.\n                // Required: nrLicenseKey\n                // Optional: pixieDeployKey, pixieApiKey\n                //\n                // Format:\n                // {\n                //     \"pixieDeployKey\": \"px-dep-XXXX\",\n                //     \"pixieApiKey\": \"px-api-XXXX\",\n                //     \"nrLicenseKey\": \"XXXXNRAL\"\n                // }\n                awsSecretName: \"newrelic-pixie-combined\",\n\n                // Uncomment \"installPixie\" and \"installPixieIntegration\" if installing Pixie.\n                // installPixie: true,\n                // installPixieIntegration: true,\n\n                // For additional install options, visit the New Relic addon docs:\n                // https://github.com/newrelic-experimental/newrelic-eks-blueprints-addon\n            })\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .version('auto')\n            .addOns(...addOns)\n            .build(scope, stackId);\n    }\n}"
  },
  {
    "path": "lib/nginx-ingress-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { DelegatingHostedZoneProvider, GlobalResources, utils } from '@aws-quickstart/eks-blueprints';\nimport { Construct } from 'constructs';\nimport { prevalidateSecrets } from '../common/construct-utils';\nimport { SECRET_ARGO_ADMIN_PWD } from '../multi-region-construct';\nimport * as team from '../teams';\n\nconst burnhamManifestDir = './lib/teams/team-burnham/';\nconst rikerManifestDir = './lib/teams/team-riker/';\nconst teamManifestDirList = [burnhamManifestDir, rikerManifestDir];\n\nconst accountID = process.env.CDK_DEFAULT_ACCOUNT!;\nconst gitUrl = 'https://github.com/aws-samples/eks-blueprints-workloads.git';\n\n\n/**\n * See docs/patterns/nginx.md for mode details on the setup.\n */\nexport default class NginxIngressConstruct {\n\n    async buildAsync(scope: Construct, id: string) {\n\n        await prevalidateSecrets(NginxIngressConstruct.name, undefined, SECRET_ARGO_ADMIN_PWD);\n\n        const teams: Array<blueprints.Team> = [\n            new team.TeamPlatform(accountID),\n            new team.TeamTroiSetup,\n            new team.TeamRikerSetup(scope, teamManifestDirList[1]),\n            new team.TeamBurnhamSetup(scope, teamManifestDirList[0])\n        ];\n\n        const subdomain: string = utils.valueFromContext(scope, \"dev.subzone.name\", \"dev.some.example.com\");\n        const parentDnsAccountId = scope.node.tryGetContext(\"parent.dns.account\")!;\n        const parentDomain = utils.valueFromContext(scope, \"parent.hostedzone.name\", \"some.example.com\");\n\n        blueprints.HelmAddOn.validateHelmVersions = false;\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION!)\n            .teams(...teams)\n            .version('auto')\n            .resourceProvider(GlobalResources.HostedZone, new DelegatingHostedZoneProvider({\n                parentDomain,\n                subdomain,\n                parentDnsAccountId,\n                delegatingRoleName: 'DomainOperatorRole',\n                wildcardSubdomain: true\n            }))\n            .resourceProvider(GlobalResources.Certificate, new blueprints.CreateCertificateProvider('wildcard-cert', `*.${subdomain}`, GlobalResources.HostedZone))\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.VpcCniAddOn(),\n                new blueprints.CoreDnsAddOn(),\n                new blueprints.CalicoOperatorAddOn(),\n                new blueprints.CertManagerAddOn,\n                new blueprints.AdotCollectorAddOn,\n                new blueprints.ExternalDnsAddOn({\n                    hostedZoneResources: [blueprints.GlobalResources.HostedZone] // you can add more if you register resource providers\n                }),\n                new blueprints.NginxAddOn({\n                    internetFacing: true,\n                    backendProtocol: \"tcp\",\n                    externalDnsHostname: subdomain,\n                    crossZoneEnabled: false,\n                    certificateResourceName: GlobalResources.Certificate\n                }),\n                new blueprints.SecretsStoreAddOn({ rotationPollInterval: \"120s\" }),\n                new blueprints.ArgoCDAddOn({\n                    bootstrapRepo: {\n                        repoUrl: gitUrl,\n                        targetRevision: \"deployable\",\n                        path: 'envs/dev'\n                    },\n                    adminPasswordSecretName: SECRET_ARGO_ADMIN_PWD,\n                }),\n                new blueprints.MetricsServerAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n                new blueprints.CloudWatchAdotAddOn)\n            .build(scope, `${id}-blueprint`);\n\n        blueprints.HelmAddOn.validateHelmVersions = false;\n    }\n}\n\n\n"
  },
  {
    "path": "lib/paralus-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { ParalusAddOn } from '@paralus/paralus-eks-blueprints-addon';\n\nexport default class ParalusConstruct {\n    constructor(scope: Construct, id: string) {\n        // AddOns for the cluster\n        const stackId = `${id}-blueprint`;\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns( \n                new blueprints.AwsLoadBalancerControllerAddOn(),\n                new blueprints.VpcCniAddOn(),\n                new blueprints.KubeProxyAddOn(),\n                new blueprints.EbsCsiDriverAddOn(),\n                new blueprints.CertManagerAddOn(),\n                new ParalusAddOn({\n                    namespace: 'paralus-system',\n                    // this signifies the paralus version to be deployed\n                    version: '0.2.3',\n                    // this flag deploys kratos in dev mode, install postgres, by default it is true\n                    development: true,\n                    /**\n                    * Values to pass to the chart as per https://github.com/paralus/helm-charts/blob/main/charts/ztka/values.yaml.\n                    */\n                    // update this to your domain, as paralus works based on domain based routing\n                    values: {\n                        \"fqdn\": {\n                            \"domain\": \"your-own-domain-name\",\n                            \"hostname\": \"console-eks\",\n                            \"coreConnectorSubdomain\": \"*.core-connector.eks\",\n                            \"userSubdomain\": \"*.user.eks\"\n                        }\n                    }\n                })\n            )\n            .teams()// add teams here)\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/pipeline-multi-env-gitops/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { getSecretValue } from '@aws-quickstart/eks-blueprints/dist/utils/secrets-manager-utils';\nimport * as cdk from 'aws-cdk-lib';\nimport { StackProps } from 'aws-cdk-lib';\nimport * as eks from 'aws-cdk-lib/aws-eks';\nimport { Construct } from 'constructs';\n// Team implementations\nimport * as team from '../teams/pipeline-multi-env-gitops';\n\n//pattern wide consts\nconst GITHUB_ORG = 'aws-samples';\nconst CLUSTER_VERSION = eks.KubernetesVersion.V1_26;\nconst WORKLOAD_REPO = `git@github.com:${GITHUB_ORG}/eks-blueprints-workloads.git`;\n\nexport function populateWithContextDefaults(\n    app: cdk.App,\n    defAccount: string,\n    defRegion: string\n) {\n    // build pipeline, dev-tes, and prod accounts\n    const pipelineEnv = buildEnv(app, defAccount, defRegion, 'pipeline');\n    const devEnv = buildEnv(app, defAccount, defRegion, 'dev');\n    const prodEnv = buildEnv(app, defAccount, defRegion, 'prod');\n\n    return { devEnv, pipelineEnv, prodEnv };\n}\n\nexport interface PipelineMultiEnvGitopsProps {\n    /**\n     * The CDK environment where dev&test, prod, and piplines will be deployed to\n     */\n    devTestEnv: cdk.Environment;\n    prodEnv: cdk.Environment;\n    pipelineEnv: cdk.Environment;\n}\n\nexport default class PipelineMultiEnvGitops {\n    readonly DEFAULT_ENV: cdk.Environment = {\n        account: process.env.CDK_DEFAULT_ACCOUNT,\n        region: process.env.CDK_DEFAULT_REGION,\n    };\n\n    async buildAsync(\n        scope: Construct,\n        id: string,\n        pipelineProps: PipelineMultiEnvGitopsProps,\n        props?: StackProps\n    ) {\n        // environments IDs consts\n        const DEV_ENV_ID = `dev-${pipelineProps.devTestEnv.region}`;\n        const TEST_ENV_ID = `test-${pipelineProps.devTestEnv.region}`;\n        const PROD_ENV_ID = `prod-${pipelineProps.prodEnv.region}`;\n\n        try {\n            // github-token is needed for CDK Pipeline functionality\n            await getSecretValue(\n                'github-token',\n                pipelineProps.pipelineEnv.region!\n            ); // Exclamation mark is used to avoid msg: ts(2345)\n        } catch (error) {\n            throw new Error(`github-token secret must be setup in AWS Secrets Manager for the GitHub pipeline.\n                    The GitHub Personal Access Token should have these scopes:\n                    * **repo** - to read the repository\n                    * * **admin:repo_hook** - if you plan to use webhooks (true by default)\n                    * @see https://docs.aws.amazon.com/codepipeline/latest/userguide/GitHub-create-personal-token-CLI.html`);\n        }\n\n        // Fargate provider - only for Karpenter\n        const genClusterProvider = new blueprints.GenericClusterProvider({\n            version: CLUSTER_VERSION,\n            fargateProfiles: {\n                karpenter: {\n                    fargateProfileName: 'karpenter',\n                    selectors: [{ namespace: 'karpenter' }],\n                },\n            },\n        });\n\n        // commonly configured addons\n        const addons: blueprints.ClusterAddOn[] = [\n            new blueprints.AwsLoadBalancerControllerAddOn(),\n            new blueprints.CertManagerAddOn(),\n            new blueprints.SecretsStoreAddOn(),\n            new blueprints.MetricsServerAddOn(),\n        ];\n\n        const blueprint = blueprints.EksBlueprint.builder()\n            .version(CLUSTER_VERSION)\n            .clusterProvider(genClusterProvider)\n            .addOns(...addons);\n\n        // custom addons per environment\n        const devAddons = buildEnvAddons('dev', DEV_ENV_ID);\n        const testAddons = buildEnvAddons('test', DEV_ENV_ID);\n        const prodAddons = buildEnvAddons('prod', PROD_ENV_ID);\n\n        // teams per environment\n        const devTeams = buildTeams('dev', pipelineProps.devTestEnv.account!);\n        const testTeams = buildTeams('test', pipelineProps.devTestEnv.account!);\n        const prodTeams = buildTeams('prod', pipelineProps.prodEnv.account!);\n\n        try {\n            // TODO - add dynamic gitowner suport when using codeStar config const { gitOwner, gitRepositoryName } = await getRepositoryData();\n            const gitRepositoryName = 'cdk-eks-blueprints-patterns';\n\n            blueprints.CodePipelineStack.builder()\n                .application('npx ts-node bin/pipeline-multienv-gitops.ts')\n                .name('eks-blueprint-pipeline')\n                .owner(GITHUB_ORG)\n                .codeBuildPolicies(blueprints.DEFAULT_BUILD_POLICIES)\n                .repository({\n                    repoUrl: gitRepositoryName,\n                    credentialsSecretName: 'github-token',\n                    targetRevision: 'main',\n                })\n                .wave({\n                    id: 'dev-test',\n                    stages: [\n                        {\n                            id: DEV_ENV_ID,\n                            stackBuilder: blueprint\n                                .clone()\n                                .withEnv(pipelineProps.devTestEnv)\n                                .name(DEV_ENV_ID)\n                                .teams(...devTeams)\n                                .addOns(...devAddons),\n                        },\n                        {\n                            id: TEST_ENV_ID,\n                            stackBuilder: blueprint\n                                .clone()\n                                .withEnv(pipelineProps.devTestEnv)\n                                .name(TEST_ENV_ID)\n                                .teams(...testTeams)\n                                .addOns(...testAddons),\n                        },\n                    ],\n                    props: {\n                        post: [\n                            new blueprints.pipelines.cdkpipelines.ManualApprovalStep(\n                                'manual-approval-before-production'\n                            ),\n                        ],\n                    },\n                })\n                .wave({\n                    id: 'prod',\n                    stages: [\n                        {\n                            id: PROD_ENV_ID,\n                            stackBuilder: blueprint\n                                .clone()\n                                .withEnv(pipelineProps.prodEnv)\n                                .name(PROD_ENV_ID)\n                                .teams(...prodTeams)\n                                .addOns(...prodAddons),\n                        },\n                    ],\n                })\n                .build(scope, 'eks-blueprint-pipeline-stack', props);\n        } catch (error) {\n            console.log(error);\n        }\n    }\n}\n\nfunction buildTeams(envId: string, account: string): Array<blueprints.Team> {\n    // Teams ids has to be globally unique --> injecting environment ID\n    const teamsList = [\n        new team.CorePlatformTeam(account, envId),\n        new team.FrontendTeam(account, envId),\n        new team.BackendNodejsTeam(account, envId),\n        new team.BackendCrystalTeam(account, envId),\n    ];\n    return teamsList;\n}\nfunction createArgoAddonConfig(\n    environment: string,\n    repoUrl: string = WORKLOAD_REPO\n): blueprints.ArgoCDAddOn {\n    const argoConfig = new blueprints.ArgoCDAddOn({\n        version: '5.37.0',\n        bootstrapRepo: {\n            repoUrl: repoUrl,\n            path: `multi-repo/argo-app-of-apps/${environment}`,\n            targetRevision: 'main',\n            credentialsSecretName: 'github-ssh-key',\n            credentialsType: 'SSH',\n        },\n        bootstrapValues: {\n            service: {\n                type: 'LoadBalancer',\n            },\n            spec: {\n                ingress: {\n                    host: 'dev.blueprint.com',\n                },\n            },\n        },\n        values: {\n            server: {},\n        },\n    });\n\n    return argoConfig;\n}\n\nfunction buildKarpenterConfig(environment: string): object {\n    return {\n        subnetTags: {\n            'aws:cloudformation:stack-name': `${environment}-${environment}-blueprint`,\n        },\n        securityGroupTags: {\n            'aws:eks:cluster-name': `${environment}-blueprint`,\n        },\n        interruptionHandling: true,\n    };\n}\n\nfunction buildEnv(\n    app: cdk.App,\n    defaultAccount: string,\n    defaultRegion: string,\n    envName: string\n): cdk.Environment {\n    // Populate Context Defaults for the an account\n    let account = app.node.tryGetContext(`${envName}_account`);\n    account = account ?? defaultAccount;\n    let region = app.node.tryGetContext(`${envName}_region`);\n    region = region ?? defaultRegion;\n    const env: cdk.Environment = {\n        account: account,\n        region: region,\n    };\n\n    return env;\n}\n\nfunction buildEnvAddons(\n    envName: string,\n    envId: string\n): blueprints.ClusterAddOn[] {\n    return [\n        new blueprints.KarpenterAddOn(buildKarpenterConfig(envId)),\n        createArgoAddonConfig(envName),\n    ];\n}\n"
  },
  {
    "path": "lib/pipeline-stack/index.ts",
    "content": "import { Construct } from 'constructs';\nimport { StackProps } from 'aws-cdk-lib';\n// Blueprints Lib\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n// Team implementations\nimport * as team from '../teams';\n\nconst burnhamManifestDir = './lib/teams/team-burnham/';\nconst rikerManifestDir = './lib/teams/team-riker/';\nconst teamManifestDirList = [burnhamManifestDir,rikerManifestDir];\n\n\nexport default class PipelineConstruct {\n\n    async buildAsync(scope: Construct, props?: StackProps) {\n    \n        await this.prevalidateSecrets();\n\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const blueprint = blueprints.EksBlueprint.builder()\n            .account(account) // the supplied default will fail, but build and synth will pass\n            .region('us-west-1')\n            .version('auto')\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn, \n                new blueprints.CertManagerAddOn,\n                new blueprints.AdotCollectorAddOn,\n                new blueprints.NginxAddOn,\n                new blueprints.ArgoCDAddOn,\n                new blueprints.AppMeshAddOn( {\n                    enableTracing: true\n                }),\n                new blueprints.SSMAgentAddOn, // this is added to deal with PVRE as it is adding correct role to the node group, otherwise stack destroy won't work\n                new blueprints.CalicoOperatorAddOn,\n                new blueprints.MetricsServerAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n                new blueprints.CloudWatchAdotAddOn,\n                new blueprints.XrayAdotAddOn,\n                new blueprints.SecretsStoreAddOn)\n            .teams(\n                new team.TeamRikerSetup(scope, teamManifestDirList[1]),\n                new team.TeamBurnhamSetup(scope, teamManifestDirList[0])\n            );\n\n        blueprints.CodePipelineStack.builder()\n            .application(\"npx ts-node bin/pipeline.ts\")\n            .name(\"blueprints-eks-pipeline\")\n            .owner(\"aws-samples\")\n            .codeBuildPolicies(blueprints.DEFAULT_BUILD_POLICIES)\n            .repository({\n                repoUrl: 'cdk-eks-blueprints-patterns',\n                credentialsSecretName: 'github-token',\n                targetRevision: 'main'\n            })\n            .stage({\n                id: 'us-west-1-sandbox',\n                stackBuilder: blueprint.clone('us-west-1')\n            })\n            .wave( {\n                id: \"dev\",\n                stages: [\n                    { id: \"dev-west-1\", stackBuilder: blueprint.clone('us-west-1')},\n                    { id: \"dev-east-2\", stackBuilder: blueprint.clone('us-east-2')},\n                ]\n            })\n            .stage({\n                id: 'us-east-2-uat',\n                stackBuilder: blueprint.clone('us-east-2'),\n                stageProps: {\n                    pre: [new blueprints.pipelines.cdkpipelines.ManualApprovalStep('manual-approval')]\n                }\n            })\n            .wave( {\n                id: \"prod\",\n                stages: [\n                    { id: \"prod-west-1\", stackBuilder: blueprint.clone('us-west-1')},\n                    { id: \"prod-east-2\", stackBuilder: blueprint.clone('us-east-2')},\n                ]\n            })\n            .build(scope, \"pipeline\", props);\n    }\n\n    async prevalidateSecrets() {\n        try {\n            await blueprints.utils.validateSecret('github-token', 'us-east-2');\n            await blueprints.utils.validateSecret('github-token', 'us-west-1');\n        }\n        catch(error) {\n            throw new Error(`github-token secret must be setup in AWS Secrets Manager for the GitHub pipeline.\n            The GitHub Personal Access Token should have these scopes:\n            * **repo** - to read the repository\n            * * **admin:repo_hook** - if you plan to use webhooks (true by default)\n            * @see https://docs.aws.amazon.com/codepipeline/latest/userguide/GitHub-create-personal-token-CLI.html`);\n        }\n    }\n}"
  },
  {
    "path": "lib/rafay-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as rafayAddOn from '@rafaysystems/rafay-eks-blueprints-addon';\nimport { prevalidateSecrets } from '../common/construct-utils';\nimport * as cdk from 'aws-cdk-lib';\n\nexport default class RafayConstruct {\n    async buildAsync(scope: cdk.App, id: string) {\n        let rafayConfig = {\n            organizationName: \"rafay-eks-org-1\", // replace with your organization Name\n            email: \"abc@example.com\", // replace with your email\n            firstName: \"John\", // replace with your first Name\n            lastName: \"Doe\", // replace with your last Name\n            passwordSecret: \"rafay-password-json\", // replace with a secret name in secrets manager that you have created, must contain a single field \"password\"\n            clusterName: \"eks-cluster-1\", // replace with the name that you want the cluster to be created in Rafay Console\n            blueprintName: \"minimal\"\n        } as rafayAddOn.RafayConfig;\n\n        await prevalidateSecrets(RafayConstruct.name, undefined, rafayConfig.passwordSecret!);\n        const stackId = `${id}-blueprint`;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new rafayAddOn.RafayClusterAddOn(rafayConfig)\n        ];\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(...addOns)\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/secure-ingress-auth-cognito/index.ts",
    "content": "import 'source-map-support/register';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { LookupHostedZoneProvider, GlobalResources, utils } from '@aws-quickstart/eks-blueprints';\nimport { KubecostAddOn } from '@kubecost/kubecost-eks-blueprints-addon';\nimport * as cdk from 'aws-cdk-lib';\nimport { Construct } from 'constructs';\nimport { prevalidateSecrets } from '../common/construct-utils';\nimport { SECRET_ARGO_ADMIN_PWD } from '../multi-region-construct';\nimport * as cognito from 'aws-cdk-lib/aws-cognito';\nimport * as lambda from 'aws-cdk-lib/aws-lambda';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';\n\nconst gitUrl = 'https://github.com/aws-samples/eks-blueprints-workloads.git';\n\n//Class Cognito Stack \n\n/**\n * Stack creates the cognito user pool, app client, configure the client and app client domain. \n * Amazon Cognito hosted UI provides you an OAuth2.0 compliant authorization server that provides default implementation of end user flows such as registration, authentication, and so on.\n */\n\nclass CognitoIdpStack extends cdk.Stack {\n\n    public readonly userPoolOut: cognito.UserPool;\n    public readonly userPoolClientOut: cognito.UserPoolClient;\n    public readonly userPoolDomainOut: cognito.UserPoolDomain;\n    \n    constructor(scope: Construct, id: string, subDomain: string, props?: cdk.StackProps) {\n        super(scope, id, props);\n\n        const lambdaExecutionRole = new iam.Role(this, 'Lambda Execution Role', {\n            assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),\n        });\n\n        lambdaExecutionRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName(\"service-role/AWSLambdaBasicExecutionRole\"));\n        lambdaExecutionRole.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName(\"AmazonSSMReadOnlyAccess\"));     \n        \n        const authChallengeFn = new lambda.Function(this, 'authChallengeFn', {\n            runtime: lambda.Runtime.PYTHON_3_12,\n            code: lambda.Code.fromAsset('./lib/secure-ingress-auth-cognito/lambda'),\n            handler: 'lambda_function.lambda_handler',\n            role: lambdaExecutionRole,\n        });\n\n\n        // Cognito User Pool\n        const userPool = new cognito.UserPool(this, 'CognitoIDPUserPool', {\n            userPoolName: 'CognitoIDPUserPool',\n            selfSignUpEnabled: true,\n            signInAliases: {\n                email: true,\n                username: true\n            },\n            standardAttributes: {\n                email: {\n                    mutable: true,\n                    required: true\n                },\n                givenName: {\n                    mutable: true,\n                    required: true\n                },\n                familyName: {\n                    mutable: true,\n                    required: true\n                }\n            },\n            lambdaTriggers: {\n                preSignUp: authChallengeFn,\n                preAuthentication: authChallengeFn,\n            },            \n        });\n        \n        \n        // Output the User Pool ID\n\n        this.userPoolOut = userPool;\n        \n        new cdk.CfnOutput(this, 'CognitoIDPUserPoolOut', {\n            value: userPool.userPoolId,\n            exportName: 'CognitoIDPUserPoolId'\n        });\n        \n        new cdk.CfnOutput(this, 'CognitoIDPUserPoolArnOut', {\n            value: userPool.userPoolArn,\n            exportName: 'CognitoIDPUserPoolArn'\n        });\n\n\n        // We will ask the IDP to redirect back to our domain's index page\n        const redirectUri = `https://${subDomain}/oauth2/idpresponse`;\n      \n        // Configure the user pool client application \n        const userPoolClient = new cognito.UserPoolClient(this, 'CognitoAppClient', {\n            userPool,\n            authFlows: {\n                userPassword: true\n            },\n            oAuth: {\n                flows: {\n                    authorizationCodeGrant: true\n                },\n                scopes: [\n                    cognito.OAuthScope.OPENID\n                ],\n                callbackUrls: [redirectUri]\n                // TODO - What about logoutUrls?\n            },\n            generateSecret: true,\n            userPoolClientName: 'Web',\n            supportedIdentityProviders: [cognito.UserPoolClientIdentityProvider.COGNITO]\n        });\n\n        // Output the User Pool App Client ID\n        this.userPoolClientOut = userPoolClient;\n\n        new cdk.CfnOutput(this, 'CognitoIDPUserPoolClientOut', {\n            value: userPoolClient.userPoolClientId,\n            exportName: 'CognitoIDPUserPoolClientId'\n        });\n\n        // Add the domain to the user pool\n        const randomText = (Math.random() + 1).toString(36).substring(7);\n        const userPoolDomain = userPool.addDomain('CognitoDomain', {\n            cognitoDomain: {\n                domainPrefix: `my-cdk-blueprint-${randomText}`,\n            },\n        });\n\n        // Output the User Pool App Client ID\n\n        this.userPoolDomainOut = userPoolDomain;\n    \n        new cdk.CfnOutput(this, 'CognitoIDPUserPoolDomainOut', {\n            value: userPoolDomain.domainName,\n            exportName: 'CognitoIDPUserPoolDomain'\n        });\n        \n    }\n}\n\n\n/**\n * See docs/patterns/secure-ingress-cognito.md for mode details on the setup.\n */\nexport class SecureIngressCognito extends cdk.Stack{\n\n    async buildAsync(scope: Construct, id: string) {\n\n        await prevalidateSecrets(SecureIngressCognito.name, undefined, SECRET_ARGO_ADMIN_PWD);\n\n        const subdomain: string = utils.valueFromContext(scope, \"dev.subzone.name\", \"dev.mycompany.a2z.com\");\n        const parentDomain = utils.valueFromContext(scope, \"parent.hostedzone.name\", \"mycompany.a2z.com\");\n        const certificate: ICertificate = blueprints.getNamedResource(GlobalResources.Certificate);\n\n        const cognitoIdpStackOut = new CognitoIdpStack (scope,'cognito-idp-stack', subdomain,\n            {\n                env: {\n                    account: process.env.CDK_DEFAULT_ACCOUNT,\n                    region: process.env.CDK_DEFAULT_REGION,\n                },\n            }\n        );\n\n        blueprints.HelmAddOn.validateHelmVersions = false;\n\n        await blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .version('auto')\n            .resourceProvider(GlobalResources.HostedZone, new LookupHostedZoneProvider(parentDomain))\n            .resourceProvider(GlobalResources.Certificate, new blueprints.CreateCertificateProvider('secure-ingress-cert', `${subdomain}`, GlobalResources.HostedZone))\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.VpcCniAddOn(),\n                new blueprints.CoreDnsAddOn(),\n                new KubecostAddOn(),\n                new blueprints.addons.EbsCsiDriverAddOn(),\n                new blueprints.ExternalDnsAddOn({\n                    hostedZoneResources: [GlobalResources.HostedZone] // you can add more if you register resource providers\n                }),\n                new blueprints.SecretsStoreAddOn({ rotationPollInterval: \"120s\" }),\n                new blueprints.ArgoCDAddOn({\n                    bootstrapRepo: {\n                        repoUrl: gitUrl,\n                        targetRevision: \"main\",\n                        path: 'secure-ingress-cognito/envs/dev'\n                    },\n                    bootstrapValues: {\n                        spec: {\n                            ingress: {\n                                host: subdomain,\n                                cognitoUserPoolArn: cognitoIdpStackOut.userPoolOut.userPoolArn,\n                                cognitoUserPoolAppId: cognitoIdpStackOut.userPoolClientOut.userPoolClientId,\n                                cognitoDomainName: cognitoIdpStackOut.userPoolDomainOut.domainName,\n                                certificateArn: certificate.certificateArn,\n                                region: process.env.CDK_DEFAULT_REGION,\n                            }\n                        },\n                    },\n                    adminPasswordSecretName: SECRET_ARGO_ADMIN_PWD,\n                }),\n            )\n            .buildAsync(scope, `${id}-blueprint`);\n\n        blueprints.HelmAddOn.validateHelmVersions = false; \n    }\n}\n\n\n"
  },
  {
    "path": "lib/secure-ingress-auth-cognito/lambda/lambda_function.py",
    "content": "import json\nimport os\nimport boto3\n\ndef lambda_handler(event, context):\n    print(\"Received event: \" + json.dumps(event, indent=2))\n    \n    ssmclient = boto3.client('ssm')\n    \n    try:    \n        paramName = '/secure-ingress-auth-cognito/ALLOWED_DOMAINS'\n        resp = ssmclient.get_parameter(Name=paramName)\n        allowed_domains_list = resp['Parameter']['Value']\n\n    except Exception as e:\n        print(\"Error in reading the SSM Parameter Store : {}\".format(str(e)))   \n        \n    triggerSource = event['triggerSource']\n    \n    # Split the email address so we can compare domains\n    emailId = event['request']['userAttributes']['email']\n    address = emailId.split('@')\n    #print(\"address={} allowed_domains_list={} auto_approved_domains_list={} email_allow_list={}\".format(address,  allowed_domains_list, auto_approved_domains_list, email_white_list))\n    \n    emailDomain = address[1]    \n    \n    print(\"Running the Validation for {} flow\".format(triggerSource))\n    \n    if triggerSource == 'PreSignUp_SignUp':\n        # It sets the user pool autoConfirmUser flag after validating the email domain\n        event['response']['autoConfirmUser'] = False\n\n        # This example uses a custom attribute 'custom:domain'\n        if emailDomain not in allowed_domains_list:\n            raise Exception(\"Cannot register users with email domains other than allowed domains list={}\".format(allowed_domains_list))\n    else:\n        print(\"triggerSource={} is incorrect\".format(triggerSource))\n\n    #print(\"Received event: \" + json.dumps(event, indent=2))\n    \n    return event\n"
  },
  {
    "path": "lib/security/data-at-rest-encryption/index.ts",
    "content": "import * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport {\n    ArgoCDAddOn,\n    EbsCsiDriverAddOn,\n    EksBlueprint,\n    GlobalResources,\n} from \"@aws-quickstart/eks-blueprints\";\nimport * as efs from \"aws-cdk-lib/aws-efs\";\nimport * as kms from \"aws-cdk-lib/aws-kms\";\nimport { prevalidateSecrets } from \"../../common/construct-utils\";\nimport { Construct } from \"constructs\";\nimport { SECRET_ARGO_ADMIN_PWD } from \"../../multi-region-construct\";\n\nconst gitUrl = \"https://github.com/aws-samples/eks-blueprints-workloads.git\";\nconst targetRevision = \"main\";\n\nexport default class DataAtRestEncryptionConstruct {\n    async buildAsync(scope: Construct, id: string) {\n\n        await prevalidateSecrets(DataAtRestEncryptionConstruct.name, process.env.CDK_DEFAULT_REGION!, SECRET_ARGO_ADMIN_PWD);\n\n        const stackId = `${id}-blueprint`;\n\n        const ebsKmsKeyName = \"ebs-kms-encryption-key\";\n        const ebsKmsKey = blueprints.getNamedResource(ebsKmsKeyName) as kms.Key;\n\n        const efsKmsKeyName = \"efs-kms-encryption-key\";\n        const efsKmsKey = blueprints.getNamedResource(efsKmsKeyName) as kms.Key;\n\n        const efsFileSystemName = \"efs-file-system\";\n        const efsFileSystem = blueprints.getNamedResource(\n            efsFileSystemName\n        ) as efs.FileSystem;\n\n        await EksBlueprint.builder()\n            .resourceProvider(GlobalResources.Vpc, new blueprints.VpcProvider())\n        // create KMS keys\n            .resourceProvider(\n                GlobalResources.KmsKey,\n                new blueprints.CreateKmsKeyProvider()\n            )\n            .resourceProvider(\n                ebsKmsKeyName,\n                new blueprints.CreateKmsKeyProvider(ebsKmsKeyName)\n            )\n            .resourceProvider(\n                efsKmsKeyName,\n                new blueprints.CreateKmsKeyProvider(efsKmsKeyName)\n            )\n        // create EFS file system\n            .resourceProvider(\n                efsFileSystemName,\n                new blueprints.CreateEfsFileSystemProvider({\n                    name: efsFileSystemName,\n                    kmsKeyResourceName: efsKmsKeyName,\n                    efsProps: {\n                        encrypted: true,\n                    },\n                })\n            )\n            .addOns(\n                new EbsCsiDriverAddOn({\n                    version: \"auto\",\n                    kmsKeys: [ebsKmsKey],\n                }),\n                new blueprints.EfsCsiDriverAddOn({\n                    kmsKeys: [efsKmsKey],\n                }),\n                new ArgoCDAddOn({\n                    bootstrapRepo: {\n                        repoUrl: gitUrl,\n                        targetRevision: targetRevision,\n                        path: \"security/envs/dev\",\n                    },\n                    bootstrapValues: {\n                        spec: {\n                            efsKmsKey: efsKmsKey.keyArn,\n                            efsFileSystemId: efsFileSystem.fileSystemId,\n                            ebsKmsKey: ebsKmsKey.keyArn,\n                        },\n                    },\n                    adminPasswordSecretName: SECRET_ARGO_ADMIN_PWD,\n                })\n            )\n            .teams()\n            .version('auto')\n            .buildAsync(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/security/eks-config-rules/config-setup.ts",
    "content": "import * as config from \"aws-cdk-lib/aws-config\";\nimport * as events from \"aws-cdk-lib/aws-events\";\nimport * as eventTargets from \"aws-cdk-lib/aws-events-targets\";\nimport * as iam from \"aws-cdk-lib/aws-iam\";\nimport * as s3 from \"aws-cdk-lib/aws-s3\";\nimport * as sns from \"aws-cdk-lib/aws-sns\";\nimport * as subs from \"aws-cdk-lib/aws-sns-subscriptions\";\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { Construct } from \"constructs\";\nimport { Stack, StackProps } from \"aws-cdk-lib\";\nimport { \n    ConfigServiceClient, \n    DescribeConfigurationRecordersCommand, \n    DescribeDeliveryChannelsCommand \n} from \"@aws-sdk/client-config-service\";\n\n\n// Enable the AWS Config Managed Rules for EKS Security Best Pratices\nexport class EksConfigSetup extends Stack {\n    constructor(scope: Construct, id: string, props?: StackProps) {\n        super(scope, id, props);\n        this.buildConfigStack();\n    }\n\n    async buildConfigStack() {\n        const email = \"your-email@example.com\";\n        const logger = blueprints.utils.logger;\n        const currentRegion = process.env.CDK_DEFAULT_REGION!;\n        const configclient = new ConfigServiceClient();\n\n        try {\n            const command = new DescribeConfigurationRecordersCommand();\n            const response = await configclient.send(command);\n\n            if (response.ConfigurationRecorders && response.ConfigurationRecorders.length > 0) {\n                logger.info(`AWS Config is already enabled in ${currentRegion} region.`);\n            } else {\n                logger.info(`AWS Config is not enabled in ${currentRegion} region.`);\n                logger.info(\"Enabling AWS Config...\");\n\n                // Create an AWS Config service role\n                const awsConfigRole = new iam.Role(this, \"RoleAwsConfig\", {\n                    assumedBy: new iam.ServicePrincipal(\"config.amazonaws.com\"),\n                });\n\n                // Attach the service role policy\n                awsConfigRole.addManagedPolicy(\n                    iam.ManagedPolicy.fromAwsManagedPolicyName(\"service-role/AWS_ConfigRole\")\n                );\n\n                // Check if delivery channel is already enabled\n                try {\n                    const command = new DescribeDeliveryChannelsCommand();\n                    const response = await configclient.send(command);\n\n                    if (response.DeliveryChannels && response.DeliveryChannels.length > 0) {\n                        logger.info(`AWS Config delivery channel is already enabled in ${currentRegion} region.`);\n                    } else {\n                        logger.info(`AWS Config delivery channel is not enabled in ${currentRegion} region.`);\n                        logger.info(\"Configuring AWS Config delivery channel...\");\n                        \n                        // Create an AWS Config delivery channel\n                        // Setup an s3 bucket for the config recorder delivery channel\n                        const awsConfigBucket = new s3.Bucket(this, \"BucketAwsConfig\", {\n                            versioned: true, enforceSSL: true,\n                        });\n\n                        // Configure bucket policy statements and attach them to the s3 bucket\n                        this.configureS3BucketPolicy(awsConfigBucket);\n\n                        // Create an SNS topic for AWS Config notifications\n                        const configTopic = new sns.Topic(this, \"ConfigNotificationTopic\");\n                        configTopic.addSubscription(new subs.EmailSubscription(email));\n                        const eventRule = new events.Rule(this, \"ConfigEventRule\", {\n                            eventPattern: {\n                                source: [\"aws.config\"],\n                                detailType: [\"Config Rules Compliance Change\"],\n                            },\n                        });\n\n                        // Format the Config notifications\n                        this.configureEventRule(eventRule, configTopic);\n\n                        // Create the AWS Config delivery channel with the s3 bucket and sns topic\n                        new config.CfnDeliveryChannel(this, \"DeliveryChannel\", {\n                            name: \"default\",\n                            s3BucketName: awsConfigBucket.bucketName,\n                            snsTopicArn: configTopic.topicArn,\n                        });\n                    }\n                } catch (error) {\n                    logger.error(error);\n                }\n\n                logger.info(\"Configuring AWS Config recorder...\");\n\n                // Create the AWS Config recorder\n                new config.CfnConfigurationRecorder(this, \"Recorder\", {\n                    name: \"default\",\n                    roleArn: awsConfigRole.roleArn,\n                });\n            }\n        } catch (error) {\n            logger.error(error);\n        }\n    }\n\n    private configureS3BucketPolicy(awsConfigBucket: s3.Bucket) {\n        const policyStatement1 = new iam.PolicyStatement({\n            actions: [\"s3:*\"],\n            principals: [new iam.AnyPrincipal()],\n            resources: [`${awsConfigBucket.bucketArn}/*`],\n            conditions: { Bool: { \"aws:SecureTransport\": false } },\n        });\n\n        policyStatement1.effect = iam.Effect.DENY;\n        awsConfigBucket.addToResourcePolicy(policyStatement1);\n\n        const policyStatement2 = new iam.PolicyStatement({\n            actions: [\"s3:PutObject\"],\n            principals: [new iam.ServicePrincipal(\"config.amazonaws.com\")],\n            resources: [`${awsConfigBucket.bucketArn}/*`],\n            conditions: {\n                StringEquals: { \"s3:x-amz-acl\": \"bucket-owner-full-control\" },\n            },\n        });\n\n        policyStatement2.effect = iam.Effect.ALLOW;\n        awsConfigBucket.addToResourcePolicy(policyStatement2);\n\n        const policyStatement3 = new iam.PolicyStatement({\n            actions: [\"s3:GetBucketAcl\"],\n            principals: [new iam.ServicePrincipal(\"config.amazonaws.com\")],\n            resources: [awsConfigBucket.bucketArn],\n        });\n\n        policyStatement3.effect = iam.Effect.ALLOW;\n        awsConfigBucket.addToResourcePolicy(policyStatement3);\n    }\n\n    private configureEventRule(eventRule: events.Rule, configTopic: sns.Topic) {\n        eventRule.addTarget(\n            new eventTargets.SnsTopic(configTopic, {\n                message: events.RuleTargetInput.fromText(\n                    `WARNING: AWS Config has detected a ${events.EventField.fromPath(\n                        \"$.detail.newEvaluationResult.complianceType\"\n                    )} for the rule ${events.EventField.fromPath(\n                        \"$.detail.configRuleName\"\n                    )}. The compliance status is ${events.EventField.fromPath(\n                        \"$.detail.newEvaluationResult.evaluationResult\"\n                    )}.`\n                ),\n            })\n        );\n    }\n}\n"
  },
  {
    "path": "lib/security/eks-config-rules/index.ts",
    "content": "import * as defaultKubeVersion from \"@aws-quickstart/eks-blueprints/dist/stacks\";\nimport * as config from \"aws-cdk-lib/aws-config\";\nimport { Construct } from \"constructs\";\nimport { Stack, StackProps } from \"aws-cdk-lib\";\n\n\n// Enable the AWS Config Managed Rules for EKS Security Best Pratices\nexport class EksConfigRulesSetup extends Stack {\n    constructor(scope: Construct, id: string, props?: StackProps) {\n        super(scope, id, props);\n\n        // Get the default kubernetes version used by the CDK EKS Blueprints framework.\n        const defaultKubeVerison = defaultKubeVersion.DEFAULT_VERSION;\n\n        // Checks if an Amazon Elastic Kubernetes Service (EKS) cluster is running a supported Kubernetes version.\n        new config.ManagedRule(this, \"EksOldestSupportedVersion\", {\n            identifier:\n        config.ManagedRuleIdentifiers.EKS_CLUSTER_OLDEST_SUPPORTED_VERSION,\n            inputParameters: {\n                oldestVersionSupported: defaultKubeVerison, // Set to the default cluster version used by CDK EKS Blueprints.\n            },\n        });\n\n        // Checks whether Amazon Elastic Kubernetes Service (Amazon EKS) endpoint is not publicly accessible.\n        new config.ManagedRule(this, \"EksSupportedVersion\", {\n            identifier: config.ManagedRuleIdentifiers.EKS_CLUSTER_SUPPORTED_VERSION,\n            inputParameters: {\n                oldestVersionSupported: defaultKubeVerison, //  Set to the default cluster version used by CDK EKS Blueprints.\n            },\n        });\n\n        // Checks whether Amazon Elastic Kubernetes Service (Amazon EKS) endpoint is not publicly accessible.\n        new config.ManagedRule(this, \"EksEndpointNoPublicAccess\", {\n            identifier: config.ManagedRuleIdentifiers.EKS_ENDPOINT_NO_PUBLIC_ACCESS,\n        });\n\n        // Checks whether Amazon Elastic Kubernetes Service clusters are configured to have Kubernetes secrets encrypted using AWS Key Management Service (KMS) keys.\n        new config.ManagedRule(this, \"EksSecretsEncrypted\", {\n            identifier: config.ManagedRuleIdentifiers.EKS_SECRETS_ENCRYPTED,\n        });\n    }\n}"
  },
  {
    "path": "lib/security/guardduty-construct/guardduty-setup.ts",
    "content": "import * as aws_guardduty from \"aws-cdk-lib/aws-guardduty\";\nimport { Construct } from \"constructs\";\nimport { Stack, StackProps } from \"aws-cdk-lib\";\nimport * as sns from \"aws-cdk-lib/aws-sns\";\nimport * as subs from \"aws-cdk-lib/aws-sns-subscriptions\";\nimport * as events from \"aws-cdk-lib/aws-events\";\nimport * as eventTargets from \"aws-cdk-lib/aws-events-targets\";\nimport * as AWS from \"aws-sdk\";\n\nconst account = process.env.CDK_DEFAULT_ACCOUNT;\nconst region = process.env.CDK_DEFAULT_REGION;\n\nexport class GuardDutySetupStack extends Stack {\n    constructor(scope: Construct, id: string, props?: StackProps) {\n        super(scope, id, { ...props, env: { account, region } });\n\n        const environmentName = \"main\";\n        const email = \"your-email@example.com\";\n        const features: aws_guardduty.CfnDetector.CFNFeatureConfigurationProperty[] =\n      [\n          { name: \"S3_DATA_EVENTS\", status: \"ENABLED\" },\n          { name: \"EKS_AUDIT_LOGS\", status: \"ENABLED\" },\n          { name: \"EBS_MALWARE_PROTECTION\", status: \"ENABLED\" },\n          { name: \"RDS_LOGIN_EVENTS\", status: \"ENABLED\" },\n          { name: \"LAMBDA_NETWORK_LOGS\", status: \"ENABLED\" },\n          { \n              name: \"RUNTIME_MONITORING\",\n              status: \"ENABLED\",\n              additionalConfiguration: [\n                  { name: \"EKS_ADDON_MANAGEMENT\", status: \"ENABLED\" },\n                  { name: \"ECS_FARGATE_AGENT_MANAGEMENT\", status: \"ENABLED\" },\n                  { name: \"EC2_AGENT_MANAGEMENT\", status: \"ENABLED\" },\n              ],\n          },\n      ];\n\n        // check if GuardDuty is already enabled in the region\n        const guardDuty = new AWS.GuardDuty();\n        guardDuty.listDetectors({}, (err, data) => {\n            if (err) {\n                console.log(err, err.stack);\n            } else {\n                if (data.DetectorIds?.length === 0) {\n                    // Create a GuardDuty detector\n                    new aws_guardduty.CfnDetector(this, id + \"GuardDutyDetector\", {\n                        enable: true,\n                        features,\n                    });\n\n                    // Configure GuardDuty to email any security findings\n                    const guardDutyTopic = new sns.Topic(\n                        this,\n                        id + \"GuardDutyNotificationTopic\"\n                    );\n                    guardDutyTopic.addSubscription(new subs.EmailSubscription(email));\n                    const eventRule = new events.Rule(this, id + \"GuardDutyEventRule\", {\n                        eventPattern: {\n                            source: [\"aws.guardduty\"],\n                            detailType: [\"GuardDuty Finding\"],\n                        },\n                    });\n\n                    // Format the GuardDuty findings emails\n                    eventRule.addTarget(\n                        new eventTargets.SnsTopic(guardDutyTopic, {\n                            message: events.RuleTargetInput.fromText(\n                                `WARNING: AWS GuardDuty has discovered a ${events.EventField.fromPath(\n                                    \"$.detail.type\"\n                                )} security issue for ${environmentName} (${events.EventField.fromPath(\n                                    \"$.region\"\n                                )}). Please go to https://${events.EventField.fromPath(\n                                    \"$.region\"\n                                )}.console.aws.amazon.com/guardduty/ to find out more details.`\n                            ),\n                        })\n                    );\n                    return;\n                } else {\n                    console.log(\"GuardDuty is enabled in this region.\");\n                }\n\n                // Update the existing detector to use the EKS features\n                console.log(\"Updating the detector to make sure EKS features are enabled.\");\n                const detectorId = data.DetectorIds[0];\n                console.log(\"Detector ID: \" + detectorId);\n                const params: AWS.GuardDuty.UpdateDetectorRequest = {\n                    DetectorId: detectorId,\n                    Features: [\n                        {\n                            AdditionalConfiguration: [\n                                {\n                                    Name: \"EKS_ADDON_MANAGEMENT\",\n                                    Status: \"ENABLED\",\n                                },\n                            ],\n                            Name: \"RUNTIME_MONITORING\",\n                            Status: \"ENABLED\",\n                        },\n                        {\n                            Name: \"EKS_AUDIT_LOGS\",\n                            Status: \"ENABLED\",\n                        },\n                    ]\n                };\n                guardDuty.updateDetector(params, (err, data) => {\n                    if (err) {\n                        console.log(err, err.stack);\n                    } else {\n                        console.log(\"Updated GuardDuty detector with EKS features.\");\n                    }\n                });\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/security/guardduty-construct/index.ts",
    "content": "import { Construct } from \"constructs\";\nimport * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport { SECRET_ARGO_ADMIN_PWD } from \"../../multi-region-construct\";\nimport { prevalidateSecrets } from \"../../common/construct-utils\";\n\nconst gitUrl = \"https://github.com/aws-samples/eks-blueprints-workloads.git\";\nconst targetRevision = \"main\";\n\nexport default class GuardDutyWorkloadConstruct {\n    async buildAsync(scope: Construct, id: string) {\n        await prevalidateSecrets(\n            GuardDutyWorkloadConstruct.name, \n      process.env.CDK_DEFAULT_REGION!,\n      SECRET_ARGO_ADMIN_PWD\n        );\n\n        const stackID = `${id}-blueprint`;\n\n        await blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION!)\n            .addOns(\n                new blueprints.ArgoCDAddOn({\n                    bootstrapRepo: {\n                        repoUrl: gitUrl,\n                        targetRevision: targetRevision,\n                        path: \"teams/team-danger/dev\",\n                    },\n                    adminPasswordSecretName: SECRET_ARGO_ADMIN_PWD,\n                })\n            )\n            .teams()\n            .version('auto')\n            .buildAsync(scope, stackID);\n    }\n}\n"
  },
  {
    "path": "lib/security/image-vulnerability-scanning/image-scanning-setup.ts",
    "content": "import { Construct } from \"constructs\";\nimport * as cr from \"aws-cdk-lib/custom-resources\";\nimport * as iam from \"aws-cdk-lib/aws-iam\";\nimport { Stack, StackProps } from \"aws-cdk-lib\";\nimport * as AWS from \"aws-sdk\";\n\nconst account = process.env.CDK_DEFAULT_ACCOUNT;\nconst region = process.env.CDK_DEFAULT_REGION;\n\nexport class ImageScanningSetupStack extends Stack {\n    constructor(scope: Construct, id: string, props?: StackProps) {\n        super(scope, id, { ...props, env: { account, region } });\n\n        // Scan type configuration: BASIC or ENHANCED\n        const scanType: AWS.ECR.ScanType = \"ENHANCED\";\n\n        // ENHANCED scanning configuration\n        const enhancedContinuousScanDuration: AWS.Inspector2.EcrRescanDuration =\n      \"LIFETIME\";\n        const enhancedScanRules: AWS.ECR.RegistryScanningRuleList = [\n            {\n                scanFrequency: \"CONTINUOUS_SCAN\",\n                repositoryFilters: [{ filter: \"prod\", filterType: \"WILDCARD\" }],\n            },\n            {\n                scanFrequency: \"SCAN_ON_PUSH\",\n                repositoryFilters: [{ filter: \"*\", filterType: \"WILDCARD\" }],\n            },\n        ];\n\n        // BASIC scanning configuration\n        const basicScanRules: AWS.ECR.RegistryScanningRuleList = [\n            {\n                scanFrequency: \"SCAN_ON_PUSH\",\n                repositoryFilters: [\n                    {\n                        filterType: \"WILDCARD\",\n                        filter: \"*\",\n                    },\n                ],\n            },\n        ];\n\n        let registyScanConfig: AWS.ECR.PutRegistryScanningConfigurationRequest = {};\n        if (scanType === \"ENHANCED\") {\n            registyScanConfig = {\n                scanType: \"ENHANCED\",\n                rules: enhancedScanRules,\n            };\n        } else if (scanType === \"BASIC\") {\n            registyScanConfig = {\n                scanType: \"BASIC\",\n                rules: basicScanRules,\n            };\n        }\n\n        new cr.AwsCustomResource(this, \"ImageScanningEnabler\", {\n            policy: cr.AwsCustomResourcePolicy.fromStatements([\n                new iam.PolicyStatement({\n                    actions: [\"config:*\", \"ecr:PutRegistryScanningConfiguration\"],\n                    resources: [\"*\"],\n                }),\n                new iam.PolicyStatement({\n                    actions: [\n                        \"inspector2:Enable\",\n                        \"inspector2:Disable\",\n                        \"inspector2:ListFindings\",\n                        \"inspector2:ListAccountPermissions\",\n                        \"inspector2:ListCoverage\",\n                    ],\n                    resources: [\"*\"],\n                }),\n                new iam.PolicyStatement({\n                    actions: [\"iam:CreateServiceLinkedRole\"],\n                    resources: [\"*\"],\n                    conditions: {\n                        StringEquals: {\n                            \"iam:AWSServiceName\": [\"inspector2.amazonaws.com\"],\n                        },\n                    },\n                }),\n            ]),\n            onUpdate: {\n                service: \"ECR\",\n                action: \"putRegistryScanningConfiguration\",\n                parameters: registyScanConfig,\n                physicalResourceId: cr.PhysicalResourceId.of(\"ImageScanningEnabler\"),\n            },\n        });\n\n        new cr.AwsCustomResource(this, \"InspectorEcrConfigurator\", {\n            policy: cr.AwsCustomResourcePolicy.fromStatements([\n                new iam.PolicyStatement({\n                    actions: [\"config:*\", \"inspector2:UpdateConfiguration\"],\n                    resources: [\"*\"],\n                }),\n            ]),\n            onUpdate: {\n                service: \"Inspector2\",\n                action: \"updateConfiguration\",\n                parameters: {\n                    ecrConfiguration: {\n                        rescanDuration: enhancedContinuousScanDuration,\n                    },\n                },\n                physicalResourceId: cr.PhysicalResourceId.of(\n                    \"InspectorEcrConfigurator\"\n                ),\n            },\n        });\n    }\n}\n"
  },
  {
    "path": "lib/security/image-vulnerability-scanning/index.ts",
    "content": "import { Construct } from \"constructs\";\nimport * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport { SECRET_ARGO_ADMIN_PWD } from \"../../multi-region-construct\";\nimport { prevalidateSecrets } from \"../../common/construct-utils\";\nimport * as team from \"../../teams/team-scan\";\nimport * as ecr from \"aws-cdk-lib/aws-ecr\";\n\nconst gitUrl = \"https://github.com/aws-samples/eks-blueprints-workloads.git\";\nconst targetRevision = \"main\";\n\nexport default class ImageScanningWorkloadConstruct {\n    async buildAsync(scope: Construct, id: string) {\n\n        await prevalidateSecrets(ImageScanningWorkloadConstruct.name, process.env.CDK_DEFAULT_REGION!, SECRET_ARGO_ADMIN_PWD);\n\n        const ecrRepositoryName = \"ImageScanningRepository\";\n        const ecrRepository = blueprints.getNamedResource(\n            ecrRepositoryName\n        ) as ecr.Repository;\n\n        const stackID = `${id}-blueprint`;\n        await blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION!)\n            .resourceProvider(\n                ecrRepositoryName,\n                new EcrResourceProvider(ecrRepositoryName)\n            )\n            .addOns(\n                new blueprints.ArgoCDAddOn({\n                    bootstrapRepo: {\n                        repoUrl: gitUrl,\n                        targetRevision: targetRevision,\n                        path: \"teams/team-scan/dev\",\n                    },\n                    bootstrapValues: {\n                        spec: {\n                            repositoryUri: ecrRepository.repositoryUri,\n                            region: process.env.CDK_DEFAULT_REGION!,\n                        },\n                    },\n                    adminPasswordSecretName: SECRET_ARGO_ADMIN_PWD,\n                })\n            )\n            .teams(new team.TeamScan())\n            .version('auto')\n            .buildAsync(scope, stackID);\n    }\n}\n\nclass EcrResourceProvider implements blueprints.ResourceProvider<ecr.IRepository> {\n\n    public constructor(private readonly ecrRepositoryName: string) {\n        this.ecrRepositoryName = ecrRepositoryName;\n    }\n\n    provide(context: blueprints.ResourceContext): ecr.IRepository {\n        const repository = new ecr.Repository(\n            context.scope,\n            this.ecrRepositoryName,\n            {\n                encryption: ecr.RepositoryEncryption.AES_256,\n            }\n        );\n        return repository;\n    }\n}\n"
  },
  {
    "path": "lib/security/securityhub-construct/index.ts",
    "content": "import * as securityhub from 'aws-cdk-lib/aws-securityhub';\nimport { Construct } from \"constructs\";\nimport { Stack, StackProps } from \"aws-cdk-lib\";\n\nexport class SecurityHubStackSetup extends Stack {\n    constructor(scope: Construct, id: string, props?: StackProps) {\n        super(scope, id, props);\n\n        // Enable Security Hub\n        new securityhub.CfnHub(this, 'MyCfnHub');\n    }\n}"
  },
  {
    "path": "lib/snyk-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { SnykMonitorAddOn } from '@snyk-partners/snyk-monitor-eks-blueprints-addon';\nimport { Construct } from \"constructs\";\n\nexport default class SnykConstruct extends Construct {\n    constructor(scope: Construct, id: string) {\n        super(scope, id);\n\n        const stackId = `${id}-blueprint`;\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new SnykMonitorAddOn({\n                version: \"1.87.2\", // replace with the version you wish to deploy\n                integrationId: \"<your-integration-id>\", // replace with your integration ID\n                values: {} // add any custom Helm values\n            })\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(...addOns)\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/starter-construct/index.ts",
    "content": "import { Construct } from 'constructs';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\n\n\n\n/**\n * Example starter with placeholders to add addOns and teams.\n */\nexport default class StarterConstruct {\n    build(scope: Construct, id: string) {\n        \n        const stackID = `${id}-blueprint`;\n        blueprints.EksBlueprint.builder()\n            .addOns(\n                new blueprints.AwsLoadBalancerControllerAddOn,\n                new blueprints.VpcCniAddOn(), \n                new blueprints.MetricsServerAddOn,\n                new blueprints.ClusterAutoScalerAddOn,\n            )\n            .teams()\n            .version('auto')\n            .build(scope, stackID);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/teams/index.ts",
    "content": "export { TeamBurnhamSetup } from \"./team-burnham\";\nexport { TeamPlatform } from \"./team-platform\";\nexport { TeamRikerSetup } from \"./team-riker\";\nexport { TeamTroiSetup } from \"./team-troi\";\nexport { dataTeam } from \"./team-emr-on-eks\";\nexport { TeamScan } from \"./team-scan\";\nexport { batchTeam } from './team-batch';\n"
  },
  {
    "path": "lib/teams/multi-account-monitoring/index.ts",
    "content": "export { TeamGeordi } from './team-geordi';\nexport { CorePlatformTeam } from './team-platform';\n"
  },
  {
    "path": "lib/teams/multi-account-monitoring/team-geordi.ts",
    "content": "import { ApplicationTeam } from '@aws-quickstart/eks-blueprints';\n\nexport class TeamGeordi extends ApplicationTeam {\n    constructor() {\n        super({\n            name: `team-geordi`,\n            namespace: 'geordie',\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/multi-account-monitoring/team-platform.ts",
    "content": "import { ClusterInfo, PlatformTeam } from '@aws-quickstart/eks-blueprints';\nimport { ArnPrincipal, IRole } from 'aws-cdk-lib/aws-iam';\n\nexport class CorePlatformTeam extends PlatformTeam {\n    constructor() {\n        super({\n            name: `team-platform`\n        });\n    }\n\n    protected getOrCreateRole(clusterInfo: ClusterInfo, users: ArnPrincipal[], roleArn?: string | undefined): IRole | undefined {\n        return super.getOrCreateRole(clusterInfo, users, roleArn ?? `arn:aws:iam::${clusterInfo.cluster.stack.account}:role/Admin`);\n    }\n}"
  },
  {
    "path": "lib/teams/pipeline-multi-env-gitops/index.ts",
    "content": "export { BackendCrystalTeam } from './team-backend-crystal';\nexport { FrontendTeam } from './team-backend-frontend';\nexport { BackendNodejsTeam } from './team-backend-nodejs';\nexport { CorePlatformTeam } from './team-platform';\n\n"
  },
  {
    "path": "lib/teams/pipeline-multi-env-gitops/team-backend-crystal.ts",
    "content": "import { ApplicationTeam } from '@aws-quickstart/eks-blueprints';\nimport { ArnPrincipal } from 'aws-cdk-lib/aws-iam';\n\nexport class BackendCrystalTeam extends ApplicationTeam {\n    constructor(accountID: string, environment: string) {\n        super({\n            name: `backend-crystal-${environment}`,\n            users: [\n                new ArnPrincipal(`arn:aws:iam::${accountID}:user/crystal-user`),\n            ],\n            namespace: 'ecsdemo-crystal',\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/pipeline-multi-env-gitops/team-backend-frontend.ts",
    "content": "import { ApplicationTeam } from '@aws-quickstart/eks-blueprints';\nimport { ArnPrincipal } from 'aws-cdk-lib/aws-iam';\n\nexport class FrontendTeam extends ApplicationTeam {\n    constructor(accountID: string, environment: string) {\n        super({\n            name: `frontend-${environment}`,\n            users: [\n                new ArnPrincipal(`arn:aws:iam::${accountID}:user/frontend-user`),\n            ],\n            namespace: 'ecsdemo-frontend',\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/pipeline-multi-env-gitops/team-backend-nodejs.ts",
    "content": "import { ApplicationTeam } from '@aws-quickstart/eks-blueprints';\nimport { ArnPrincipal } from 'aws-cdk-lib/aws-iam';\n\nexport class BackendNodejsTeam extends ApplicationTeam {\n    constructor(accountID: string, environment: string) {\n        super({\n            name: `backend-nodejs-${environment}`,\n            users: [\n                new ArnPrincipal(`arn:aws:iam::${accountID}:user/nodejs-user`),\n            ],\n            namespace: 'ecsdemo-nodejs',\n\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/pipeline-multi-env-gitops/team-platform.ts",
    "content": "import { PlatformTeam } from '@aws-quickstart/eks-blueprints';\nimport { ArnPrincipal } from 'aws-cdk-lib/aws-iam';\n\nexport class CorePlatformTeam extends PlatformTeam {\n    constructor(accountID: string, environment: string) {\n        super({\n            name: `platform-${environment}`,\n            users: [\n                new ArnPrincipal(`arn:aws:iam::${accountID}:user/platform-user`),\n            ],\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/team-batch/index.ts",
    "content": "import { BatchEksTeam, BatchEnvType, BatchAllocationStrategy } from \"@aws-quickstart/eks-blueprints\";\n\nexport const batchTeam = new BatchEksTeam({\n    name: \"batch-team-a\",\n    namespace: \"aws-batch\",\n    envName: \"batch-team-a-comp-env\",\n    computeResources:{\n        envType: BatchEnvType.EC2,\n        allocationStrategy: BatchAllocationStrategy.BEST,\n        priority: 10,\n        minvCpus: 1,\n        maxvCpus: 128,\n        instanceTypes: [\"m5\", \"c4.2xlarge\"]\n    },\n    jobQueueName: \"batch-team-a-job-queue\"\n});"
  },
  {
    "path": "lib/teams/team-burnham/index.ts",
    "content": "import { ArnPrincipal } from 'aws-cdk-lib/aws-iam';\nimport { Construct } from 'constructs';\nimport { ApplicationTeam, GenerateSecretManagerProvider } from '@aws-quickstart/eks-blueprints';\n\nfunction getUserArns(scope: Construct, key: string): ArnPrincipal[] {\n    const context: string = scope.node.tryGetContext(key);\n    if (context) {\n        return context.split(\",\").map(e => new ArnPrincipal(e));\n    }\n    return [];\n}\n\nexport class TeamBurnhamSetup extends ApplicationTeam {\n    constructor(scope: Construct, teamManifestDir: string) {\n        super({\n            name: \"burnham\",\n            users: getUserArns(scope, \"team-burnham.users\"),\n            namespaceAnnotations: {\n                \"appmesh.k8s.aws/sidecarInjectorWebhook\": \"enabled\"\n            },\n            teamSecrets: [\n                {\n                    secretProvider: new GenerateSecretManagerProvider('auth-password-id','AuthPassword' + (+new Date())),\n                    kubernetesSecret: {\n                        secretName: 'auth-password',\n                        data: [\n                            {\n                                key: 'password'\n                            }\n                        ]\n                    }\n                }\n            ],\n            teamManifestDir: teamManifestDir\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/team-burnham/restrict-ingress-egress-burnham.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: restrict-ingress-egress-burnham\n  namespace: team-burnham\nspec:\n  podSelector: {}\n  policyTypes:\n  - Ingress\n  - Egress\n  ingress:\n  - from:\n    - namespaceSelector:\n        matchLabels:\n          name: team-riker\n    ports:\n    - protocol: TCP\n      port: 80\n  egress:\n  - to:\n    - namespaceSelector:\n        matchLabels:\n          name: team-troi\n    ports:\n    - protocol: TCP\n      port: 53\n    - protocol: UDP\n      port: 53\n"
  },
  {
    "path": "lib/teams/team-emr-on-eks/index.ts",
    "content": "import { EmrEksTeam } from '@aws-quickstart/eks-blueprints';\nimport { PolicyStatement } from 'aws-cdk-lib/aws-iam';\n\n\nconst executionRolePolicyStatement: PolicyStatement[] = [\n    new PolicyStatement({\n        actions: ['logs:PutLogEvents', 'logs:CreateLogStream', 'logs:DescribeLogGroups', 'logs:DescribeLogStreams'],\n        resources: ['arn:aws:logs:*:*:*'],\n    }),\n];\n\nexport const dataTeam = new EmrEksTeam({\n    name: 'emr-data-team-a',\n    virtualClusterName: 'emr-data-team-a',\n    virtualClusterNamespace: 'batchjob',\n    createNamespace: true,\n    executionRoles: [\n        {\n            executionRoleIamPolicyStatement: executionRolePolicyStatement,\n            executionRoleName: 'myBlueprintExecRole'\n        }\n    ]\n});\n"
  },
  {
    "path": "lib/teams/team-platform/index.ts",
    "content": "import { ArnPrincipal } from \"aws-cdk-lib/aws-iam\";\n\nimport { PlatformTeam } from '@aws-quickstart/eks-blueprints';\n\nexport class TeamPlatform extends PlatformTeam {\n    constructor(accountID: string) {\n        super({\n            name: \"platform\",\n            users: [new ArnPrincipal(`arn:aws:iam::${accountID}:user/superadmin`)],\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/team-riker/index.ts",
    "content": "import { ApplicationTeam }  from '@aws-quickstart/eks-blueprints';\nimport { Construct } from 'constructs';\nimport { ArnPrincipal } from 'aws-cdk-lib/aws-iam';\n\nfunction getUserArns(scope: Construct, key: string): ArnPrincipal[] {\n    const context: string = scope.node.tryGetContext(key);\n    if (context) {\n        return context.split(\",\").map(e => new ArnPrincipal(e));\n    }\n    return [];\n}\n\nexport class TeamRikerSetup extends ApplicationTeam {\n    constructor(scope: Construct, teamManifestDir: string) {\n        super({\n            name: \"riker\",\n            users: getUserArns(scope, \"team-riker.users\"),\n            teamManifestDir: teamManifestDir,\n            namespaceHardLimits: {\n                'requests.cpu': '0.5', \n                'requests.memory': '1Gi',\n                'limits.cpu': '1',\n                'limits.memory': '2Gi'\n            }\n        });\n    }\n}"
  },
  {
    "path": "lib/teams/team-riker/restrict-ingress-egress-riker.yaml",
    "content": "apiVersion: networking.k8s.io/v1\nkind: NetworkPolicy\nmetadata:\n  name: restrict-ingress-egress-riker\n  namespace: team-riker\nspec:\n  podSelector: {}\n  policyTypes:\n  - Ingress\n  - Egress\n  ingress:\n  - from:\n    - namespaceSelector:\n        matchLabels:\n          name: team-troi\n    ports:\n    - protocol: TCP\n      port: 80\n  egress:\n  - to:\n    - namespaceSelector:\n        matchLabels:\n          name: team-burnham\n    ports:\n    - protocol: TCP\n      port: 53\n    - protocol: UDP\n      port: 53"
  },
  {
    "path": "lib/teams/team-scan/index.ts",
    "content": "import { ApplicationTeam, ClusterInfo } from \"@aws-quickstart/eks-blueprints\";\nimport * as iam from \"aws-cdk-lib/aws-iam\";\n\nexport class TeamScan extends ApplicationTeam {\n    constructor() {\n        super({\n            name: `team-scan`,\n            namespace: \"scan\",\n        });\n    }\n\n    protected setupServiceAccount(clusterInfo: ClusterInfo) {\n        super.setupServiceAccount(clusterInfo);\n        const ecrIamPolicy = new iam.PolicyStatement({\n            actions: [\"ecr:*\"],\n            resources: [\"*\"],\n        });\n        this.serviceAccount.addToPrincipalPolicy(ecrIamPolicy);\n    }\n}\n"
  },
  {
    "path": "lib/teams/team-troi/index.ts",
    "content": "import * as cdk from 'aws-cdk-lib';\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport * as s3 from \"aws-cdk-lib/aws-s3\";\n\nimport { ClusterInfo, Team } from '@aws-quickstart/eks-blueprints';\n\nexport class TeamTroiSetup implements Team {\n    readonly name: string = 'team-troi';\n\n    setup(clusterInfo: ClusterInfo) {\n        const cluster = clusterInfo.cluster;\n        const stack = cluster.stack;\n        const namespace = cluster.addManifest(this.name, {\n            apiVersion: 'v1',\n            kind: 'Namespace',\n            metadata: {\n                name: this.name,\n                annotations: { \"argocd.argoproj.io/sync-wave\": \"-1\" }\n            }\n        });\n\n        this.setupNamespacePolicies(cluster);\n\n        const sa = cluster.addServiceAccount('inf-backend', { name: 'inf-backend', namespace: this.name });\n        sa.node.addDependency(namespace);\n        const bucket = new s3.Bucket(stack, 'inf-backend-bucket');\n        bucket.grantReadWrite(sa);\n        new cdk.CfnOutput(stack, this.name + '-sa-iam-role', { value: sa.role.roleArn });\n    }\n\n    setupNamespacePolicies(cluster: eks.ICluster) {\n        const quotaName = this.name + \"-quota\";\n        cluster.addManifest(quotaName, {\n            apiVersion: 'v1',\n            kind: 'ResourceQuota',\n            metadata: { name: quotaName },\n            spec: {\n                hard: {\n                    'requests.cpu': '10',\n                    'requests.memory': '10Gi',\n                    'limits.cpu': '20',\n                    'limits.memory': '20Gi'\n                }\n            }\n        });\n    }\n}"
  },
  {
    "path": "lib/union-dataplane-construct/index.ts",
    "content": "import * as cdk from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as union from '@unionai/union-eks-blueprints-addon';\nimport { prevalidateSecrets, getJsonSecret } from '../common/construct-utils';\n\nconst BUCKET_PROVIDER_NAME = \"union-s3-bucket\";\n\nexport default class UnionDataplaneConstruct {\n    async buildAsync(scope: cdk.App, id: string) {\n\n        await prevalidateSecrets(UnionDataplaneConstruct.name, undefined,  \"union-client-secret\", \"union-secret\");\n        const unionSecretString = await blueprints.utils.getSecretValue(\"union-secret\", process.env.CDK_DEFAULT_REGION!);\n\n        const unionConfig: union.UnionDataplaneAddOnProps = {\n            orgName: getJsonSecret(unionSecretString, \"orgName\"),\n            unionSecretName: \"union-client-secret\",\n            clusterName: getJsonSecret(unionSecretString, \"clusterName\"),\n            s3BucketProviderName: BUCKET_PROVIDER_NAME,\n            host: getJsonSecret(unionSecretString, \"host\"),\n        };\n\n        const stackId = `${id}-blueprint`;\n\n        const nodeClassSpec: blueprints.Ec2NodeClassV1Spec = {\n            amiFamily: \"Bottlerocket\",\n            amiSelectorTerms: [\n                { alias: \"bottlerocket@1.50.0\" }, // alias allows to specify ami family and version. @latest also supported\n            ],\n            subnetSelectorTerms: [\n                { tags: { Name: `${stackId}/${stackId}-vpc/PrivateSubnet*` } },\n            ],\n            securityGroupSelectorTerms: [\n                { tags: { \"aws:eks:cluster-name\": `${stackId}` } },\n            ],\n        };\n\n        const nodePoolSpec: blueprints.NodePoolV1Spec = {\n            labels: { type: \"karpenter-test\" },\n            requirements: [\n                { key: \"node.kubernetes.io/instance-type\", operator: \"In\", values: [\"m5.2xlarge\", \"m5.xlarge\"] },\n                {\n                    key: \"topology.kubernetes.io/zone\",\n                    operator: \"In\",\n                    values: [`${process.env.CDK_DEFAULT_REGION}a`, `${process.env.CDK_DEFAULT_REGION}b`],\n                },\n                { key: \"kubernetes.io/arch\", operator: \"In\", values: [\"amd64\"] },\n                { key: \"karpenter.sh/capacity-type\", operator: \"In\", values: [\"on-demand\"] },\n            ],\n            expireAfter: \"20m\",\n            disruption: { consolidationPolicy: \"WhenEmpty\", consolidateAfter: \"30s\" },\n            limits: { cpu: 400 }\n        };\n\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.addons.MetricsServerAddOn,\n            new blueprints.addons.VpcCniAddOn,\n            new blueprints.addons.KubeProxyAddOn,\n            new blueprints.addons.CoreDnsAddOn,\n            new blueprints.addons.EksPodIdentityAgentAddOn,\n            new blueprints.addons.KarpenterV1AddOn({\n                ec2NodeClassSpec: nodeClassSpec,\n                nodePoolSpec: nodePoolSpec,\n                interruptionHandling: true,\n                podIdentity: true\n            }),\n            new union.UnionDataplaneCRDsAddOn,\n            new union.UnionDataplaneAddOn(unionConfig)\n        ];\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .resourceProvider(BUCKET_PROVIDER_NAME, new blueprints.CreateS3BucketProvider({ name: `union-bucket-${process.env.CDK_DEFAULT_ACCOUNT!}`, id: \"union-bucket\" }))\n            .addOns(...addOns)\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n\n\n"
  },
  {
    "path": "lib/windows-construct/index.ts",
    "content": "import * as blueprints from \"@aws-quickstart/eks-blueprints\";\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport * as eks from \"aws-cdk-lib/aws-eks\";\nimport * as iam from \"aws-cdk-lib/aws-iam\";\nimport { Construct } from \"constructs\";\nimport { WindowsBuilder, WindowsOptions, KarpenterAddOnProps } from \"@aws-quickstart/eks-blueprints\";\nimport { WindowsVpcCni } from \"./vpc-cni\";\n\nexport default class WindowsConstruct {\n    build(scope: Construct, id: string) {\n        const account = process.env.CDK_DEFAULT_ACCOUNT!;\n        const region = process.env.CDK_DEFAULT_REGION!;\n        const stackID = `${id}-eks-blueprint`;\n\n        const nodeRole = new blueprints.CreateRoleProvider(\"blueprint-node-role\", new iam.ServicePrincipal(\"ec2.amazonaws.com\"),\n            [\n                iam.ManagedPolicy.fromAwsManagedPolicyName(\"AmazonEKSWorkerNodePolicy\"),\n                iam.ManagedPolicy.fromAwsManagedPolicyName(\"AmazonEC2ContainerRegistryReadOnly\"),\n                iam.ManagedPolicy.fromAwsManagedPolicyName(\"AmazonSSMManagedInstanceCore\"),\n                iam.ManagedPolicy.fromAwsManagedPolicyName(\"AmazonEKS_CNI_Policy\")\n            ]);\n\n        const options: WindowsOptions = {\n            kubernetesVersion: eks.KubernetesVersion.V1_29,\n            instanceClass: ec2.InstanceClass.T3,\n            instanceSize: ec2.InstanceSize.MEDIUM,\n            desiredNodeCount: 1,\n            minNodeSize: 1,\n            maxNodeSize: 3,\n            blockDeviceSize: 50,\n            noScheduleForWindowsNodes: true,\n            clusterProviderTags: {\n                \"Name\": \"blueprints-windows-eks-cluster\",\n                \"Type\": \"generic-windows-cluster\"\n            },\n            genericNodeGroupOptions: {\n                nodegroupName: \"Mng-linux\",\n                tags: {\n                    \"kubernetes.io/cluster/windows-eks-blueprint\": \"owned\"\n                }\n            },\n            windowsNodeGroupOptions: {\n                nodegroupName: \"Mng-windows\",\n                tags: {\n                    \"kubernetes.io/cluster/windows-eks-blueprint\": \"owned\"\n                }\n            }\n        };\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new WindowsVpcCni(),\n        ];\n\n        const karpenterProps :KarpenterAddOnProps = {\n            nodePoolSpec: {\n                requirements: [\n                    { key: 'kubernetes.io/os', operator: 'In', values: ['windows']},\n                ],\n                taints: [\n                    {\n                        key: \"os\",\n                        value: \"windows\",\n                        effect: \"NoSchedule\"\n                    }\n                ],\n                disruption: {\n                    consolidationPolicy: \"WhenEmpty\",\n                    consolidateAfter: \"300s\",\n                    expireAfter: \"2592000s\"\n                },\n                weight: 20\n            },\n            ec2NodeClassSpec : {\n                subnetSelectorTerms: [\n                    { tags:  { \"Name\": `${stackID}/${stackID}-vpc/Private*` }}\n                ],\n                securityGroupSelectorTerms: [\n                    {tags: { [`kubernetes.io/cluster/${stackID}`]: \"owned\",}}\n                ],\n                amiFamily: \"Windows2022\"\n            },\n        };\n\n        WindowsBuilder.builder(options)\n            .addOns(...addOns)\n            .account(account)\n            .region(region)\n            .withKarpenterProps(karpenterProps)\n            .enableKarpenter()\n            .resourceProvider(\"node-role\", nodeRole)\n            .resourceProvider(\n                blueprints.GlobalResources.Vpc,\n                new blueprints.VpcProvider()\n            )\n            .build(scope, stackID);\n    }\n}\n"
  },
  {
    "path": "lib/windows-construct/vpc-cni/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { KubernetesManifest } from 'aws-cdk-lib/aws-eks';\nimport { Construct } from 'constructs';\n\nexport class WindowsVpcCni implements blueprints.ClusterAddOn {\n    id: \"amazon-vpc-cni\";\n\n    deploy(clusterInfo: blueprints.ClusterInfo): void | Promise<Construct> {\n        const cluster = clusterInfo.cluster;\n        const configmap = new KubernetesManifest(cluster, 'amazon-vpc-cni', { cluster: cluster,\n            manifest : [{\n                apiVersion: \"v1\",\n                kind: \"ConfigMap\",\n                metadata: {\n                    name: \"amazon-vpc-cni\",\n                    namespace: \"kube-system\",\n                },\n                data:{\n                    \"enable-windows-ipam\": \"true\"\n                },\n            }], overwrite: true });\n\n        return Promise.resolve(configmap);\n    }\n}\n\n"
  },
  {
    "path": "lib/workloads-codecommit-construct/codecommit-credentials.ts",
    "content": "import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId, PhysicalResourceIdReference } from 'aws-cdk-lib/custom-resources';\nimport { Construct } from 'constructs';\n\nexport class CodeCommitCredentials extends Construct {\n    readonly serviceSpecificCredentialId: string;\n    readonly serviceName: string;\n    readonly serviceUserName: string;\n    readonly servicePassword: string;\n    readonly status: string;\n\n    constructor(scope: Construct, id: string, userName: string) {\n        super(scope, id);\n\n        const codeCommitCredentialsResponse = new AwsCustomResource(this, \"codecommit-credentials-custom-resource\", {\n            onCreate: {\n                service: \"IAM\",\n                action: \"createServiceSpecificCredential\",\n                parameters: {\n                    ServiceName: \"codecommit.amazonaws.com\",\n                    UserName: userName\n                },\n                physicalResourceId: PhysicalResourceId.fromResponse(\"ServiceSpecificCredential.ServiceSpecificCredentialId\")\n            },\n            onDelete: {\n                service: \"IAM\",\n                action: \"deleteServiceSpecificCredential\",\n                parameters: {\n                    ServiceSpecificCredentialId: new PhysicalResourceIdReference(),\n                    UserName: userName,\n                }\n            },\n            policy: AwsCustomResourcePolicy.fromSdkCalls({\n                resources: AwsCustomResourcePolicy.ANY_RESOURCE,\n            }),\n        });\n\n        this.serviceSpecificCredentialId = codeCommitCredentialsResponse.getResponseField(\"ServiceSpecificCredential.ServiceSpecificCredentialId\");\n        this.serviceName = codeCommitCredentialsResponse.getResponseField(\"ServiceSpecificCredential.ServiceName\");\n        this.serviceUserName = codeCommitCredentialsResponse.getResponseField(\"ServiceSpecificCredential.ServiceUserName\");\n        this.servicePassword = codeCommitCredentialsResponse.getResponseField(\"ServiceSpecificCredential.ServicePassword\");\n        this.status = codeCommitCredentialsResponse.getResponseField(\"ServiceSpecificCredential.Status\");\n    }\n}"
  },
  {
    "path": "lib/workloads-codecommit-construct/index.ts",
    "content": "import * as blueprints from '@aws-quickstart/eks-blueprints';\nimport { Construct } from 'constructs';\nimport WorkloadsCodeCommitRepoStack from './workloads-codecommit-repo-stack';\n\n/**\n * Demonstrates how to use AWS CodeCommmit as a repository for ArgoCD workloads.\n */\n\nexport default class WorkloadsCodeCommitConstruct extends Construct {\n    constructor(scope: Construct, id: string) {\n        super(scope, id);\n\n        const region = process.env.CDK_DEFAULT_REGION!;\n\n        const userName = 'argocd-cc';\n        const repoName = 'eks-blueprints-workloads-cc';\n\n        const repoUrl = 'https://git-codecommit.' + region + '.amazonaws.com/v1/repos/' + repoName;\n\n        const stackId = `${id}-blueprint`;\n\n        const bootstrapRepo : blueprints.ApplicationRepository = {\n            repoUrl,\n            targetRevision: 'main',\n            credentialsSecretName: repoName + '-codecommit-secret',\n            credentialsType: 'TOKEN'\n        };\n\n        const addOns: Array<blueprints.ClusterAddOn> = [\n            new blueprints.NestedStackAddOn({\n                builder: WorkloadsCodeCommitRepoStack.builder(userName, repoName),\n                id: repoName + \"-codecommit-repo-nested-stack\"\n            }),\n            new blueprints.SecretsStoreAddOn,\n            new blueprints.ArgoCDAddOn({\n                bootstrapRepo: {\n                    ...bootstrapRepo,\n                    path: 'envs/dev'\n                },\n                values: {\n                    server: {\n                        service: {\n                            type: \"LoadBalancer\"\n                        }\n                    }\n                }\n            })\n        ];\n\n        blueprints.EksBlueprint.builder()\n            .account(process.env.CDK_DEFAULT_ACCOUNT!)\n            .region(process.env.CDK_DEFAULT_REGION)\n            .addOns(...addOns)\n\n            .version('auto')\n            .build(scope, stackId);\n    }\n}\n"
  },
  {
    "path": "lib/workloads-codecommit-construct/lambda/index.js",
    "content": "/* eslint-env node */\nconst https = require('https'); // eslint-disable-line\n\n/* webhook payload\n    {\n        \"ref\": \"refs/heads/main\",\n        \"repository\": {\n            \"html_url\": \"https://git-codecommit.us-west-2.amazonaws.com/v1/repos/eks-blueprints-workloads-cc\",\n            \"default_branch\": \"main\"\n        }\n    }\n*/\n\nexports.handler = async function(event) {\n    const eventSourceARNarray = event.Records[0].eventSourceARN.split(':');\n    const repoName = eventSourceARNarray[eventSourceARNarray.length - 1];\n    const ref = event.Records[0].codecommit.references[0].ref;\n    const refArray = ref.split('/');\n    const branch = refArray[refArray.length - 1];\n    const data = JSON.stringify({\n        \"ref\": ref,\n        \"repository\": {\n            \"html_url\": \"https://git-codecommit.\" + event.Records[0].awsRegion + \".amazonaws.com/v1/repos/\" + repoName,\n            \"default_branch\": branch\n        }\n    });\n    console.log(data);\n\n    const options = {\n        hostname: event.Records[0].customData,\n        path: '/api/webhook',\n        method: 'POST',\n        port: 443,\n        headers: {\n            'Content-Type': 'application/json',\n            'X-GitHub-Event': 'push',\n            'Content-Length': data.length,\n        },\n    };\n\n    const promise = new Promise(function(resolve, reject) {\n        process.env[\"NODE_TLS_REJECT_UNAUTHORIZED\"] = 0;\n        const req = https.request(options, (res) => {\n            resolve(res.statusCode);\n        }).on('error', (e) => {\n            reject(Error(e));\n        });\n        req.write(data);\n        req.end();\n    });\n    return promise;\n};"
  },
  {
    "path": "lib/workloads-codecommit-construct/workloads-codecommit-repo-stack.ts",
    "content": "import { Construct } from 'constructs';\nimport { NestedStack, NestedStackProps, SecretValue } from 'aws-cdk-lib';\nimport * as blueprints from '@aws-quickstart/eks-blueprints';\nimport * as codecommit from 'aws-cdk-lib/aws-codecommit';\nimport * as iam from 'aws-cdk-lib/aws-iam';\nimport * as lambda from 'aws-cdk-lib/aws-lambda';\nimport * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';\nimport { CodeCommitCredentials } from './codecommit-credentials';\n\nexport default class WorkloadsCodeCommitRepoStack extends NestedStack {\n    public static builder(userName: string, repoName: string): blueprints.NestedStackBuilder {\n        return {\n            build(scope: Construct, id: string, props: NestedStackProps) {\n                return new WorkloadsCodeCommitRepoStack(scope, id, props, userName, repoName);\n            }\n        };\n    }\n\n    constructor(scope: Construct, id: string, props: NestedStackProps, userName: string, repoName: string) {\n        super(scope, id);\n\n        const repo = new codecommit.Repository(this, repoName + '-codecommit-repo', {\n            repositoryName: repoName,\n        });\n\n        const user = new iam.User(this, userName + '-user-name', {\n            userName: userName,\n        });\n        repo.grantPull(user);\n\n        const credentials = new CodeCommitCredentials(this, \"codecommit-credentials\", user.userName);\n        credentials.node.addDependency(user);\n\n        new secretsmanager.Secret(this, 'codecommit-secret', {\n            secretObjectValue: {\n                username: SecretValue.unsafePlainText(credentials.serviceUserName),\n                password: SecretValue.unsafePlainText(credentials.servicePassword),\n                url: SecretValue.unsafePlainText(repo.repositoryCloneUrlHttp)\n            },\n            secretName: repoName + '-codecommit-secret'\n        });\n\n        const fn = new lambda.Function(this, repoName + '-webhook', {\n            runtime: lambda.Runtime.NODEJS_20_X,\n            functionName: repoName + '-webhook',\n            description: 'Webhook for ArgoCD on commit to AWS CodeCommit',\n            handler: 'index.handler',\n            code: lambda.Code.fromAsset(\"lib/workloads-codecommit-construct/lambda\"),\n        });\n\n        const principal = new iam.ServicePrincipal('codecommit.amazonaws.com');\n        fn.grantInvoke(principal);\n    }\n}\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: Amazon EKS Blueprints Patterns\nrepo_name: \"aws-samples/cdk-eks-blueprints-patterns\"\nrepo_url: \"https://github.com/aws-samples/cdk-eks-blueprints-patterns\"\ndocs_dir: \"docs\"\ntheme:\n  name: material\n  features:\n    - tabs\nnav:\n  - Overview: 'index.md'\n  - Patterns: \n    - Generative AI:\n      - Prompt Showcase Pattern: 'patterns/generative-ai/showcase.md'\n    - Observability Patterns:\n      - New Cluster:\n        - AWS Native Observability Pattern: 'patterns/observability/single-new-eks-native.md'\n        - AWS Graviton Observability Pattern: 'patterns/observability/single-new-eks-graviton-opensource-observability.md'\n        - OSS Observability Pattern: 'patterns/observability/single-new-eks-opensource.md'\n        - Fargate Observability Pattern : patterns/observability/single-new-eks-awsnative-fargate-observability.md\n        - Mixed Observability Pattern: patterns/observability/single-new-eks-mixed-observability.md\n        - Multi-Cluster Multi-Region Monitoring : patterns/observability/multi-acc-new-eks-mixed-observability.md\n        - OSS Apiserver Monitoring: patterns/observability/single-new-eks-apiserver-opensource-observability.md\n        - OSS GPU Monitoring: patterns/observability/single-new-eks-gpu-opensource-observability.md\n        - OSS Java Monitoring : patterns/observability/single-new-eks-java-opensource-observability.md\n        - OSS Nginx Monitoring : patterns/observability/single-new-eks-nginx-opensource-observability.md\n      - Existing Cluster:\n        - AWS Native Observability Pattern : patterns/observability/existing-eks-awsnative-observability.md\n        - Mixed Observability Pattern: patterns/observability/existing-eks-mixed-observability.md\n        - OSS Observability Pattern: patterns/observability/existing-eks-opensource-observability.md\n        - OSS Apiserver Monitoring : patterns/observability/existing-eks-apiserver-observability.md\n        - OSS Nginx Monitoring : patterns/observability/existing-eks-nginx-observability.md\n      - Multi Account Observability Pattern: 'patterns/observability/multi-account-monitoring.md'\n    - Security Patterns:\n      - EKS Config Rules: 'patterns/security/eks-config-rules.md'\n      - Encryption At Rest: 'patterns/security/encryption-at-rest.md'\n      - GuardDuty: 'patterns/security/guardduty.md'\n      - Image Scanning: 'patterns/security/image-scanning.md'\n      - SecurityHub: 'patterns/security/securityhub.md'\n    - AWS Batch Pattern: 'patterns/batch.md'\n    - Backstage Pattern: 'patterns/backstage.md'\n    - Customer Network with IPV4 Pattern: 'patterns/custom-networking-with-ipv4.md'\n    - Instana Pattern: 'patterns/instana.md'\n    - Jupyterhub Pattern: 'patterns/jupyterhub.md'\n    - Konveyor Pattern: 'patterns/knoveyor.md'\n    - Kubeflow Pattern: 'patterns/kubeflow.md'\n    - Kubeshark Pattern: 'patterns/kubeshark.md'\n    - Multi environment Pipeline GitOps pattern Pattern: 'patterns/pipeline-multi-env-gitops.md'\n    - Multi cluster conformitron pattern : 'patterns/multi-cluster-conformitron.md'\n    - Nginx Pattern: 'patterns/nginx.md'\n    - Secure Ingress and Authentication Pattern: 'patterns/secureingresscognito.md'\n    - Graviton Pattern: 'patterns/graviton.md'\n    - Windows Pattern: 'patterns/windows.md'\n    - gMaestro Pattern: 'patterns/gmaestro.md'\n    - Union.ai Pattern : 'patterns/union.md'\nmarkdown_extensions:\n  - def_list\n  - pymdownx.highlight\n  - pymdownx.superfences\n  - pymdownx.inlinehilite\n  - pymdownx.emoji\n  - pymdownx.magiclink\n  - pymdownx.snippets:\n      check_paths: true\n  - pymdownx.tabbed:\n      alternate_style: true\n  - pymdownx.tasklist:\n      custom_checkbox: true\n  - toc:\n      permalink: \"¤\"\nplugins:\n  - search\n  - external-markdown\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"eks-blueprints-patterns\",\n  \"version\": \"1.2.0\",\n  \"main\": \"dist/lib/common/default-main.js\",\n  \"types\": \"dist/lib/common/default-main.d.ts\",\n  \"scripts\": {\n    \"build\": \"rm -rf dist && tsc --skipLibCheck\",\n    \"watch\": \"tsc -w\",\n    \"test\": \"jest\",\n    \"cdk\": \"cdk\",\n    \"nginx\": \"cdk --app \\\"npx ts-node bin/nginx.ts\\\"\",\n    \"lint\": \"npx eslint . --ext .js,.jsx,.ts,.tsx\"\n  },\n  \"devDependencies\": {\n    \"@types/jest\": \"^29.5.11\",\n    \"@types/node\": \"^22.7.9\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.11.0\",\n    \"@typescript-eslint/parser\": \"^8.11.0\",\n    \"copyfiles\": \"^2.4.1\",\n    \"eslint\": \"^8.56.0\",\n    \"jest\": \"^29.7.0\",\n    \"ts-jest\": \"^29.1.1\",\n    \"ts-node\": \"^10.9.2\",\n    \"typescript\": \"^5.3.3\"\n  },\n  \"dependencies\": {\n    \"@aws-quickstart/eks-blueprints\": \"1.17.4\",\n    \"@aws-sdk/client-config-service\": \"^3.576.0\",\n    \"@aws-sdk/client-eks\": \"^3.478.0\",\n    \"@claranet-ch/konveyor-eks-blueprint-addon\": \"^1.0.2\",\n    \"@datadog/datadog-eks-blueprints-addon\": \"^0.1.2\",\n    \"@dynatrace/dynatrace-eks-blueprints-addon\": \"^1.4.0-1\",\n    \"@granulate/gmaestro-eks-blueprints-addon\": \"^1.0.16\",\n    \"@instana/aws-eks-blueprint-addon\": \"^1.0.4\",\n    \"@kastenhq/kasten-eks-blueprints-addon\": \"^1.0.1\",\n    \"@keptn/keptn-controlplane-eks-blueprints-addon\": \"^0.5.0\",\n    \"@komodor/komodor-eks-blueprints-addon\": \"^1.2.0\",\n    \"@kubecost/kubecost-eks-blueprints-addon\": \"^0.1.8\",\n    \"@newrelic/newrelic-eks-blueprints-addon\": \"^1.0.9\",\n    \"@paralus/paralus-eks-blueprints-addon\": \"^0.1.5\",\n    \"@rafaysystems/rafay-eks-blueprints-addon\": \"^0.0.2\",\n    \"@snyk-partners/snyk-monitor-eks-blueprints-addon\": \"^1.1.1\",\n    \"@unionai/union-eks-blueprints-addon\": \"^0.0.5\",\n    \"aws-cdk\": \"2.1029.2\",\n    \"aws-cdk-lib\": \"2.215.0\",\n    \"eks-blueprints-cdk-kubeflow-ext\": \"0.1.9\",\n    \"kubeshark\": \"^0.0.9\",\n    \"source-map-support\": \"^0.5.21\"\n  },\n  \"overrides\": {\n    \"@aws-quickstart/eks-blueprints\": \"1.17.4\",\n    \"aws-cdk\": \"2.1029.2\",\n    \"aws-cdk-lib\": \"2.215.0\",\n    \"xml2js\": \"0.5.0\",\n    \"axios\": \"^1.6.2\"\n  }\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"outDir\": \"dist\",\n        \"target\": \"ES2022\",\n        \"module\": \"commonjs\",\n        \"lib\": [\n            \"es2022\",\n            \"dom\"\n        ],\n        \"declaration\": true,\n        \"strict\": true,\n        \"noImplicitAny\": true,\n        \"strictNullChecks\": true,\n        \"noImplicitThis\": true,\n        \"alwaysStrict\": true,\n        \"noUnusedLocals\": false,\n        \"noUnusedParameters\": false,\n        \"noImplicitReturns\": true,\n        \"noFallthroughCasesInSwitch\": false,\n        \"inlineSourceMap\": true,\n        \"inlineSources\": true,\n        \"experimentalDecorators\": true,\n        \"strictPropertyInitialization\": false,\n        \"typeRoots\": [\n            \"./node_modules/@types\"\n        ]\n    },\n    \"exclude\": [\n        \"cdk.out\",\n        \"node_modules\",\n        \"test\",\n        \".git\",\n        \"dist\"\n    ],\n    \"include\": [\n        \"bin\",\n        \"lib\",\n        \"examples\"\n    ]\n}"
  }
]