├── .banditrc ├── .checkov.yml ├── .flake8 ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── THIRD-PARTY-LICENSES ├── architecture.png ├── cdk ├── .gitignore ├── app.py ├── cdk.json └── cdk │ └── cdk_stack.py ├── edit-secret.png ├── edit-vpc.png ├── functions ├── thick │ ├── Dockerfile │ ├── lambda_handler.py │ └── requirements.txt ├── thin-layer │ └── requirements.txt └── thin │ └── lambda_handler.py ├── pyproject.toml ├── pytest.ini ├── requirements-dev.txt ├── test-function.png └── tests ├── conftest.py └── test_lambda_handler.py /.banditrc: -------------------------------------------------------------------------------- 1 | [bandit] 2 | exclude: ./.venv 3 | -------------------------------------------------------------------------------- /.checkov.yml: -------------------------------------------------------------------------------- 1 | #compact: true 2 | #quiet: true 3 | download-external-modules: true 4 | external-modules-download-path: .external_modules 5 | framework: all 6 | evaluate-variables: true 7 | skip-path: cdk/cdk.out/asset.* 8 | skip-check: CKV_AWS_117 9 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E203, E266, E501, W503, F403, F401, E741, F541, C901 3 | max-line-length = 120 4 | max-complexity = 18 5 | select = B,C,E,F,W,T4,B9 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## python 2 | 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 99 | __pypackages__/ 100 | 101 | # Celery stuff 102 | celerybeat-schedule 103 | celerybeat.pid 104 | 105 | # SageMath parsed files 106 | *.sage.py 107 | 108 | # Environments 109 | .env 110 | .venv 111 | env/ 112 | venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # pytype static type analyzer 136 | .pytype/ 137 | 138 | # Cython debug symbols 139 | cython_debug/ 140 | 141 | 142 | # build 143 | junit.xml 144 | 145 | ### VisualStudioCode ### 146 | .vscode/ 147 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | known_third_party =aws_cdk,aws_lambda_powertools,constructs,oracledb,pytest 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | default_language_version: 4 | python: python3.9 5 | default_stages: 6 | - commit 7 | - push 8 | fail_fast: False 9 | repos: 10 | - repo: https://github.com/pre-commit/pre-commit-hooks 11 | rev: v3.4.0 12 | hooks: 13 | - id: trailing-whitespace 14 | - id: end-of-file-fixer 15 | - id: check-yaml 16 | - id: check-case-conflict 17 | - id: check-merge-conflict 18 | - id: check-executables-have-shebangs 19 | - id: check-added-large-files 20 | - id: check-docstring-first 21 | - id: check-symlinks 22 | - id: debug-statements 23 | - id: detect-aws-credentials 24 | args: 25 | - --allow-missing-credentials 26 | - id: detect-private-key 27 | - id: no-commit-to-branch 28 | name: no-commit-to-master 29 | args: 30 | - --branch 31 | - master 32 | - id: no-commit-to-branch 33 | name: no-commit-to-main 34 | args: 35 | - --branch 36 | - main 37 | - id: mixed-line-ending 38 | args: [ --fix=lf ] 39 | - id: requirements-txt-fixer 40 | - repo: https://gitlab.com/pycqa/flake8 41 | rev: 3.9.2 42 | hooks: 43 | - id: flake8 44 | additional_dependencies: 45 | - mccabe==0.6.1 46 | - repo: https://github.com/jorisroovers/gitlint 47 | rev: v0.15.1 48 | hooks: 49 | - id: gitlint 50 | stages: [commit-msg] 51 | - repo: https://github.com/asottile/add-trailing-comma 52 | rev: v2.1.0 53 | hooks: 54 | - id: add-trailing-comma 55 | - repo: https://github.com/asottile/seed-isort-config 56 | rev: v1.9.3 57 | hooks: 58 | - id: seed-isort-config 59 | - repo: https://github.com/pre-commit/mirrors-isort 60 | rev: v4.3.21 61 | hooks: 62 | - id: isort 63 | additional_dependencies: [toml] 64 | - repo: https://github.com/psf/black 65 | rev: 21.12b0 66 | hooks: 67 | - id: black 68 | language_version: python3.9 69 | - repo: https://github.com/pycqa/pydocstyle 70 | rev: 6.1.1 71 | hooks: 72 | - id: pydocstyle 73 | additional_dependencies: 74 | - toml 75 | - repo: https://github.com/Lucas-C/pre-commit-hooks-bandit 76 | rev: v1.0.5 77 | hooks: 78 | - id: python-bandit-vulnerability-check 79 | args: [--ini, .banditrc, --recursive, modules, --exclude, "*/.external_modules/*,*/.terraform/*"] 80 | - repo: https://github.com/pre-commit/mirrors-mypy 81 | rev: 'v0.910' 82 | hooks: 83 | - id: mypy 84 | additional_dependencies: [types-requests] 85 | args: [--strict, --ignore-missing-imports, --allow-untyped-decorators, --install-types, --non-interactive, --scripts-are-modules] 86 | pass_filenames: false 87 | - repo: local 88 | hooks: 89 | - id: security-scanning 90 | name: checkov 91 | entry: bash -c 'cd cdk && cdk synth > /dev/null && cd .. && checkov -d . --compact --quiet --skip-suppressions' 92 | language: system 93 | pass_filenames: false 94 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Nicolas Jacob Baer 2 | Goeksel Sarikaya 3 | Shukhrat Khodjaev 4 | Moinul Al-Mamun 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Oracle Database Connection in AWS Lambda using Python 2 | 3 | This example shows how to connect to an Oracle database (RDS or on-prem) from AWS Lambda using python. 4 | The official Oracle driver (python-oracledb) comes with two different modes: (1) a *thin* mode that does not 5 | require any additional system libraries, but comes with a few limitations and a *thick* mode, which requires the Oracle Instant Client with system dependencies 6 | to connect to an Oracle database. The example here deploys both versions of the driver. However, the thick mode does not fit well into a lambda package or layer. Therefore 7 | this example shows how to use Lambda container images to properly install the Oracle dependencies. The thin mode can be deployed using a simple lambda layer and is the leaner version. 8 | 9 | This example will deploy both, the thin and the tick mode for reference. For application usage, we recommend to pick the option that is suitable for the application. See the following reference for 10 | a description of these options: https://python-oracledb.readthedocs.io/en/latest/user_guide/appendix_a.html#featuresummary 11 | 12 | ![architecture diagram](architecture.png "Architecture") 13 | 14 | 15 | ## Requirements 16 | 17 | The following software is necessary on the client in order to build and deploy this example. 18 | 19 | - Python 3.9.x (preferably with a dedicated virtualenv) 20 | - Docker 19.x or above 21 | - CDK version 2.3.0 22 | 23 | In addition, this setup does not include an Oracle database, but assumes that one already exists. 24 | The deployment procedure outlined below will download and install the [python-oracledb driver](https://oracle.github.io/python-oracledb/index.html) and [Oracle Instant Client](http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html), both are subject to separate licensing terms. 25 | 26 | 27 | ## AWS Services 28 | 29 | The following AWS services are used to run this example. 30 | 31 | - **AWS Lambda** establish connection and run queries against Oracle database 32 | - **Elastic Container Registry (Amazon ECR)** storage for lambda container image versions 33 | - **AWS Secrets Manager** store credentials to connect to Oracle database 34 | - **AWS X-Ray** providing tracing information for lambda 35 | - **AWS SQS** dead letter queue for lambda failed lambda events 36 | 37 | 38 | ## Deployment Instructions 39 | 40 | 1. Install build and deployment dependencies: 41 | 42 | ``` 43 | python -m pip install -r requirements-dev.txt 44 | ``` 45 | 46 | 2. Setup AWS profile to point to the account to deploy, either using AWS_PROFILE or through setting AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. 47 | 48 | 3. Deploy stack using CDK 49 | 50 | ``` 51 | cd cdk 52 | cdk bootstrap 53 | cdk deploy 54 | ``` 55 | 56 | 4. Open SecretsManager in the AWS Console and adapt the connection settings in the secret `py-oracle-connection-credentials` 57 | 58 | ![edit secret in secrets manager](edit-secret.png "Add Credentials") 59 | 60 | 5. Open the Lambda function `py-oracle-connection-example-thin` or `py-oracle-connection-example-thick` in the AWS Console and specify the correct VPC settings in order to connect to the Oracle database. 61 | 62 | ![configure vpc](edit-vpc.png "Add VPC configuration to connect to Oracle") 63 | 64 | 6. Execute the lambda function with an empty example payload (e.g. `{}`) 65 | 66 | ![test function](test-function.png "Test Lambda function") 67 | 68 | 7. Adapt the lambda handler as necessary to execute queries against Oracle database, see [lambda_handler.py](functions/thin/lambda_handler.py) 69 | 70 | 8. Destroy all resources when tests are done 71 | 72 | ``` 73 | cd cdk 74 | cdk destroy 75 | ``` 76 | 77 | ## Limitations 78 | 79 | The example presented here opens an Oracle database connection per execution. This is not suitable for high-throughput scenarios. Therefore it is adviced 80 | to configure the reserved concurrency for the lambda according to the maximum open connections in parallel that the database allows for. 81 | 82 | ## Reference 83 | 84 | - [Lambda Container Images](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html) 85 | - [Lambda Secrets Management](https://aws.amazon.com/blogs/security/how-to-securely-provide-database-credentials-to-lambda-functions-by-using-aws-secrets-manager/) 86 | - [Oracle Driver Documentation](https://cx-oracle.readthedocs.io/en/latest/) 87 | 88 | 89 | ## Contributions 90 | 91 | See CONTRIBUTING for more information. 92 | 93 | 94 | ## License 95 | 96 | This library is licensed under the MIT-0 License. See the LICENSE file. 97 | -------------------------------------------------------------------------------- /THIRD-PARTY-LICENSES: -------------------------------------------------------------------------------- 1 | ** oracle-instant-client; version 21.3 -- http://www.oracle.com/technetwork/database/database-technologies/instant-client/overview/index.html 2 | Copyright © 2016-2018, Oracle and/or its affiliates. All rights reserved. 3 | 4 | Copyright © 2007-2015, Anthony Tuininga. All rights reserved. 5 | 6 | Copyright © 2001-2007, Computronix (Canada) Ltd., Edmonton, Alberta, Canada. All 7 | rights reserved. 8 | 9 | You may use the Oracle Instant Client Basic (“Oracle Client”) – which is Third- 10 | Party Content owned by Oracle America, Inc. (“Oracle”) – included in this 11 | package subject to the "Ownership," "Export Controls," "Disclaimer of 12 | Warranties; Limitation of Liability," "No Technical Support", "Audit; 13 | Termination (except that Oracle’s audit right is not included)," "Relationship 14 | Between the Parties," and “U.S. Government End Users” of the Oracle Technology 15 | Network License Agreement (as of April 23, 2018) found at 16 | http://www.oracle.com/technetwork/licenses/distribution-license-152002.html. In 17 | addition, you may not distribute the specific Oracle Client included in this 18 | package to any third parties. Only as relates to the specific Oracle Client 19 | included in this package, Oracle is an intended third party beneficiary of the 20 | applicable terms of the Oracle Technology Network License Agreement (as of April 21 | 23, 2018) listed above. 22 | Redistribution and use in source and binary forms, with or without modification, 23 | are permitted provided that the following conditions are met: 24 | 25 | Redistributions of source code must retain the above copyright notice, this 26 | list of conditions, and the disclaimer that follows. 27 | Redistributions in binary form must reproduce the above copyright notice, 28 | this list of conditions, and the following disclaimer in the documentation 29 | and/or other materials provided with the distribution. 30 | Neither the names of the copyright holders nor the names of any contributors 31 | may be used to endorse or promote products derived from this software without 32 | specific prior written permission. 33 | 34 | DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35 | *AS IS* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 36 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 38 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 39 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 40 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 41 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 42 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 43 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | 45 | Computronix ® is a registered trademark of Computronix (Canada) Ltd. 46 | 47 | ------ 48 | 49 | ** python-oracledb; version 1.2.1 -- https://github.com/oracle/python-oracledb/blob/main/LICENSE.txt 50 | Copyright (c) 2016, 2022 Oracle and/or its affiliates. 51 | 52 | This software is dual-licensed to you under the Universal Permissive License 53 | (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl and Apache License 54 | 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose 55 | either license. 56 | 57 | If you elect to accept the software under the Apache License, Version 2.0, 58 | the following applies: 59 | 60 | Licensed under the Apache License, Version 2.0 (the "License"); 61 | you may not use this file except in compliance with the License. 62 | You may obtain a copy of the License at 63 | 64 | https://www.apache.org/licenses/LICENSE-2.0 65 | 66 | Unless required by applicable law or agreed to in writing, software 67 | distributed under the License is distributed on an "AS IS" BASIS, 68 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 69 | See the License for the specific language governing permissions and 70 | limitations under the License. 71 | 72 | 73 | The Universal Permissive License (UPL), Version 1.0 74 | =================================================== 75 | 76 | Subject to the condition set forth below, permission is hereby granted to any 77 | person obtaining a copy of this software, associated documentation and/or data 78 | (collectively the "Software"), free of charge and under any and all copyright 79 | rights in the Software, and any and all patent rights owned or freely 80 | licensable by each licensor hereunder covering either (i) the unmodified 81 | Software as contributed to or provided by such licensor, or (ii) the Larger 82 | Works (as defined below), to deal in both 83 | 84 | (a) the Software, and 85 | 86 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 87 | one is included with the Software (each a "Larger Work" to which the 88 | Software is contributed by such licensors), 89 | 90 | without restriction, including without limitation the rights to copy, create 91 | derivative works of, display, perform, and distribute the Software and make, 92 | use, sell, offer for sale, import, export, have made, and have sold the 93 | Software and the Larger Work(s), and to sublicense the foregoing rights on 94 | either these or other terms. 95 | 96 | This license is subject to the following condition: 97 | 98 | The above copyright notice and either this complete permission notice or at a 99 | minimum a reference to the UPL must be included in all copies or substantial 100 | portions of the Software. 101 | 102 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 103 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 104 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 105 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 106 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 107 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 108 | SOFTWARE. 109 | 110 | 111 | Apache License 112 | ============== 113 | 114 | Version 2.0, January 2004 115 | 116 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 117 | 118 | 1. **Definitions**. 119 | 120 | "License" shall mean the terms and conditions for use, reproduction, and 121 | distribution as defined by Sections 1 through 9 of this document. 122 | 123 | "Licensor" shall mean the copyright owner or entity authorized by the 124 | copyright owner that is granting the License. 125 | 126 | "Legal Entity" shall mean the union of the acting entity and all other 127 | entities that control, are controlled by, or are under common control with 128 | that entity. For the purposes of this definition, "control" means (i) the 129 | power, direct or indirect, to cause the direction or management of such 130 | entity, whether by contract or otherwise, or (ii) ownership of fifty 131 | percent (50%) or more of the outstanding shares, or (iii) beneficial 132 | ownership of such entity. 133 | 134 | "You" (or "Your") shall mean an individual or Legal Entity exercising 135 | permissions granted by this License. 136 | 137 | "Source" form shall mean the preferred form for making modifications, 138 | including but not limited to software source code, documentation source, 139 | and configuration files. 140 | 141 | "Object" form shall mean any form resulting from mechanical transformation 142 | or translation of a Source form, including but not limited to compiled 143 | object code, generated documentation, and conversions to other media types. 144 | 145 | "Work" shall mean the work of authorship, whether in Source or Object form, 146 | made available under the License, as indicated by a copyright notice that 147 | is included in or attached to the work (an example is provided in the 148 | Appendix below). 149 | 150 | "Derivative Works" shall mean any work, whether in Source or Object form, 151 | that is based on (or derived from) the Work and for which the editorial 152 | revisions, annotations, elaborations, or other modifications represent, as 153 | a whole, an original work of authorship. For the purposes of this License, 154 | Derivative Works shall not include works that remain separable from, or 155 | merely link (or bind by name) to the interfaces of, the Work and Derivative 156 | Works thereof. 157 | 158 | "Contribution" shall mean any work of authorship, including the original 159 | version of the Work and any modifications or additions to that Work or 160 | Derivative Works thereof, that is intentionally submitted to Licensor for 161 | inclusion in the Work by the copyright owner or by an individual or Legal 162 | Entity authorized to submit on behalf of the copyright owner. For the 163 | purposes of this definition, "submitted" means any form of electronic, 164 | verbal, or written communication sent to the Licensor or its 165 | representatives, including but not limited to communication on electronic 166 | mailing lists, source code control systems, and issue tracking systems that 167 | are managed by, or on behalf of, the Licensor for the purpose of discussing 168 | and improving the Work, but excluding communication that is conspicuously 169 | marked or otherwise designated in writing by the copyright owner as "Not a 170 | Contribution." 171 | 172 | "Contributor" shall mean Licensor and any individual or Legal Entity on 173 | behalf of whom a Contribution has been received by Licensor and 174 | subsequently incorporated within the Work. 175 | 176 | 2. **Grant of Copyright License.** Subject to the terms and conditions of this 177 | License, each Contributor hereby grants to You a perpetual, worldwide, 178 | non-exclusive, no-charge, royalty-free, irrevocable copyright license to 179 | reproduce, prepare Derivative Works of, publicly display, publicly perform, 180 | sublicense, and distribute the Work and such Derivative Works in Source or 181 | Object form. 182 | 183 | 3. **Grant of Patent License.** Subject to the terms and conditions of this 184 | License, each Contributor hereby grants to You a perpetual, worldwide, 185 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in 186 | this section) patent license to make, have made, use, offer to sell, sell, 187 | import, and otherwise transfer the Work, where such license applies only to 188 | those patent claims licensable by such Contributor that are necessarily 189 | infringed by their Contribution(s) alone or by combination of their 190 | Contribution(s) with the Work to which such Contribution(s) was submitted. 191 | If You institute patent litigation against any entity (including a 192 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 193 | Contribution incorporated within the Work constitutes direct or 194 | contributory patent infringement, then any patent licenses granted to You 195 | under this License for that Work shall terminate as of the date such 196 | litigation is filed. 197 | 198 | 4. **Redistribution.** You may reproduce and distribute copies of the Work or 199 | Derivative Works thereof in any medium, with or without modifications, and 200 | in Source or Object form, provided that You meet the following conditions: 201 | 202 | 1. You must give any other recipients of the Work or Derivative Works a 203 | copy of this License; and 204 | 205 | 2. You must cause any modified files to carry prominent notices stating 206 | that You changed the files; and 207 | 208 | 3. You must retain, in the Source form of any Derivative Works that You 209 | distribute, all copyright, patent, trademark, and attribution notices 210 | from the Source form of the Work, excluding those notices that do not 211 | pertain to any part of the Derivative Works; and 212 | 213 | 4. If the Work includes a "NOTICE" text file as part of its distribution, 214 | then any Derivative Works that You distribute must include a readable 215 | copy of the attribution notices contained within such NOTICE file, 216 | excluding those notices that do not pertain to any part of the 217 | Derivative Works, in at least one of the following places: within a 218 | NOTICE text file distributed as part of the Derivative Works; within 219 | the Source form or documentation, if provided along with the Derivative 220 | Works; or, within a display generated by the Derivative Works, if and 221 | wherever such third-party notices normally appear. The contents of the 222 | NOTICE file are for informational purposes only and do not modify the 223 | License. You may add Your own attribution notices within Derivative 224 | Works that You distribute, alongside or as an addendum to the NOTICE 225 | text from the Work, provided that such additional attribution notices 226 | cannot be construed as modifying the License. 227 | 228 | You may add Your own copyright statement to Your modifications and may 229 | provide additional or different license terms and conditions for use, 230 | reproduction, or distribution of Your modifications, or for any such 231 | Derivative Works as a whole, provided Your use, reproduction, and 232 | distribution of the Work otherwise complies with the conditions stated 233 | in this License. 234 | 235 | 5. **Submission of Contributions.** Unless You explicitly state otherwise, any 236 | Contribution intentionally submitted for inclusion in the Work by You to 237 | the Licensor shall be under the terms and conditions of this License, 238 | without any additional terms or conditions. Notwithstanding the above, 239 | nothing herein shall supersede or modify the terms of any separate license 240 | agreement you may have executed with Licensor regarding such Contributions. 241 | 242 | 6. **Trademarks.** This License does not grant permission to use the trade 243 | names, trademarks, service marks, or product names of the Licensor, except 244 | as required for reasonable and customary use in describing the origin of 245 | the Work and reproducing the content of the NOTICE file. 246 | 247 | 7. **Disclaimer of Warranty.** Unless required by applicable law or agreed to 248 | in writing, Licensor provides the Work (and each Contributor provides its 249 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 250 | KIND, either express or implied, including, without limitation, any 251 | warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or 252 | FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for 253 | determining the appropriateness of using or redistributing the Work and 254 | assume any risks associated with Your exercise of permissions under this 255 | License. 256 | 257 | 8. **Limitation of Liability.** In no event and under no legal theory, whether 258 | in tort (including negligence), contract, or otherwise, unless required by 259 | applicable law (such as deliberate and grossly negligent acts) or agreed to 260 | in writing, shall any Contributor be liable to You for damages, including 261 | any direct, indirect, special, incidental, or consequential damages of any 262 | character arising as a result of this License or out of the use or 263 | inability to use the Work (including but not limited to damages for loss of 264 | goodwill, work stoppage, computer failure or malfunction, or any and all 265 | other commercial damages or losses), even if such Contributor has been 266 | advised of the possibility of such damages. 267 | 268 | 9. **Accepting Warranty or Additional Liability.** While redistributing the 269 | Work or Derivative Works thereof, You may choose to offer, and charge a fee 270 | for, acceptance of support, warranty, indemnity, or other liability 271 | obligations and/or rights consistent with this License. However, in 272 | accepting such obligations, You may act only on Your own behalf and on Your 273 | sole responsibility, not on behalf of any other Contributor, and only if 274 | You agree to indemnify, defend, and hold each Contributor harmless for any 275 | liability incurred by, or claims asserted against, such Contributor by 276 | reason of your accepting any such warranty or additional liability. 277 | 278 | END OF TERMS AND CONDITIONS 279 | ------ 280 | 281 | ** boto3 - https://github.com/boto/boto3/ 282 | ** AWS Cloud Development Kit (AWS CDK) - https://github.com/aws/aws-cdk 283 | ** bandit - https://github.com/PyCQA/bandit 284 | ** checkov - https://github.com/bridgecrewio/checkov 285 | 286 | Apache License 287 | Version 2.0, January 2004 288 | http://www.apache.org/licenses/ 289 | 290 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 291 | 292 | 1. Definitions. 293 | 294 | "License" shall mean the terms and conditions for use, reproduction, 295 | and distribution as defined by Sections 1 through 9 of this document. 296 | 297 | "Licensor" shall mean the copyright owner or entity authorized by 298 | the copyright owner that is granting the License. 299 | 300 | "Legal Entity" shall mean the union of the acting entity and all 301 | other entities that control, are controlled by, or are under common 302 | control with that entity. For the purposes of this definition, 303 | "control" means (i) the power, direct or indirect, to cause the 304 | direction or management of such entity, whether by contract or 305 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 306 | outstanding shares, or (iii) beneficial ownership of such entity. 307 | 308 | "You" (or "Your") shall mean an individual or Legal Entity 309 | exercising permissions granted by this License. 310 | 311 | "Source" form shall mean the preferred form for making modifications, 312 | including but not limited to software source code, documentation 313 | source, and configuration files. 314 | 315 | "Object" form shall mean any form resulting from mechanical 316 | transformation or translation of a Source form, including but 317 | not limited to compiled object code, generated documentation, 318 | and conversions to other media types. 319 | 320 | "Work" shall mean the work of authorship, whether in Source or 321 | Object form, made available under the License, as indicated by a 322 | copyright notice that is included in or attached to the work 323 | (an example is provided in the Appendix below). 324 | 325 | "Derivative Works" shall mean any work, whether in Source or Object 326 | form, that is based on (or derived from) the Work and for which the 327 | editorial revisions, annotations, elaborations, or other modifications 328 | represent, as a whole, an original work of authorship. For the purposes 329 | of this License, Derivative Works shall not include works that remain 330 | separable from, or merely link (or bind by name) to the interfaces of, 331 | the Work and Derivative Works thereof. 332 | 333 | "Contribution" shall mean any work of authorship, including 334 | the original version of the Work and any modifications or additions 335 | to that Work or Derivative Works thereof, that is intentionally 336 | submitted to Licensor for inclusion in the Work by the copyright owner 337 | or by an individual or Legal Entity authorized to submit on behalf of 338 | the copyright owner. For the purposes of this definition, "submitted" 339 | means any form of electronic, verbal, or written communication sent 340 | to the Licensor or its representatives, including but not limited to 341 | communication on electronic mailing lists, source code control systems, 342 | and issue tracking systems that are managed by, or on behalf of, the 343 | Licensor for the purpose of discussing and improving the Work, but 344 | excluding communication that is conspicuously marked or otherwise 345 | designated in writing by the copyright owner as "Not a Contribution." 346 | 347 | "Contributor" shall mean Licensor and any individual or Legal Entity 348 | on behalf of whom a Contribution has been received by Licensor and 349 | subsequently incorporated within the Work. 350 | 351 | 2. Grant of Copyright License. Subject to the terms and conditions of 352 | this License, each Contributor hereby grants to You a perpetual, 353 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 354 | copyright license to reproduce, prepare Derivative Works of, 355 | publicly display, publicly perform, sublicense, and distribute the 356 | Work and such Derivative Works in Source or Object form. 357 | 358 | 3. Grant of Patent License. Subject to the terms and conditions of 359 | this License, each Contributor hereby grants to You a perpetual, 360 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 361 | (except as stated in this section) patent license to make, have made, 362 | use, offer to sell, sell, import, and otherwise transfer the Work, 363 | where such license applies only to those patent claims licensable 364 | by such Contributor that are necessarily infringed by their 365 | Contribution(s) alone or by combination of their Contribution(s) 366 | with the Work to which such Contribution(s) was submitted. If You 367 | institute patent litigation against any entity (including a 368 | cross-claim or counterclaim in a lawsuit) alleging that the Work 369 | or a Contribution incorporated within the Work constitutes direct 370 | or contributory patent infringement, then any patent licenses 371 | granted to You under this License for that Work shall terminate 372 | as of the date such litigation is filed. 373 | 374 | 4. Redistribution. You may reproduce and distribute copies of the 375 | Work or Derivative Works thereof in any medium, with or without 376 | modifications, and in Source or Object form, provided that You 377 | meet the following conditions: 378 | 379 | (a) You must give any other recipients of the Work or 380 | Derivative Works a copy of this License; and 381 | 382 | (b) You must cause any modified files to carry prominent notices 383 | stating that You changed the files; and 384 | 385 | (c) You must retain, in the Source form of any Derivative Works 386 | that You distribute, all copyright, patent, trademark, and 387 | attribution notices from the Source form of the Work, 388 | excluding those notices that do not pertain to any part of 389 | the Derivative Works; and 390 | 391 | (d) If the Work includes a "NOTICE" text file as part of its 392 | distribution, then any Derivative Works that You distribute must 393 | include a readable copy of the attribution notices contained 394 | within such NOTICE file, excluding those notices that do not 395 | pertain to any part of the Derivative Works, in at least one 396 | of the following places: within a NOTICE text file distributed 397 | as part of the Derivative Works; within the Source form or 398 | documentation, if provided along with the Derivative Works; or, 399 | within a display generated by the Derivative Works, if and 400 | wherever such third-party notices normally appear. The contents 401 | of the NOTICE file are for informational purposes only and 402 | do not modify the License. You may add Your own attribution 403 | notices within Derivative Works that You distribute, alongside 404 | or as an addendum to the NOTICE text from the Work, provided 405 | that such additional attribution notices cannot be construed 406 | as modifying the License. 407 | 408 | You may add Your own copyright statement to Your modifications and 409 | may provide additional or different license terms and conditions 410 | for use, reproduction, or distribution of Your modifications, or 411 | for any such Derivative Works as a whole, provided Your use, 412 | reproduction, and distribution of the Work otherwise complies with 413 | the conditions stated in this License. 414 | 415 | 5. Submission of Contributions. Unless You explicitly state otherwise, 416 | any Contribution intentionally submitted for inclusion in the Work 417 | by You to the Licensor shall be under the terms and conditions of 418 | this License, without any additional terms or conditions. 419 | Notwithstanding the above, nothing herein shall supersede or modify 420 | the terms of any separate license agreement you may have executed 421 | with Licensor regarding such Contributions. 422 | 423 | 6. Trademarks. This License does not grant permission to use the trade 424 | names, trademarks, service marks, or product names of the Licensor, 425 | except as required for reasonable and customary use in describing the 426 | origin of the Work and reproducing the content of the NOTICE file. 427 | 428 | 7. Disclaimer of Warranty. Unless required by applicable law or 429 | agreed to in writing, Licensor provides the Work (and each 430 | Contributor provides its Contributions) on an "AS IS" BASIS, 431 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 432 | implied, including, without limitation, any warranties or conditions 433 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 434 | PARTICULAR PURPOSE. You are solely responsible for determining the 435 | appropriateness of using or redistributing the Work and assume any 436 | risks associated with Your exercise of permissions under this License. 437 | 438 | 8. Limitation of Liability. In no event and under no legal theory, 439 | whether in tort (including negligence), contract, or otherwise, 440 | unless required by applicable law (such as deliberate and grossly 441 | negligent acts) or agreed to in writing, shall any Contributor be 442 | liable to You for damages, including any direct, indirect, special, 443 | incidental, or consequential damages of any character arising as a 444 | result of this License or out of the use or inability to use the 445 | Work (including but not limited to damages for loss of goodwill, 446 | work stoppage, computer failure or malfunction, or any and all 447 | other commercial damages or losses), even if such Contributor 448 | has been advised of the possibility of such damages. 449 | 450 | 9. Accepting Warranty or Additional Liability. While redistributing 451 | the Work or Derivative Works thereof, You may choose to offer, 452 | and charge a fee for, acceptance of support, warranty, indemnity, 453 | or other liability obligations and/or rights consistent with this 454 | License. However, in accepting such obligations, You may act only 455 | on Your own behalf and on Your sole responsibility, not on behalf 456 | of any other Contributor, and only if You agree to indemnify, 457 | defend, and hold each Contributor harmless for any liability 458 | incurred by, or claims asserted against, such Contributor by reason 459 | of your accepting any such warranty or additional liability. 460 | 461 | END OF TERMS AND CONDITIONS 462 | -------------------------------------------------------------------------------- /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-python-oracle-connection/4cf1a7a9aeb2555a35bdfa91688719e9105b280e/architecture.png -------------------------------------------------------------------------------- /cdk/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | __pycache__ 4 | .pytest_cache 5 | .venv 6 | *.egg-info 7 | 8 | # CDK asset staging directory 9 | .cdk.staging 10 | cdk.out 11 | -------------------------------------------------------------------------------- /cdk/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | ######################################################################## 3 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # SPDX-License-Identifier: MIT-0 5 | ######################################################################## 6 | import os 7 | 8 | import aws_cdk as cdk 9 | 10 | from cdk.cdk_stack import CdkStack 11 | 12 | 13 | app = cdk.App() 14 | CdkStack( 15 | app, 16 | "CdkStack", 17 | env=cdk.Environment(account=os.getenv("CDK_DEFAULT_ACCOUNT"), region=os.getenv("CDK_DEFAULT_REGION")), 18 | ) 19 | 20 | app.synth() 21 | -------------------------------------------------------------------------------- /cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "source.bat", 12 | "**/__init__.py", 13 | "python/__pycache__", 14 | "tests" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 19 | "@aws-cdk/core:stackRelativeExports": true, 20 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 21 | "@aws-cdk/aws-lambda:recognizeVersionProps": true, 22 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cdk/cdk/cdk_stack.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: MIT-0 4 | ######################################################################## 5 | import json 6 | import os 7 | from typing import Any, Dict 8 | 9 | from aws_cdk import Duration, Stack 10 | from aws_cdk.aws_iam import AnyPrincipal, Effect, PolicyStatement 11 | from aws_cdk.aws_kms import Key 12 | from aws_cdk.aws_lambda import DockerImageCode, DockerImageFunction, Runtime, Tracing 13 | from aws_cdk.aws_lambda_python_alpha import PythonFunction, PythonLayerVersion 14 | from aws_cdk.aws_secretsmanager import Secret, SecretStringGenerator 15 | from aws_cdk.aws_sqs import Queue 16 | from constructs import Construct 17 | 18 | 19 | class CdkStack(Stack): # type: ignore 20 | """Stack for the AWS Lambda Oracle connection example.""" 21 | 22 | def __init__(self, scope: Construct, construct_id: str, **kwargs: Dict[str, Any]) -> None: 23 | """CDK entry point. 24 | 25 | Args: 26 | scope (Construct): scope of the cdk stack 27 | construct_id (str): construct id of the stack 28 | """ 29 | super().__init__(scope, construct_id, **kwargs) 30 | 31 | kms_key = Key( 32 | self, 33 | "PyOracleKMSKey", 34 | description="KMS key for Py Oracle Connection", 35 | enable_key_rotation=True, 36 | pending_window=Duration.days(7), 37 | ) 38 | 39 | secret = self.build_connection_secret(kms_key) 40 | self.build_lambda(kms_key, secret) 41 | 42 | def build_lambda(self, kms_key: Key, secret: Secret) -> None: 43 | """Build Lambda function with connection to Oracle database. 44 | 45 | Args: 46 | kms_key (Key): encryption key for the secret and env variables 47 | secret (Secret): secret used to store Oracle connection details 48 | 49 | Returns: 50 | DockerImageFunction: lambda function 51 | """ 52 | stack_path = os.path.dirname(os.path.realpath(__file__)) 53 | lambda_path = os.path.join(stack_path, "..", "..", "functions") 54 | thick_path = os.path.join(lambda_path, "thick") 55 | thin_path = os.path.join(lambda_path, "thin") 56 | thin_path_layer = os.path.join(lambda_path, "thin-layer") 57 | 58 | dlq = Queue( 59 | self, 60 | "PyOracleConnectionLambdaDLQ", 61 | encryption_master_key=kms_key, 62 | retention_period=Duration.days(5), 63 | ) 64 | 65 | dlq.add_to_resource_policy( 66 | PolicyStatement( 67 | actions=["sqs:*"], 68 | effect=Effect.DENY, 69 | principals=[AnyPrincipal()], 70 | resources=[dlq.queue_arn], 71 | conditions={ 72 | "Bool": {"aws:secureTransport": "false"}, 73 | }, 74 | ), 75 | ) 76 | 77 | fn_thick = DockerImageFunction( 78 | self, 79 | "PyOracleConnectionLambdaThick", 80 | function_name="py-oracle-connection-example-thick", 81 | code=DockerImageCode.from_image_asset(directory=thick_path), 82 | description="Example Lambda to illustrate connection to Oracle using Python (thick client)", 83 | dead_letter_queue=dlq, 84 | environment={ 85 | "POWERTOOLS_SERVICE_NAME": "connection-example", 86 | "POWERTOOLS_METRICS_NAMESPACE": "PyOracleConn", 87 | "REGION": self.region, 88 | "SECRET_NAME": secret.secret_name, 89 | }, 90 | environment_encryption=kms_key, 91 | memory_size=128, 92 | tracing=Tracing.ACTIVE, 93 | reserved_concurrent_executions=5, 94 | timeout=Duration.seconds(45), 95 | ) 96 | 97 | fn_thin_layer = PythonLayerVersion( 98 | self, 99 | "PyOracleConnectionLambdaThinLayer", 100 | entry=thin_path_layer, 101 | compatible_runtimes=[Runtime.PYTHON_3_9], 102 | ) 103 | 104 | fn_thin = PythonFunction( 105 | self, 106 | "PyOracleConnectionLambdaThin", 107 | function_name="py-oracle-connection-example-thin", 108 | description="Example Lambda to illustrate connection to Oracle using Python (thin client)", 109 | entry=thin_path, 110 | runtime=Runtime.PYTHON_3_9, 111 | index="lambda_handler.py", 112 | handler="handler", 113 | dead_letter_queue=dlq, 114 | layers=[fn_thin_layer], 115 | environment={ 116 | "POWERTOOLS_SERVICE_NAME": "connection-example", 117 | "POWERTOOLS_METRICS_NAMESPACE": "PyOracleConn", 118 | "REGION": self.region, 119 | "SECRET_NAME": secret.secret_name, 120 | }, 121 | environment_encryption=kms_key, 122 | memory_size=128, 123 | tracing=Tracing.ACTIVE, 124 | reserved_concurrent_executions=5, 125 | timeout=Duration.seconds(45), 126 | ) 127 | 128 | for fn in [fn_thin, fn_thick]: 129 | kms_key.grant_decrypt(fn) 130 | secret.grant_read(fn) 131 | 132 | def build_connection_secret(self, kms_key: Key) -> Secret: 133 | """Secret for the Oracle DB Connection. 134 | 135 | Args: 136 | kms_key (Key): kms key for encryption 137 | 138 | Returns: 139 | Secret: secret in secret manager 140 | """ 141 | template = SecretStringGenerator( 142 | secret_string_template=json.dumps({"host": "", "port": "", "sid": "", "username": ""}), 143 | generate_string_key="password", 144 | ) 145 | 146 | return Secret( 147 | self, 148 | "PyOracleConnectionCredentials", 149 | generate_secret_string=template, 150 | encryption_key=kms_key, 151 | secret_name="py-oracle-connection-credentials", 152 | ) 153 | -------------------------------------------------------------------------------- /edit-secret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-python-oracle-connection/4cf1a7a9aeb2555a35bdfa91688719e9105b280e/edit-secret.png -------------------------------------------------------------------------------- /edit-vpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-python-oracle-connection/4cf1a7a9aeb2555a35bdfa91688719e9105b280e/edit-vpc.png -------------------------------------------------------------------------------- /functions/thick/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM public.ecr.aws/lambda/python:3.9 2 | # for further reference, see https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-reqs 3 | #checkov:skip=CKV_DOCKER_3::user setup not necessary in lambda context, see link line above 4 | 5 | # copy function 6 | COPY . ${LAMBDA_TASK_ROOT} 7 | 8 | # install python dependencies 9 | RUN pip3 install --upgrade pip && pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" 10 | 11 | # setup oracle paths 12 | ENV ORACLE_HOME=/opt/oracle 13 | ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib 14 | 15 | # install oracle client library dependencies 16 | RUN yum update -y \ 17 | && yum install -y libaio unzip \ 18 | && yum clean all \ 19 | && rm -rf /var/cache/yum 20 | 21 | # install oracle client library 22 | RUN curl -o instantclient.zip https://download.oracle.com/otn_software/linux/instantclient/213000/instantclient-basic-linux.x64-21.3.0.0.0.zip \ 23 | && unzip instantclient.zip \ 24 | && mkdir -p $ORACLE_HOME \ 25 | && mv instantclient_21_3 $ORACLE_HOME/lib \ 26 | && rm -f instantclient.zip 27 | 28 | # see https://docs.aws.amazon.com/lambda/latest/dg/troubleshooting-deployment.html#troubleshooting-deployment-denied 29 | RUN chmod 644 $(find . -type f) && chmod 755 $(find . -type d) 30 | 31 | # not required in lambda context 32 | HEALTHCHECK NONE 33 | 34 | CMD [ "lambda_handler.handler" ] 35 | -------------------------------------------------------------------------------- /functions/thick/lambda_handler.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: MIT-0 4 | ######################################################################## 5 | 6 | import os 7 | from typing import Any, Dict, List 8 | 9 | import oracledb as cx_Oracle 10 | from aws_lambda_powertools import Logger, Tracer 11 | from aws_lambda_powertools.metrics import Metrics 12 | from aws_lambda_powertools.utilities import parameters 13 | from aws_lambda_powertools.utilities.typing import LambdaContext 14 | 15 | 16 | REGION = os.getenv("REGION") 17 | SECRET_NAME = os.getenv("SECRET_NAME") 18 | 19 | tracer = Tracer() 20 | logger = Logger() 21 | metrics = Metrics() 22 | cx_Oracle.init_oracle_client() 23 | 24 | 25 | @tracer.capture_lambda_handler 26 | @metrics.log_metrics 27 | def handler(event: Dict[str, Any], _: LambdaContext) -> List[str]: 28 | """ 29 | Lambda function example to connect to Oracle database using python-oracledb. 30 | 31 | Keep in mind that this function will open a single connection on each execution 32 | and therefore measures need to be put in place to not overwhelm the database - 33 | either through a proxy or limiting the concurrency. 34 | 35 | Args: 36 | event: Lambda event - ignored for now 37 | context: lambda context 38 | """ 39 | logger.info(f"fetching secret {SECRET_NAME} from secret manager") 40 | secret = parameters.get_secret(SECRET_NAME, transform="json") 41 | 42 | dsn = cx_Oracle.makedsn(secret["host"], secret["port"], secret["sid"]) 43 | logger.info(f"connecting to oracle database: {dsn}") 44 | 45 | with cx_Oracle.connect( 46 | user=secret["username"], 47 | password=secret["password"], 48 | dsn=dsn, 49 | encoding="UTF-8", 50 | ) as connection: 51 | query = """ 52 | SELECT 53 | table_name 54 | FROM 55 | all_tables 56 | """ 57 | logger.info(f"displaying all accessible tables in database.") 58 | with connection.cursor() as cursor: 59 | cursor.execute(query) 60 | tables: List[str] = cursor.fetchall() 61 | logger.info(f"found {len(tables)} tables: {tables}") 62 | return tables 63 | -------------------------------------------------------------------------------- /functions/thick/requirements.txt: -------------------------------------------------------------------------------- 1 | aws_lambda_powertools[tracer]~=2.5.0 2 | boto3~=1.26.41 3 | oracledb~=1.2.1 4 | -------------------------------------------------------------------------------- /functions/thin-layer/requirements.txt: -------------------------------------------------------------------------------- 1 | aws_lambda_powertools[tracer]~=2.5.0 2 | boto3~=1.26.41 3 | oracledb~=1.2.1 4 | -------------------------------------------------------------------------------- /functions/thin/lambda_handler.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: MIT-0 4 | ######################################################################## 5 | 6 | import os 7 | from typing import Any, Dict, List 8 | 9 | import oracledb as cx_Oracle 10 | from aws_lambda_powertools import Logger, Tracer 11 | from aws_lambda_powertools.metrics import Metrics 12 | from aws_lambda_powertools.utilities import parameters 13 | from aws_lambda_powertools.utilities.typing import LambdaContext 14 | 15 | 16 | REGION = os.getenv("REGION") 17 | SECRET_NAME = os.getenv("SECRET_NAME") 18 | 19 | tracer = Tracer() 20 | logger = Logger() 21 | metrics = Metrics() 22 | 23 | 24 | @tracer.capture_lambda_handler 25 | @metrics.log_metrics 26 | def handler(event: Dict[str, Any], _: LambdaContext) -> List[str]: 27 | """ 28 | Lambda function example to connect to Oracle database using python-oracledb. 29 | 30 | Keep in mind that this function will open a single connection on each execution 31 | and therefore measures need to be put in place to not overwhelm the database - 32 | either through a proxy or limiting the concurrency. 33 | 34 | Args: 35 | event: Lambda event - ignored for now 36 | context: lambda context 37 | """ 38 | logger.info(f"fetching secret {SECRET_NAME} from secret manager") 39 | secret = parameters.get_secret(SECRET_NAME, transform="json") 40 | 41 | dsn = cx_Oracle.makedsn(secret["host"], secret["port"], secret["sid"]) 42 | logger.info(f"connecting to oracle database: {dsn}") 43 | 44 | with cx_Oracle.connect( 45 | user=secret["username"], 46 | password=secret["password"], 47 | dsn=dsn, 48 | encoding="UTF-8", 49 | ) as connection: 50 | query = """ 51 | SELECT 52 | table_name 53 | FROM 54 | all_tables 55 | """ 56 | logger.info(f"displaying all accessible tables in database.") 57 | with connection.cursor() as cursor: 58 | cursor.execute(query) 59 | tables: List[str] = cursor.fetchall() 60 | logger.info(f"found {len(tables)} tables: {tables}") 61 | return tables 62 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.isort] 2 | multi_line_output = 3 3 | include_trailing_comma = true 4 | force_grid_wrap = 0 5 | use_parantheses = true 6 | line_length = 120 7 | lines_after_imports = 2 8 | 9 | [tool.black] 10 | line-length = 120 11 | include = '\.pyi?$' 12 | exclude = ''' 13 | /( 14 | \.git 15 | | \.hg 16 | | \.mypy_cache 17 | | \.tox 18 | | \.venv 19 | | _build 20 | | egg-info 21 | | buck-out 22 | | build 23 | | dist 24 | | env 25 | )/ 26 | ''' 27 | 28 | [tool.pydocstyle] 29 | convention = "google" 30 | add-ignore = "D100,D212" 31 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = 3 | -rsxX 4 | --cov=functions 5 | --cov-report=xml 6 | --cov-report=term-missing 7 | --junitxml junit.xml 8 | testpaths = tests 9 | log_cli_level = ERROR 10 | log_format = %(asctime)s %(levelname)s %(message)s 11 | log_date_format = %Y-%m-%d %H:%M:%S 12 | cache_dir = .cache 13 | pythonpath = functions 14 | junit_family=xunit2 15 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | 2 | -r functions/thin-layer/requirements.txt 3 | aws-cdk-lib~=2.58.1 4 | aws-cdk.aws-lambda-python-alpha~=2.58.1a0 5 | bandit~=1.7.1 6 | black==24.3.0 7 | checkov~=2.2.229 8 | flake8~=4.0.1 9 | pre-commit~=2.16.0 10 | pytest~=6.2.5 11 | pytest-cov~=3.0.0 12 | -------------------------------------------------------------------------------- /test-function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-python-oracle-connection/4cf1a7a9aeb2555a35bdfa91688719e9105b280e/test-function.png -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: MIT-0 4 | ######################################################################## 5 | 6 | import os 7 | import sys 8 | from unittest.mock import MagicMock 9 | 10 | 11 | sys.modules["oracledb"] = MagicMock() 12 | os.environ["REGION"] = "eu-central-1" 13 | os.environ["SECRET_NAME"] = "mock-secret" 14 | -------------------------------------------------------------------------------- /tests/test_lambda_handler.py: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # SPDX-License-Identifier: MIT-0 4 | ######################################################################## 5 | 6 | import importlib 7 | from typing import Any 8 | from unittest import mock 9 | from unittest.mock import MagicMock 10 | 11 | import pytest 12 | 13 | 14 | class TestLambdaHandler: 15 | """Tests for the lambda handler.""" 16 | 17 | TABLES = ["table1", "table2", "table3"] 18 | 19 | @pytest.fixture(params=["thick", "thin"]) 20 | def lambda_handler(self, request: Any) -> Any: 21 | """Imports lambda handler for both thick and thin mode. 22 | 23 | Args: 24 | request (Any): _description_ 25 | 26 | Returns: 27 | Any: _description_ 28 | """ 29 | return importlib.import_module(f"functions.{request.param}.lambda_handler") 30 | 31 | @pytest.fixture() 32 | def mock_connection(self) -> MagicMock: 33 | """ 34 | Mock Oracle database connection. 35 | 36 | Returns: 37 | mock of database connection 38 | """ 39 | connection_mock = MagicMock() 40 | connection_mock.cursor().__enter__().fetchall.return_value = self.TABLES 41 | return connection_mock 42 | 43 | def test_query_should_return_tables(self, mock_connection: MagicMock, lambda_handler: Any) -> None: 44 | """Test query should return tables. 45 | 46 | Args: 47 | mock_connection (MagicMock): connection mock to database 48 | lambda_handler (Any): module of lambda handler to test 49 | """ 50 | lambda_handler.parameters = MagicMock() 51 | lambda_handler.cx_Oracle.connect().__enter__.return_value = mock_connection 52 | result = lambda_handler.handler(None, None) 53 | assert self.TABLES == result 54 | 55 | def test_query_should_fail_on_connection_issue(self, lambda_handler: Any) -> None: 56 | """Test query should fail when connection to database fails. 57 | 58 | Args: 59 | lambda_handler (Any): module of lambda handler to test 60 | """ 61 | lambda_handler.parameters = MagicMock() 62 | lambda_handler.cx_Oracle.connect().__enter__.side_effect = ValueError() 63 | with pytest.raises(ValueError): 64 | lambda_handler.handler(None, None) 65 | --------------------------------------------------------------------------------