├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ag_lambda ├── lambda_function.py └── requirements.txt ├── bedrock-agents-for-eks-template.yaml ├── cr_lambda ├── indices_custom_resource.py └── requirements.txt ├── images ├── agent-test-1.png ├── agent-test-2.png ├── agent-test-3.png ├── agent-test-4.png ├── bedrock-agents-for-eks.png ├── knowledge-base-test-1.png └── knowledge-base-test-2.png ├── kube-down.sh ├── kube-setup.sh ├── lambda-build.sh └── open-api-schema.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ag_lambda_build 3 | cr_lambda_build 4 | packaged-bedrock-agents-for-eks-template.yaml 5 | stack-events -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bedrock Agents For EKS 2 | 3 | ## Introduction 4 | [Amazon Bedrock Agents](https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html) are a feature within [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) that allows you to build and configure software agents to automate tasks within your applications. These agents leverage [foundation models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html) like [Anthropic Claude](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-claude.html) to help them make intelligent decisions about how to respond to conversational prompts. Using [retrieval augmented generation (RAG)](https://aws.amazon.com/what-is/retrieval-augmented-generation/), agents can respond to prompts using domain-specific data sources for more up-to-date authoritative information. Agents can also complete actions on your behalf by leveraging a combination of [OpenAPI schemas](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-api-schema.html) and [AWS Lambda functions](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html) to intelligently access external systems. 5 | 6 | This post demonstrates how to configure [Amazon Bedrock Agents](https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html) as a new natural language interface for [Amazon EKS clusters](https://docs.aws.amazon.com/eks/latest/userguide/clusters.html). By configuring [knowledge bases](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html) with Kubernetes and Amazon EKS documentation as data sources, agents can answer specific questions related to container orchestration on AWS. Furthermore, [action groups](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-action-create.html) configured to use the [Kubernetes Python Client](https://github.com/kubernetes-client/python?tab=readme-ov-file#kubernetes-python-client) within AWS Lambda functions give agents the ability to pragmatically access Amazon EKS cluster resources by querying the Kubernetes API server on your behalf when prompted. 7 | 8 | [Trivy](https://trivy.dev/) is a popular open-source security scanner designed to find vulnerabilities and misconfigurations in various targets. The [Trivy Operator](https://github.com/aquasecurity/trivy-operator) can be installed within a Kubernetes cluster to perform, among other things, a set of checks based on the [Center for Internet Security (CIS) Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes). The CIS Benchmark is a set of guidelines that outlines best practices for securing a Kubernetes cluster. By configuring an [Amazon Bedrock Agent](https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html) to query the Kubernetes API Server of an Amazon EKS cluster, this post will further demonstrate how you can review the results of the CIS Kubernetes Benchmark report that the [Trivy Operator](https://github.com/aquasecurity/trivy-operator) produces through natural conversation with an agent. 9 | 10 | ## Solution Overview 11 | 12 | The following diagram provides a visual overview of the solution architecture: 13 | 14 | !["architecture diagram"](images/bedrock-agents-for-eks.png) 15 | 16 | 1. The user provides a natural language input prompt to the agent. For example, a user may ask “How many failed checks are in the CIS Benchmark compliance report? ” 17 | 2. The agent interprets the user’s input by using a combination of the chat history as well as the instructions provided and underlying foundation model that was selected upon agent creation. 18 | 3. [Knowledge bases](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html) are used to enhance the accuracy and performance of the foundation model through fully [managed retrieval augmented generation (RAG)](https://aws.amazon.com/what-is/retrieval-augmented-generation/), allowing the retrieval of domain-specific data that the foundation model wasn’t originally trained on. Files are uploaded to an Amazon S3 bucket to act as the data source for the knowledge base. For this solution, Kubernetes and Amazon EKS documentation are uploaded as data sources to imbue the agent with expert level container orchestration knowledge. 19 | 4. Amazon Bedrock automatically splits the source data into chunks, then uses an embeddings model to convert them into numerical representations or vectors. These vectors are then stored in a vector index. When a knowledge base is created through the AWS management console, Amazon Bedrock can automatically create and manage a vector search collection and index in [Amazon OpenSearch Serverless](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/serverless-vector-search.html). This solution uses an AWS CloudFormation template to create the necessary vector search collection and index in OpenSearch Serverless as well, but knowledge bases can also leverage vector indexes created in [Amazon Aurora PostgreSQL](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/AuroraPostgreSQL.VectorDB.html),[Pinecone](https://docs.pinecone.io/integrations/amazon-bedrock), [Redis Enterprise Cloud](https://redis.io/docs/latest/integrate/amazon-bedrock/), and [MongoDB Atlas](https://www.mongodb.com/docs/atlas/atlas-vector-search/ai-integrations/amazon-bedrock/). 20 | 5. An [OpenAPI schema](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-api-schema.html) is uploaded to an Amazon S3 bucket to define the API operations that the agent can invoke on the user’s behalf. 21 | 6. An [AWS Lambda function](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html) is defined with the necessary logic to process requests made by the agent and return a properly formatted response body. The OpenAPI schema together with the AWS Lambda function from an [action group](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-action-create.html) resource. This solution uses the [Kubernetes Python Client](https://github.com/kubernetes-client/python) to enable the AWS Lambda function to query the Kubernetes API server of an Amazon EKS cluster. 22 | 7. The[Trivy Operator](https://github.com/aquasecurity/trivy-operator) generates a [CIS Kubernetes Benchmark](https://www.cisecurity.org/benchmark/kubernetes) report as a [ClusterComplianceReport](https://aquasecurity.github.io/trivy-operator/latest/docs/crds/clustercompliance-report/) custom resource, and the findings of the report can then be retrieved by the agent based on the user’s input. 23 | 24 | ## Getting started 25 | 26 | ### Prerequisites 27 | 28 | To deploy this solution you will need following tools: 29 | - [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 30 | - [eksctl](https://eksctl.io/installation/) 31 | - [kubectl](https://kubernetes.io/docs/tasks/tools/) 32 | - [Helm](https://helm.sh/docs/intro/install/) 33 | - [Git](https://github.com/git-guides/install-git) 34 | 35 | You will also need to [request access to Bedrock Foundation Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html#model-access-add). At a minimum, you will need access to [Amazon Titan Embeddings G1 - Text](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html) and [Anthropic Claude models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-claude.html). 36 | 37 | ### Setup the Amazon EKS Cluster 38 | Although you can configure this solution to work with an existing Amazon EKS cluster, only non-production environments should be targeted for initial testing and experimentation. 39 | 40 | To deploy a new Amazon EKS Cluster using the [eksctl](https://eksctl.io/usage/creating-and-managing-clusters/) CLI tool, execute the following command: 41 | 42 | ``` 43 | eksctl create cluster \ 44 | --name bedrock-agent-eks-cluster \ 45 | --region us-west-2 \ 46 | --version 1.31 47 | ``` 48 | The [eksctl](https://eksctl.io/usage/creating-and-managing-clusters/) CLI tool automatically adds the appropriate kubernetes configuration to your kubeconfig file, located at `~/.kube/config` by default. You can confirm that you have access to the Amazon EKS cluster by executing the following kubectl command: 49 | ``` 50 | kubectl get nodes 51 | ``` 52 | Next, use Helm to Install the [Trivy Operator](https://github.com/aquasecurity/trivy-operator): 53 | ``` 54 | helm repo add aqua https://aquasecurity.github.io/helm-charts/ 55 | 56 | helm repo update 57 | 58 | helm install trivy-operator aqua/trivy-operator \ 59 | --namespace trivy-system \ 60 | --create-namespace \ 61 | --version 0.21.4 \ 62 | --set="compliance.cron=*/10 * * * *" 63 | ``` 64 | The Trivy Operator will generate a compliance report every six hours by default. The `compliance.cron` setting has been updated here to generate reports every 10 minutes for demonstration purposes. 65 | 66 | Confirm that the Trivy Operator was successfully installed by directly requesting the [CIS ClusterComplianceReport](https://aquasecurity.github.io/trivy-operator/latest/docs/crds/clustercompliance-report/): 67 | ``` 68 | kubectl get clustercompliancereport cis -o yaml 69 | ``` 70 | Later on, the Amazon Bedrock Agent will be prompted to pull and parse through this report on your behalf. 71 | 72 | ## Prepare Data Sources for the Knowledge Base 73 | Create an Amazon S3 bucket to store the data sources of the knowledge base: 74 | ``` 75 | AWS_ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) 76 | ``` 77 | ``` 78 | aws s3 mb s3://eks-bedrock-knowledge-base-data-source-${AWS_ACCOUNT} \ 79 | --region us-west-2 80 | ``` 81 | Next, create a local directory to store the necessary reference documentation. This solution uses the [Kubernetes Documentation](https://github.com/kubernetes/website/tree/main/content/en), the [Amazon EKS Best Practices Guide](https://github.com/aws/aws-eks-best-practices), the [Amazon EKS User Guide](https://docs.aws.amazon.com/pdfs/eks/latest/userguide/eks-ug.pdf), and the [Amazon EKS API Reference](https://docs.aws.amazon.com/pdfs/eks/latest/APIReference/eks-api.pdf) as aggregated data sources. 82 | 83 | ``` 84 | mkdir data_sources 85 | 86 | # Get the Kubernetes Documentation: 87 | git clone git@github.com:kubernetes/website.git 88 | cp -r website/content/en data_sources/kubernetes_docs 89 | 90 | # Get the Amazon EKS Best Practices Guide: 91 | curl https://docs.aws.amazon.com/pdfs/eks/latest/best-practices/eks-bpg.pdf \ 92 | -o data_sources/eks-dest-practices-guide.pdf 93 | 94 | # Get the Amazon EKS User Guide: 95 | curl https://docs.aws.amazon.com/pdfs/eks/latest/userguide/eks-ug.pdf \ 96 | -o data_sources/eks-user-guide.pdf 97 | 98 | # Get the Amazon EKS API Reference: 99 | curl https://docs.aws.amazon.com/pdfs/eks/latest/APIReference/eks-api.pdf \ 100 | -o data_sources/eks-api-ref.pdf 101 | ``` 102 | To add additional data sources, review the [supported file formats](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-ds.html) and ensure that each file size doesn’t exceed the quota of 50 MB. 103 | 104 | Finally, use the [aws s3 sync](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/s3/sync.html) command to copy the data sources to the target Amazon S3 bucket, using a combination of `--exclude` and `--include` flags to filter out any non-supported file formats. 105 | 106 | ``` 107 | aws s3 sync ~/data_sources s3://eks-bedrock-knowledge-base-data-source-${AWS_ACCOUNT} \ 108 | --region us-west-2 \ 109 | --exclude "*" \ 110 | --include "*.txt" \ 111 | --include "*.md" \ 112 | --include "*.html" \ 113 | --include "*.pdf" 114 | ``` 115 | ## Prepare the Action Group OpenAPI Schema 116 | Clone the solution repository: 117 | ``` 118 | git clone git@github.com:aws-samples/bedrock-agents-for-eks.gits 119 | ``` 120 | The repository contains a pre-configured OpenAPI schema (`open-api-schema.json`) that will be used as part of an action group to specify the API actions that the agent can perform. Create an Amazon S3 bucket and copy the OpenAPI schema into it for later reference: 121 | ``` 122 | # Create an S3 Bucket 123 | aws s3 mb s3://eks-bedrock-agent-openapi-schema-${AWS_ACCOUNT} \ 124 | --region us-west-2 125 | 126 | cd bedrock-agents-for-eks 127 | 128 | aws s3 cp open-api-schema.json s3://eks-bedrock-agent-openapi-schema-${AWS_ACCOUNT} \ 129 | --region us-west-2 130 | ``` 131 | ## Prepare AWS Lambda Function Artifacts 132 | This solution uses two AWS Lambda functions; the `CustomResourceFunction` creates the necessary vector index in the OpenSearch Serverless collection, while the `ActionGroupFunction` serves as a proxy to give the agent programatic access to the EKS Cluster. Run the `lambda-build.sh` script to install, package, and store the AWS Lambda artifacts that the AWS CloudFormation template references in a newly created S3 Bucket: 133 | ``` 134 | ./lambda-build.sh 135 | ``` 136 | ## Deploy the Stack 137 | Deploy the packaged AWS CloudFormation template: 138 | ``` 139 | aws cloudformation deploy \ 140 | --template-file packaged-bedrock-agents-for-eks-template.yaml \ 141 | --stack-name bedrock-agents-for-eks-stack \ 142 | --capabilities CAPABILITY_NAMED_IAM \ 143 | --region us-west-2 144 | ``` 145 | ## Configure EKS Cluster Access 146 | The `ActionGroupFunction` needs read-only access to the EKS cluster. Run the `kube-setup.sh` script to create the necessary ClusterRole and ClusterRoleBinding objects and an [EKS Access Entry](https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html): 147 | 148 | Note that the default `CLUSTER_NAME` is set to `bedrock-agent-eks-cluster`. If you are using another EKS cluster, be sure to update the value in the script before running it. 149 | 150 | ``` 151 | ./kube-setup.sh 152 | ``` 153 | ## Test the Knowledge Base 154 | You can test agent knowledge bases and action groups using the chat interface built into Amazon Bedrock Dashboard. As a prerequisite, follow [the instructions in the documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-ingest.html) to sync your data source from the Amazon S3 bucket. The sync process converts the raw data in your data source into vector embeddings then ingests them into the knowledge base for querying. 155 | 156 | Once the sync process is complete, you can follow [the instruction in the documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/kb-test-how.html) for issuing test queries to the knowledge base. You can choose from a number of foundation models for knowledge base testing. To remain consistent with the default configuration of the agent that was deployed, select the `Anthropic Claude 3 Sonnet` model. 157 | 158 | Using the built-in chat interface, run a series of queries to validate access to Kubernetes and EKS domain-specific knowledge. For example: 159 | 160 | !["knowledge base test 1"](images/knowledge-base-test-1.png) 161 | !["knowledge base test 2"](images/knowledge-base-test-2.png) 162 | 163 | Note that you can click on `Show source details` to view the source chunks and metadata pointing to the specific referenced data source files in Amazon S3. 164 | 165 | ## Test the Agent 166 | Follow [the instructions in the documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-test.html) to prepare and test a working draft of your Bedrock agent. 167 | 168 | Verify that the Bedrock Agent can query your EKS cluster by asking questions related to namespaces, pods, and the CIS Benchmark compliance report checks. For example: 169 | !["agent test 1"](images/agent-test-1.png) 170 | !["agent test 2"](images/agent-test-2.png) 171 | !["agent test 3"](images/agent-test-3.png) 172 | !["agent test 4"](images/agent-test-4.png) 173 | 174 | The Bedrock agent intelligently interpret prompts to determine when it should query the EKS cluster for specific information, including details about the CIS Benchmark compliance report that is generated by the Trivy Operator. 175 | 176 | ## Clean Up 177 | To avoid charges in your AWS account, clean up the resources provisioned for this solution. 178 | 179 | Run the `kube-down.sh` script to delete the ClusterRole and ClusterRoleBinding objects and remove the [Access Entry](https://docs.aws.amazon.com/eks/latest/userguide/access-entries.html) from your EKS cluster: 180 | 181 | ``` 182 | ./kube-down.sh 183 | ``` 184 | Uninstall the Trivy Operator: 185 | ``` 186 | helm uninstall trivy-operator -n trivy-system 187 | ``` 188 | Follow [the Trivy documentation](https://aquasecurity.github.io/trivy-operator/v0.19.4/getting-started/installation/helm/#uninstall) to further delete the custom resource definitions, but note that this will also delete all security reports generated by the operator. 189 | 190 | Optionally, delete the EKS cluster created for testing out this solution: 191 | ``` 192 | eksctl delete cluster \ 193 | --name bedrock-agent-eks-cluster \ 194 | --region us-west-2 195 | ``` 196 | Delete the AWS CloudFormation stack: 197 | ``` 198 | aws cloudformation delete-stack \ 199 | --stack-name bedrock-agents-for-eks-stack \ 200 | --region us-west-2 201 | ``` 202 | Finally, empty and delete the Amazon S3 buckets that were used to store the data sources, the OpenAPI schema, and the AWS Lambda function artifacts: 203 | ``` 204 | aws s3 rm s3://eks-bedrock-knowledge-base-data-source-${AWS_ACCOUNT}/ \ 205 | --recursive \ 206 | --region us-west-2 207 | 208 | aws s3 rb s3://eks-bedrock-knowledge-base-data-source-${AWS_ACCOUNT} \ 209 | --region us-west-2 210 | 211 | aws s3 rm s3://eks-bedrock-agent-openapi-schema-${AWS_ACCOUNT}/ \ 212 | --recursive \ 213 | --region us-west-2 214 | 215 | aws s3 rb s3://eks-bedrock-agent-openapi-schema-${AWS_ACCOUNT} \ 216 | --region us-west-2 217 | 218 | aws s3 rm s3://bedrock-agent-lambda-artifacts-${AWS_ACCOUNT}/ \ 219 | --recursive \ 220 | --region us-west-2 221 | 222 | aws s3 rb s3://bedrock-agent-lambda-artifacts-${AWS_ACCOUNT} \ 223 | --region us-west-2 224 | ``` 225 | ## Conclusion 226 | [Amazon Bedrock Agents](https://docs.aws.amazon.com/bedrock/latest/userguide/agents.html) can leverage [foundation models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html) such as [Anthropic Claude](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-claude.html) to act as a new natural language interface for [Amazon EKS clusters](https://docs.aws.amazon.com/eks/latest/userguide/clusters.html). By intelligently querying the Kubernetes API server on your behalf through [action groups](https://docs.aws.amazon.com/bedrock/latest/userguide/agents-action-create.html), Bedrock agents can relay cluster-specific information in a more human-readable form as a response to conversational prompts. Although this post demonstrated such agentic capabilities by prompting an agent to retrieve the results of the [CIS Benchmark compliance report](https://www.cisecurity.org/benchmark/kubernetes) generated by the [Trivy Operator](https://github.com/aquasecurity/trivy-operator), this solution can be extended to further democratize access to EKS clusters and Kubernetes resources through natural conversation, creating opportunities for new deployment patterns and cluster management paradigms. -------------------------------------------------------------------------------- /ag_lambda/lambda_function.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import os 3 | import logging 4 | import re 5 | import boto3 6 | 7 | from botocore.signers import RequestSigner 8 | from kubernetes import client, config 9 | 10 | 11 | logger = logging.getLogger() 12 | logger.setLevel(logging.INFO) 13 | 14 | STS_TOKEN_EXPIRES_IN = 60 15 | session = boto3.session.Session() 16 | sts = session.client('sts') 17 | service_id = sts.meta.service_model.service_id 18 | cluster_name = os.environ["CLUSTER_NAME"] 19 | eks = boto3.client('eks') 20 | cluster_cache = {} 21 | 22 | def get_cluster_info(): 23 | "Retrieve cluster endpoint and certificate" 24 | cluster_info = eks.describe_cluster(name=cluster_name) 25 | endpoint = cluster_info['cluster']['endpoint'] 26 | cert_authority = cluster_info['cluster']['certificateAuthority']['data'] 27 | cluster_info = { 28 | "endpoint" : endpoint, 29 | "ca" : cert_authority 30 | } 31 | return cluster_info 32 | 33 | def get_bearer_token(): 34 | "Create authentication token" 35 | signer = RequestSigner( 36 | service_id, 37 | session.region_name, 38 | 'sts', 39 | 'v4', 40 | session.get_credentials(), 41 | session.events 42 | ) 43 | 44 | params = { 45 | 'method': 'GET', 46 | 'url': 'https://sts.{}.amazonaws.com/' 47 | '?Action=GetCallerIdentity&Version=2011-06-15'.format(session.region_name), 48 | 'body': {}, 49 | 'headers': { 50 | 'x-k8s-aws-id': cluster_name 51 | }, 52 | 'context': {} 53 | } 54 | 55 | signed_url = signer.generate_presigned_url( 56 | params, 57 | region_name=session.region_name, 58 | expires_in=STS_TOKEN_EXPIRES_IN, 59 | operation_name='' 60 | ) 61 | base64_url = base64.urlsafe_b64encode(signed_url.encode('utf-8')).decode('utf-8') 62 | 63 | # remove any base64 encoding padding: 64 | return 'k8s-aws-v1.' + re.sub(r'=*', '', base64_url) 65 | 66 | def lambda_handler(event, context): 67 | "Lambda handler" 68 | if cluster_name in cluster_cache: 69 | cluster = cluster_cache[cluster_name] 70 | else: 71 | # not present in cache retrieve cluster info from EKS service 72 | cluster = get_cluster_info() 73 | # store in cache for execution environment resuse 74 | cluster_cache[cluster_name] = cluster 75 | 76 | kubeconfig = { 77 | 'apiVersion': 'v1', 78 | 'clusters': [{ 79 | 'name': 'cluster1', 80 | 'cluster': { 81 | 'certificate-authority-data': cluster["ca"], 82 | 'server': cluster["endpoint"]} 83 | }], 84 | 'contexts': [{'name': 'context1', 'context': {'cluster': 'cluster1', "user": "user1"}}], 85 | 'current-context': 'context1', 86 | 'kind': 'Config', 87 | 'preferences': {}, 88 | 'users': [{'name': 'user1', "user" : {'token': get_bearer_token()}}] 89 | } 90 | 91 | config.load_kube_config_from_dict(config_dict=kubeconfig) 92 | v1_api = client.CoreV1Api() # api_client 93 | co_api = client.CustomObjectsApi() # api_client 94 | 95 | # Extract the action group, api path, and parameters from the prediction 96 | action = event["actionGroup"] 97 | api_path = event["apiPath"] 98 | httpMethod = event["httpMethod"] 99 | 100 | # get pods by namespace 101 | if api_path.rsplit('/',1)[1] =='get-pods': 102 | parameters = event["parameters"] 103 | namespace_name=parameters[0]["value"] 104 | pod_list = v1_api.list_namespaced_pod(namespace_name) 105 | body = [{"name": item.metadata.name, 106 | "phase": item.status.phase, 107 | "containers": [{"name": container.name, 108 | "image": container.image 109 | } for container in item.spec.containers], 110 | } for item in pod_list.items ] 111 | response_code = 200 112 | response_body = {"application/json": {"body": str(body)}} 113 | 114 | # get namespaces 115 | elif api_path == '/namespaces': 116 | namespace_list = v1_api.list_namespace() 117 | body = [item.metadata.name for item in namespace_list.items] 118 | response_code = 200 119 | response_body = {"application/json": {"body": str(body)}} 120 | 121 | # get the CIS report custom resource 122 | elif api_path == '/reports/cis': 123 | cis_report = co_api.get_cluster_custom_object( 124 | group="aquasecurity.github.io", 125 | version="v1alpha1", 126 | plural="clustercompliancereports", 127 | name="cis" 128 | ) 129 | # check if cis_report["status"] exists 130 | try: 131 | body = {"status": cis_report["status"]} 132 | response_code = 200 133 | 134 | except KeyError: 135 | body = {"status": "not found"} 136 | response_code = 404 137 | 138 | response_body = {"application/json": {"body": str(body)}} 139 | 140 | else: 141 | body = {"{}::{} is not a valid api, try another one.".format(action, api_path)} 142 | response_code = 400 143 | response_body = {"application/json": {"body": str(body)}} 144 | 145 | 146 | action_response = { 147 | 'actionGroup': action, 148 | 'apiPath': api_path, 149 | 'httpMethod': httpMethod, 150 | 'httpStatusCode': response_code, 151 | 'responseBody': response_body 152 | } 153 | 154 | session_attributes = event['sessionAttributes'] 155 | prompt_session_attributes = event['promptSessionAttributes'] 156 | 157 | api_response = { 158 | 'messageVersion': '1.0', 159 | 'response': action_response, 160 | 'sessionAttributes': session_attributes, 161 | 'promptSessionAttributes': prompt_session_attributes 162 | } 163 | 164 | return api_response 165 | -------------------------------------------------------------------------------- /ag_lambda/requirements.txt: -------------------------------------------------------------------------------- 1 | kubernetes~=24.2.0 2 | -------------------------------------------------------------------------------- /bedrock-agents-for-eks-template.yaml: -------------------------------------------------------------------------------- 1 | # CloudFormation Template 2 | AWSTemplateFormatVersion: 2010-09-09 3 | Description: Bedrock Agents for EKS 4 | Parameters: 5 | EmbeddingModelARN: 6 | Type: String 7 | Default: arn:aws:bedrock:us-west-2::foundation-model/amazon.titan-embed-text-v2:0 8 | FoundationModelName: 9 | Type: String 10 | Default: anthropic.claude-3-5-sonnet-20241022-v2:0 11 | FoundationModelARN: 12 | Type: String 13 | Default: arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-3-5-sonnet-20241022-v2:0 14 | EKSClusterName: 15 | Type: String 16 | Default: bedrock-agent-eks-cluster 17 | AgentInstruction: 18 | Type: String 19 | Default: > 20 | You are a Kubernetes expert that can answer questions about Kubernetes and Amazon EKS. 21 | You can also query the API Server of an Amazon EKS cluster in order to tell users 22 | what namespaces exist on the cluster, what pods have been deployed within each namespace, 23 | and retrieve details of the CIS Benchmark compliance report checks. 24 | Resources: 25 | # Bedrock Knowledge Base IAM Role 26 | BedrockKnowledgeBaseRole: 27 | Type: AWS::IAM::Role 28 | Properties: 29 | AssumeRolePolicyDocument: 30 | Statement: 31 | - Action: sts:AssumeRole 32 | Effect: Allow 33 | Principal: 34 | Service: bedrock.amazonaws.com 35 | Version: "2012-10-17" 36 | RoleName: AmazonBedrockExecutionRoleForKnowledgeBase_kb_test 37 | # Bedrock Knowledge Base IAM Policy 38 | BedrockKnowledgeBasePolicy: 39 | Type: AWS::IAM::Policy 40 | Properties: 41 | PolicyDocument: 42 | Statement: 43 | - Action: bedrock:InvokeModel 44 | Effect: Allow 45 | Resource: 46 | - Ref: EmbeddingModelARN 47 | - Action: s3:ListBucket 48 | Effect: Allow 49 | Resource: 50 | Fn::Join: 51 | - "" 52 | - - arn:aws:s3:::eks-bedrock-knowledge-base-data-source- 53 | - Ref: AWS::AccountId 54 | - Action: s3:GetObject 55 | Effect: Allow 56 | Resource: 57 | Fn::Join: 58 | - "" 59 | - - arn:aws:s3:::eks-bedrock-knowledge-base-data-source- 60 | - Ref: AWS::AccountId 61 | - /* 62 | - Action: aoss:APIAccessAll 63 | Effect: Allow 64 | Resource: 65 | Fn::GetAtt: 66 | - OpenSearchCollection 67 | - Arn 68 | Version: "2012-10-17" 69 | PolicyName: BedrockKnowledgeBasePolicy 70 | Roles: 71 | - Ref: BedrockKnowledgeBaseRole 72 | DependsOn: CustomResource 73 | # Indexer Lambda Function IAM Role 74 | CustomResourceRole: 75 | Type: AWS::IAM::Role 76 | Properties: 77 | AssumeRolePolicyDocument: 78 | Statement: 79 | - Action: sts:AssumeRole 80 | Effect: Allow 81 | Principal: 82 | Service: lambda.amazonaws.com 83 | Version: "2012-10-17" 84 | ManagedPolicyArns: 85 | - Fn::Join: 86 | - "" 87 | - - "arn:" 88 | - Ref: AWS::Partition 89 | - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 90 | # Indexer Lambda Function IAM Policy 91 | CustomResourcePolicy: 92 | Type: AWS::IAM::Policy 93 | Properties: 94 | PolicyDocument: 95 | Statement: 96 | - Action: aoss:APIAccessAll 97 | Effect: Allow 98 | Resource: 99 | Fn::GetAtt: 100 | - OpenSearchCollection 101 | - Arn 102 | Version: "2012-10-17" 103 | PolicyName: CustomResourcePolicy 104 | Roles: 105 | - Ref: CustomResourceRole 106 | # Network Access Policy for OpenSearch Collection 107 | OpenSearchNetworkPolicy: 108 | Type: AWS::OpenSearchServerless::SecurityPolicy 109 | Properties: 110 | Name: opensearch-network-policy 111 | Policy: '[{"Rules": [{"Resource": ["collection/opensearch-vector-collection"], "ResourceType": "dashboard"}, {"Resource": ["collection/opensearch-vector-collection"], "ResourceType": "collection"}], "AllowFromPublic": true}]' 112 | Type: network 113 | # Encryption Policy for OpenSearch Collection 114 | OpenSearchEncryptionPolicy: 115 | Type: AWS::OpenSearchServerless::SecurityPolicy 116 | Properties: 117 | Name: opensearch-encryption-policy 118 | Policy: '{"Rules": [{"ResourceType": "collection", "Resource": ["collection/opensearch-vector-collection"]}], "AWSOwnedKey": true}' 119 | Type: encryption 120 | # Data Access Policy for OpenSearch Collection 121 | OpenSearchDataAccessPolicy: 122 | Type: AWS::OpenSearchServerless::AccessPolicy 123 | Properties: 124 | Name: embeddings-access-policy 125 | Policy: 126 | Fn::Join: 127 | - "" 128 | - - '[{"Rules": [{"Resource": ["collection/opensearch-vector-collection"], "Permission": ["aoss:DescribeCollectionItems", "aoss:CreateCollectionItems", "aoss:UpdateCollectionItems"], "ResourceType": "collection"}, {"Resource": ["index/opensearch-vector-collection/*"], "Permission": ["aoss:UpdateIndex", "aoss:DescribeIndex", "aoss:ReadDocument", "aoss:WriteDocument", "aoss:CreateIndex"], "ResourceType": "index"}], "Principal": ["' 129 | - Fn::GetAtt: 130 | - BedrockKnowledgeBaseRole 131 | - Arn 132 | - '", "' 133 | - Fn::GetAtt: 134 | - CustomResourceRole 135 | - Arn 136 | - '"]}]' 137 | Type: data 138 | # OpenSearch Collection 139 | OpenSearchCollection: 140 | Type: AWS::OpenSearchServerless::Collection 141 | Properties: 142 | Name: opensearch-vector-collection 143 | Type: VECTORSEARCH 144 | DependsOn: 145 | - OpenSearchDataAccessPolicy 146 | - OpenSearchEncryptionPolicy 147 | - OpenSearchNetworkPolicy 148 | # Indexer Lambda Function Layer 149 | # CustomResourceLayer: 150 | # Type: AWS::Lambda::LayerVersion 151 | # Properties: 152 | # CompatibleRuntimes: 153 | # - python3.10 154 | # Content: ./cr_lambda/indices-layer.zip 155 | # Description: Required dependencies for Lambda 156 | # Indexer Lambda Function 157 | CustomResourceFunction: 158 | Type: AWS::Lambda::Function 159 | Properties: 160 | Code: cr_lambda_build/. 161 | Environment: 162 | Variables: 163 | COLLECTION_ENDPOINT: 164 | Fn::GetAtt: 165 | - OpenSearchCollection 166 | - CollectionEndpoint 167 | VECTOR_INDEX_NAME: vector-index 168 | VECTOR_FIELD_NAME: vector-field 169 | TEXT_FIELD: text-field 170 | METADATA_FIELD: metadata-field 171 | Handler: indices_custom_resource.on_event 172 | # Layers: 173 | # - Ref: CustomResourceLayer 174 | ReservedConcurrentExecutions: 5 175 | Role: 176 | Fn::GetAtt: 177 | - CustomResourceRole 178 | - Arn 179 | Runtime: python3.10 180 | Timeout: 600 181 | DependsOn: 182 | - CustomResourcePolicy 183 | - CustomResourceRole 184 | - OpenSearchCollection 185 | # Indexer Custom Resource 186 | CustomResource: 187 | Type: AWS::CloudFormation::CustomResource 188 | Properties: 189 | ServiceToken: 190 | Fn::GetAtt: 191 | - CustomResourceFunction 192 | - Arn 193 | UpdateReplacePolicy: Delete 194 | DeletionPolicy: Delete 195 | # Bedrock Agent Lambda Function IAM Role 196 | ActionGroupFunctionRole: 197 | Type: AWS::IAM::Role 198 | Properties: 199 | AssumeRolePolicyDocument: 200 | Statement: 201 | - Action: sts:AssumeRole 202 | Effect: Allow 203 | Principal: 204 | Service: lambda.amazonaws.com 205 | Version: "2012-10-17" 206 | ManagedPolicyArns: 207 | - Fn::Join: 208 | - "" 209 | - - "arn:" 210 | - Ref: AWS::Partition 211 | - ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 212 | RoleName: BedrockAgentLambdaFunctionRole 213 | DependsOn: CustomResource 214 | # Bedrock Agent Lambda Function IAM Policy 215 | ActionGroupFunctionPolicy: 216 | Type: AWS::IAM::Policy 217 | Properties: 218 | PolicyDocument: 219 | Statement: 220 | - Action: eks:DescribeCluster 221 | Effect: Allow 222 | Resource: 223 | Fn::Join: 224 | - "" 225 | - - "arn:aws:eks:us-west-2:" 226 | - Ref: AWS::AccountId 227 | - ":cluster/" 228 | - Ref: EKSClusterName 229 | Version: "2012-10-17" 230 | PolicyName: ActionGroupFunctionPolicy 231 | Roles: 232 | - Ref: ActionGroupFunctionRole 233 | DependsOn: CustomResource 234 | # Bedrock Agent Lambda Function Layer 235 | # ActionGroupFunctionLayer: 236 | # Type: AWS::Lambda::LayerVersion 237 | # Properties: 238 | # CompatibleRuntimes: 239 | # - python3.10 240 | # Content: ./ag_lambda/k8s-layer.zip 241 | # Description: Required dependencies for Lambda 242 | # DependsOn: CustomResource 243 | # Bedrock Agent Lambda Function 244 | ActionGroupFunction: 245 | Type: AWS::Lambda::Function 246 | Properties: 247 | Code: ag_lambda_build/. 248 | Environment: 249 | Variables: 250 | CLUSTER_NAME: 251 | Ref: EKSClusterName 252 | FunctionName: bedrock-agent-eks-executor 253 | Handler: lambda_function.lambda_handler 254 | # Layers: 255 | # - Ref: ActionGroupFunctionLayer 256 | MemorySize: 256 257 | ReservedConcurrentExecutions: 5 258 | Role: 259 | Fn::GetAtt: 260 | - ActionGroupFunctionRole 261 | - Arn 262 | Runtime: python3.10 263 | Timeout: 30 264 | DependsOn: 265 | - ActionGroupFunctionPolicy 266 | - ActionGroupFunctionRole 267 | - CustomResource 268 | # Bedrock Agent Lambda Permission 269 | ActionGroupFunctionInvokePermission: 270 | Type: AWS::Lambda::Permission 271 | Properties: 272 | Action: lambda:InvokeFunction 273 | FunctionName: 274 | Fn::GetAtt: 275 | - ActionGroupFunction 276 | - Arn 277 | Principal: bedrock.amazonaws.com 278 | SourceAccount: 279 | Ref: AWS::AccountId 280 | SourceArn: 281 | Fn::GetAtt: 282 | - BedrockAgent 283 | - AgentArn 284 | # Knowledge Base 285 | BedrockKnowledgeBase: 286 | Type: AWS::Bedrock::KnowledgeBase 287 | Properties: 288 | Name: k8s-docs-knowledge-base 289 | RoleArn: 290 | Fn::GetAtt: 291 | - BedrockKnowledgeBaseRole 292 | - Arn 293 | KnowledgeBaseConfiguration: 294 | Type: "VECTOR" 295 | VectorKnowledgeBaseConfiguration: 296 | EmbeddingModelArn: 297 | Ref: EmbeddingModelARN 298 | StorageConfiguration: 299 | Type: "OPENSEARCH_SERVERLESS" 300 | OpensearchServerlessConfiguration: 301 | CollectionArn: 302 | Fn::GetAtt: 303 | - OpenSearchCollection 304 | - Arn 305 | VectorIndexName: vector-index 306 | FieldMapping: 307 | VectorField: vector-field 308 | TextField: text-field 309 | MetadataField: metadata-field 310 | DependsOn: 311 | - BedrockKnowledgeBasePolicy 312 | - BedrockKnowledgeBaseRole 313 | - ActionGroupFunction 314 | - CustomResource 315 | # Data Source 316 | DataSource: 317 | Type: AWS::Bedrock::DataSource 318 | Properties: 319 | KnowledgeBaseId: 320 | Ref: BedrockKnowledgeBase 321 | Name: k8s-docs-data-source 322 | DataSourceConfiguration: 323 | Type: "S3" 324 | S3Configuration: 325 | BucketArn: 326 | Fn::Join: 327 | - "" 328 | - - "arn:aws:s3:::eks-bedrock-knowledge-base-data-source-" 329 | - Ref: AWS::AccountId 330 | # Bedrock Agent IAM Roles 331 | BedrockAgentRole: 332 | Type: AWS::IAM::Role 333 | Properties: 334 | AssumeRolePolicyDocument: 335 | Statement: 336 | - Action: sts:AssumeRole 337 | Effect: Allow 338 | Principal: 339 | Service: bedrock.amazonaws.com 340 | Version: "2012-10-17" 341 | RoleName: AmazonBedrockExecutionRoleForAgents_agent_test 342 | # Bedrock Agent IAM Policy 343 | BedrockAgentPolicy: 344 | Type: AWS::IAM::Policy 345 | Properties: 346 | PolicyDocument: 347 | Statement: 348 | - Action: bedrock:InvokeModel 349 | Effect: Allow 350 | Resource: 351 | - Ref: FoundationModelARN 352 | - Action: bedrock:Retrieve 353 | Effect: Allow 354 | Resource: 355 | Fn::GetAtt: 356 | - BedrockKnowledgeBase 357 | - KnowledgeBaseArn 358 | - Action: s3:GetObject 359 | Effect: Allow 360 | Resource: 361 | Fn::Join: 362 | - "" 363 | - - "arn:aws:s3:::" 364 | - "eks-bedrock-agent-openapi-schema-" 365 | - Ref: AWS::AccountId 366 | - "/" 367 | - "open-api-schema.json" 368 | Version: "2012-10-17" 369 | PolicyName: BedrockAgentPolicy 370 | Roles: 371 | - Ref: BedrockAgentRole 372 | # Agent 373 | BedrockAgent: 374 | Type: AWS::Bedrock::Agent 375 | Properties: 376 | AgentName: eks-bedrock-agent 377 | AgentResourceRoleArn: 378 | Fn::GetAtt: 379 | - BedrockAgentRole 380 | - Arn 381 | FoundationModel: 382 | Ref: FoundationModelName 383 | Instruction: 384 | Ref: AgentInstruction 385 | ActionGroups: 386 | - ActionGroupName: eks-action-group 387 | Description: "Action Group for EKS Cluster" 388 | ActionGroupExecutor: 389 | Lambda: 390 | Fn::GetAtt: 391 | - ActionGroupFunction 392 | - Arn 393 | ApiSchema: 394 | S3: 395 | S3BucketName: 396 | Fn::Join: 397 | - "" 398 | - - "eks-bedrock-agent-openapi-schema-" 399 | - Ref: AWS::AccountId 400 | S3ObjectKey: open-api-schema.json 401 | KnowledgeBases: 402 | - KnowledgeBaseId: 403 | Ref: BedrockKnowledgeBase 404 | Description: "Kubernetes and Amazon EKS Documentation" 405 | KnowledgeBaseState: ENABLED -------------------------------------------------------------------------------- /cr_lambda/indices_custom_resource.py: -------------------------------------------------------------------------------- 1 | from opensearchpy import OpenSearch, RequestsHttpConnection 2 | from requests_aws4auth import AWS4Auth 3 | import boto3 4 | import time 5 | import json 6 | import os 7 | import cfnresponse 8 | 9 | region = os.environ['AWS_REGION'] 10 | collection_endpoint = os.environ['COLLECTION_ENDPOINT'] 11 | vector_field = os.environ['VECTOR_FIELD_NAME'] 12 | vector_index_name = os.environ['VECTOR_INDEX_NAME'] 13 | text_field = os.environ['TEXT_FIELD'] 14 | metadata_field = os.environ['METADATA_FIELD'] 15 | 16 | 17 | def on_event(event, context): 18 | physical_id = "CreatedIndexId" 19 | reason = '' 20 | print(json.dumps(event)) 21 | request_type = event['RequestType'] 22 | 23 | if request_type == 'Create': 24 | reason = on_create(event, physical_id=physical_id, region=region, endpoint=collection_endpoint, vector_field=vector_field, 25 | vector_index_name=vector_index_name, text_field=text_field, metadata_field=metadata_field) 26 | 27 | elif request_type == 'Update': 28 | reason = on_update(event, physical_id=physical_id) 29 | 30 | elif request_type == 'Delete': 31 | reason = on_delete(event, physical_id=physical_id) 32 | 33 | else: 34 | responseData = { 35 | 'Status': 'FAILED', 36 | 'Reason': "Invalid request type: %s" % request_type, 37 | 'PhysicalResourceId': physical_id 38 | } 39 | cfnresponse.send(event, context, cfnresponse.FAILED, responseData) 40 | 41 | responseData = { 42 | 'Status': 'SUCCESS', 43 | 'Reason': reason, 44 | 'PhysicalResourceId': physical_id 45 | } 46 | cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) 47 | 48 | 49 | 50 | def on_create(event, physical_id, region, endpoint, vector_index_name, 51 | vector_field, text_field, metadata_field): 52 | props = event["ResourceProperties"] 53 | print("create new resource with props %s" % props) 54 | 55 | index_data(region=region, vector_index_name=vector_index_name, 56 | text_field=text_field, metadata_field=metadata_field, 57 | vector_field=vector_field, endpoint=endpoint) 58 | 59 | reason = "Created new resource with props %s" % props 60 | return reason 61 | 62 | 63 | def on_update(event, physical_id): 64 | # physical_id = event["PhysicalResourceId"] 65 | props = event["ResourceProperties"] 66 | print("update resource %s with props %s" % (physical_id, props)) 67 | 68 | reason = "Updated resource %s with props %s" % (physical_id, props) 69 | return reason 70 | 71 | 72 | def on_delete(event, physical_id): 73 | # physical_id = event["PhysicalResourceId"] 74 | print("delete resource %s" % physical_id) 75 | 76 | reason = "Deleted resource %s" % physical_id 77 | return reason 78 | 79 | 80 | def index_data(region, vector_index_name, text_field, 81 | metadata_field, vector_field, endpoint): 82 | 83 | host = endpoint.replace("https://", "") 84 | 85 | # Set up auth for Opensearch client 86 | service = 'aoss' 87 | credentials = boto3.Session().get_credentials() 88 | awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, 89 | region, service, session_token=credentials.token) 90 | 91 | """Create an index""" 92 | # Build the OpenSearch client 93 | client = OpenSearch( 94 | hosts=[{'host': host, 'port': 443}], 95 | http_auth=awsauth, 96 | use_ssl=True, 97 | verify_certs=True, 98 | connection_class=RequestsHttpConnection, 99 | timeout=300 100 | ) 101 | # It can take up to a minute for data access rules to be enforced 102 | time.sleep(45) 103 | 104 | # Create index 105 | body = { 106 | "mappings": { 107 | "properties": { 108 | f"{metadata_field}": { 109 | "type": "text", 110 | "index": False 111 | }, 112 | "id": { 113 | "type": "text", 114 | "fields": { 115 | "keyword": { 116 | "type": "keyword", 117 | "ignore_above": 256 118 | } 119 | } 120 | }, 121 | f"{text_field}": { 122 | "type": "text", 123 | "index": True 124 | }, 125 | f"{vector_field}": { 126 | "type": "knn_vector", 127 | "dimension": 1024, 128 | "method": { 129 | "engine": "faiss", 130 | "space_type": "l2", 131 | "name": "hnsw" 132 | } 133 | } 134 | } 135 | }, 136 | "settings": { 137 | "index": { 138 | "number_of_shards": 2, 139 | "knn.algo_param": { 140 | "ef_search": 512 141 | }, 142 | "knn": True, 143 | } 144 | } 145 | } 146 | 147 | response = client.indices.create(index=vector_index_name, body=body) 148 | print('\nCreating index:') 149 | print(response) 150 | -------------------------------------------------------------------------------- /cr_lambda/requirements.txt: -------------------------------------------------------------------------------- 1 | opensearch-py~=2.3.2 2 | requests_aws4auth~=1.2.3 3 | cfnresponse~=1.1.4 -------------------------------------------------------------------------------- /images/agent-test-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/agent-test-1.png -------------------------------------------------------------------------------- /images/agent-test-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/agent-test-2.png -------------------------------------------------------------------------------- /images/agent-test-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/agent-test-3.png -------------------------------------------------------------------------------- /images/agent-test-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/agent-test-4.png -------------------------------------------------------------------------------- /images/bedrock-agents-for-eks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/bedrock-agents-for-eks.png -------------------------------------------------------------------------------- /images/knowledge-base-test-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/knowledge-base-test-1.png -------------------------------------------------------------------------------- /images/knowledge-base-test-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/bedrock-agents-for-eks/2842b09da2767ca704618b6f9981ef113fbd54a4/images/knowledge-base-test-2.png -------------------------------------------------------------------------------- /kube-down.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if ! hash aws 2>/dev/null || ! hash kubectl 2>/dev/null; then 3 | echo "This script requires the AWS CLI and kubectl to be installed" 4 | exit 2 5 | fi 6 | 7 | set -eo pipefail 8 | 9 | CLUSTER_NAME='bedrock-agent-eks-cluster' 10 | 11 | ROLE_ARN=$(aws lambda get-function --function-name bedrock-agent-eks-executor --region us-west-2 --query "Configuration.Role" --output text) 12 | 13 | RBAC_OBJECT='kind: ClusterRole 14 | apiVersion: rbac.authorization.k8s.io/v1 15 | metadata: 16 | name: read-only 17 | rules: 18 | - apiGroups: [ "aquasecurity.github.io", ""] 19 | resources: ["*"] 20 | verbs: ["get", "watch", "list"] 21 | --- 22 | kind: ClusterRoleBinding 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | metadata: 25 | name: read-only-binding 26 | roleRef: 27 | kind: ClusterRole 28 | name: read-only 29 | apiGroup: rbac.authorization.k8s.io 30 | subjects: 31 | - kind: Group 32 | name: read-only-group' 33 | 34 | 35 | echo ========== 36 | echo Delete Role and RoleBinding in Kubernetes with kubectl 37 | echo ========== 38 | echo "$RBAC_OBJECT" 39 | echo 40 | while true; do 41 | read -p "Do you want to delete the ClusterRole and ClusterRoleBinding? (y/n)" response 42 | case $response in 43 | [Yy]* ) echo "$RBAC_OBJECT" | kubectl delete -f -; break;; 44 | [Nn]* ) break;; 45 | * ) echo "Response must start with y or n.";; 46 | esac 47 | done 48 | 49 | echo 50 | echo ========== 51 | echo Delete the access entry for the action group Lambda function 52 | echo ========== 53 | echo Cluster: $CLUSTER_NAME 54 | echo RoleArn: $ROLE_ARN 55 | echo 56 | while true; do 57 | read -p "Do you want to delete the access entry? (y/n)" response 58 | case $response in 59 | [Yy]* ) aws eks delete-access-entry --cluster-name $CLUSTER_NAME --principal-arn $ROLE_ARN --region=us-west-2; break;; 60 | [Nn]* ) break;; 61 | * ) echo "Response must start with y or n.";; 62 | esac 63 | done 64 | 65 | -------------------------------------------------------------------------------- /kube-setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if ! hash aws 2>/dev/null || ! hash kubectl 2>/dev/null; then 3 | echo "This script requires the AWS CLI and kubectl to be installed" 4 | exit 2 5 | fi 6 | 7 | set -eo pipefail 8 | 9 | CLUSTER_NAME='bedrock-agent-eks-cluster' 10 | 11 | ROLE_ARN=$(aws lambda get-function --function-name bedrock-agent-eks-executor --region us-west-2 --query "Configuration.Role" --output text) 12 | 13 | RBAC_OBJECT='kind: ClusterRole 14 | apiVersion: rbac.authorization.k8s.io/v1 15 | metadata: 16 | name: read-only 17 | rules: 18 | - apiGroups: [ "aquasecurity.github.io", ""] 19 | resources: ["*"] 20 | verbs: ["get", "watch", "list"] 21 | --- 22 | kind: ClusterRoleBinding 23 | apiVersion: rbac.authorization.k8s.io/v1 24 | metadata: 25 | name: read-only-binding 26 | roleRef: 27 | kind: ClusterRole 28 | name: read-only 29 | apiGroup: rbac.authorization.k8s.io 30 | subjects: 31 | - kind: Group 32 | name: read-only-group' 33 | 34 | 35 | echo ========== 36 | echo Create Role and RoleBinding in Kubernetes with kubectl 37 | echo ========== 38 | echo "$RBAC_OBJECT" 39 | echo 40 | while true; do 41 | read -p "Do you want to create the ClusterRole and ClusterRoleBinding? (y/n)" response 42 | case $response in 43 | [Yy]* ) echo "$RBAC_OBJECT" | kubectl apply -f -; break;; 44 | [Nn]* ) break;; 45 | * ) echo "Response must start with y or n.";; 46 | esac 47 | done 48 | 49 | echo 50 | echo ========== 51 | echo Create an access entry for the action group Lambda function 52 | echo ========== 53 | echo Cluster: $CLUSTER_NAME 54 | echo RoleArn: $ROLE_ARN 55 | echo 56 | while true; do 57 | read -p "Do you want to create the access entry? (y/n)" response 58 | case $response in 59 | [Yy]* ) aws eks create-access-entry --cluster-name $CLUSTER_NAME --principal-arn $ROLE_ARN --type STANDARD --kubernetes-groups read-only-group --region=us-west-2; break;; 60 | [Nn]* ) break;; 61 | * ) echo "Response must start with y or n.";; 62 | esac 63 | done 64 | 65 | -------------------------------------------------------------------------------- /lambda-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if ! hash aws 2>/dev/null || ! hash pip3 2>/dev/null; then 3 | echo "This script requires the AWS cli, and pip3 installed" 4 | exit 2 5 | fi 6 | 7 | set -eo pipefail 8 | 9 | rm -rf ag_lambda_build ; mkdir ag_lambda_build ; cd ag_lambda_build 10 | cp -r ../ag_lambda/* . 11 | pip3 install --target . -r requirements.txt 12 | cd ../ 13 | 14 | rm -rf cr_lambda_build ; mkdir cr_lambda_build ; cd cr_lambda_build 15 | cp -r ../cr_lambda/* . 16 | pip3 install --target . -r requirements.txt 17 | cd ../ 18 | 19 | AWS_ACCOUNT=$(aws sts get-caller-identity --query "Account" --output text) 20 | 21 | BUCKET_NAME=bedrock-agent-lambda-artifacts-${AWS_ACCOUNT} 22 | 23 | if ! aws s3api head-bucket --bucket $BUCKET_NAME > /dev/null 2>&1; then 24 | aws s3 mb s3://${BUCKET_NAME} --region us-west-2 25 | fi 26 | 27 | aws cloudformation package \ 28 | --template-file bedrock-agents-for-eks-template.yaml \ 29 | --s3-bucket $BUCKET_NAME \ 30 | --output-template-file packaged-bedrock-agents-for-eks-template.yaml \ 31 | --region us-west-2 32 | -------------------------------------------------------------------------------- /open-api-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "openapi": "3.0.0", 3 | "info": { 4 | "title": "Kubernetes Pods Automation API", 5 | "version": "1.0.0", 6 | "description": "API for listing Kubernetes resources by Namespace." 7 | }, 8 | "paths": { 9 | "/namespaces": { 10 | "get": { 11 | "summary": "Get a list of all namespaces in a Kubernetes cluster", 12 | "description": "Get a list of all namespaces in a Kubernetes cluster. This API return all the namespace names.", 13 | "operationId": "getAllNamespaces", 14 | "responses": { 15 | "200": { 16 | "description": "Gets the list of all namespaces in a Kubernetes cluster", 17 | "content": { 18 | "application/json": { 19 | "schema": { 20 | "type": "array", 21 | "items": { 22 | "type": "object", 23 | "properties": { 24 | "namespace_name": { 25 | "type": "string", 26 | "description": "name of the namespace" 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | }, 37 | "/namespaces/{namespace_name}/get-pods": { 38 | "get": { 39 | "summary": "Get a list of pods in a specified namespace.", 40 | "description": "Get a list of pods in a specified namespace. The API takes in only one namespace name and returns the list of pods in the associated namespace.", 41 | "operationId": "getPods", 42 | "parameters": [{ 43 | "name": "namespace_name", 44 | "in": "path", 45 | "description": "Unique name of the namespace", 46 | "required": true, 47 | "schema": { 48 | "type": "string" 49 | } 50 | }], 51 | "responses": { 52 | "200": { 53 | "description": "List of pods in a specified namespace", 54 | "content": { 55 | "application/json": { 56 | "schema": { 57 | "type": "object", 58 | "properties": { 59 | "pods": { 60 | "type": "array", 61 | "description": "An array of pods in the specified namespace.", 62 | "items": { 63 | "type": "object", 64 | "properties": { 65 | "name": { 66 | "type": "string", 67 | "description": "Name of the pod" 68 | }, 69 | "phase": { 70 | "type": "string", 71 | "description": "The phase of the pod" 72 | }, 73 | "containers": { 74 | "type": "array", 75 | "description": "An array of containers belonging to the pod", 76 | "items": { 77 | "type": "object", 78 | "properties": { 79 | "name": { 80 | "type": "string", 81 | "description": "Name of the container" 82 | }, 83 | "image": { 84 | "type": "string", 85 | "description": "Image of the container" 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | } 99 | } 100 | } 101 | }, 102 | "/reports/cis": { 103 | "get": { 104 | "summary": "Get the CIS Benchmark compliance report", 105 | "description": "Get the CIS Benchmark compliance report. This API returns an object with the compliance control checks specification and the status results.", 106 | "operationId": "getCISReport", 107 | "responses": { 108 | "200": { 109 | "description": "CIS Benchmark compliance report object", 110 | "content": { 111 | "application/json": { 112 | "schema": { 113 | "type": "object", 114 | "properties": { 115 | "status": { 116 | "type": "object", 117 | "description": "The status object", 118 | "properties": { 119 | "summary": { 120 | "type": "object", 121 | "description": "The summary object", 122 | "properties": { 123 | "failCount": { 124 | "type": "integer", 125 | "description": "Number of failed checks" 126 | }, 127 | "passCount": { 128 | "type": "integer", 129 | "description": "Number of passed checks" 130 | } 131 | } 132 | }, 133 | "summaryReport": { 134 | "type": "object", 135 | "description": "The summary report object", 136 | "properties": { 137 | "controlCheck": { 138 | "type": "array", 139 | "description": "An array of control checks", 140 | "items": { 141 | "type": "object", 142 | "description": "A control check object", 143 | "properties": { 144 | "id": { 145 | "type": "string", 146 | "description": "Unique ID of the control check" 147 | }, 148 | "name": { 149 | "type": "string", 150 | "description": "Name of the control check" 151 | }, 152 | "severity": { 153 | "type": "string", 154 | "description": "Severity of the control check" 155 | }, 156 | "totalFail": { 157 | "type": "integer", 158 | "description": "Total number of failed checks" 159 | } 160 | } 161 | } 162 | }, 163 | "id": { 164 | "type": "string", 165 | "description": "Unique ID of the summary report" 166 | }, 167 | "title": { 168 | "type": "string", 169 | "description": "Title of the summary report" 170 | } 171 | } 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | } 179 | } 180 | } 181 | } 182 | } 183 | } 184 | } --------------------------------------------------------------------------------