├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bookstore ├── books │ ├── db_connection.py │ ├── main.py │ └── requirements.txt ├── reviews │ ├── main.py │ └── requirements.txt └── template.yaml ├── db_schema ├── .gitignore ├── db_schema_lambda │ ├── main.py │ └── requirements.txt ├── db_schema_lambda_layer │ ├── Makefile │ ├── bookstore_orm_objects.py │ ├── bookstore_utils.py │ └── requirements.txt └── template.yaml ├── docs └── blog-sql-alchemy-solution-diagram.png └── rds_proxy └── template.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### OSX ### 20 | *.DS_Store 21 | .AppleDouble 22 | .LSOverride 23 | 24 | # Icon must end with two \r 25 | Icon 26 | 27 | # Thumbnails 28 | ._* 29 | 30 | # Files that might appear in the root of a volume 31 | .DocumentRevisions-V100 32 | .fseventsd 33 | .Spotlight-V100 34 | .TemporaryItems 35 | .Trashes 36 | .VolumeIcon.icns 37 | .com.apple.timemachine.donotpresent 38 | 39 | # Directories potentially created on remote AFP share 40 | .AppleDB 41 | .AppleDesktop 42 | Network Trash Folder 43 | Temporary Items 44 | .apdisk 45 | 46 | ### PyCharm ### 47 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 48 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 49 | 50 | # User-specific stuff: 51 | .idea/**/workspace.xml 52 | .idea/**/tasks.xml 53 | .idea/dictionaries 54 | 55 | # Sensitive or high-churn files: 56 | .idea/**/dataSources/ 57 | .idea/**/dataSources.ids 58 | .idea/**/dataSources.xml 59 | .idea/**/dataSources.local.xml 60 | .idea/**/sqlDataSources.xml 61 | .idea/**/dynamic.xml 62 | .idea/**/uiDesigner.xml 63 | 64 | # Gradle: 65 | .idea/**/gradle.xml 66 | .idea/**/libraries 67 | 68 | # CMake 69 | cmake-build-debug/ 70 | 71 | # Mongo Explorer plugin: 72 | .idea/**/mongoSettings.xml 73 | 74 | ## File-based project format: 75 | *.iws 76 | 77 | ## Plugin-specific files: 78 | 79 | # IntelliJ 80 | /out/ 81 | 82 | # mpeltonen/sbt-idea plugin 83 | .idea_modules/ 84 | 85 | # JIRA plugin 86 | atlassian-ide-plugin.xml 87 | 88 | # Cursive Clojure plugin 89 | .idea/replstate.xml 90 | 91 | # Ruby plugin and RubyMine 92 | /.rakeTasks 93 | 94 | # Crashlytics plugin (for Android Studio and IntelliJ) 95 | com_crashlytics_export_strings.xml 96 | crashlytics.properties 97 | crashlytics-build.properties 98 | fabric.properties 99 | 100 | ### PyCharm Patch ### 101 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 102 | 103 | # *.iml 104 | # modules.xml 105 | # .idea/misc.xml 106 | # *.ipr 107 | 108 | # Sonarlint plugin 109 | .idea/sonarlint 110 | 111 | ### Python ### 112 | # Byte-compiled / optimized / DLL files 113 | __pycache__/ 114 | *.py[cod] 115 | *$py.class 116 | 117 | # C extensions 118 | *.so 119 | 120 | # Distribution / packaging 121 | .Python 122 | build/ 123 | develop-eggs/ 124 | dist/ 125 | downloads/ 126 | eggs/ 127 | .eggs/ 128 | lib/ 129 | lib64/ 130 | parts/ 131 | sdist/ 132 | var/ 133 | wheels/ 134 | *.egg-info/ 135 | .installed.cfg 136 | *.egg 137 | 138 | # PyInstaller 139 | # Usually these files are written by a python script from a template 140 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 141 | *.manifest 142 | *.spec 143 | 144 | # Installer logs 145 | pip-log.txt 146 | pip-delete-this-directory.txt 147 | 148 | # Unit test / coverage reports 149 | htmlcov/ 150 | .tox/ 151 | .coverage 152 | .coverage.* 153 | .cache 154 | .pytest_cache/ 155 | nosetests.xml 156 | coverage.xml 157 | *.cover 158 | .hypothesis/ 159 | 160 | # Translations 161 | *.mo 162 | *.pot 163 | 164 | # Flask stuff: 165 | instance/ 166 | .webassets-cache 167 | 168 | # Scrapy stuff: 169 | .scrapy 170 | 171 | # Sphinx documentation 172 | docs/_build/ 173 | 174 | # PyBuilder 175 | target/ 176 | 177 | # Jupyter Notebook 178 | .ipynb_checkpoints 179 | 180 | # pyenv 181 | .python-version 182 | 183 | # celery beat schedule file 184 | celerybeat-schedule.* 185 | 186 | # SageMath parsed files 187 | *.sage.py 188 | 189 | # Environments 190 | .env 191 | .venv 192 | env/ 193 | venv/ 194 | ENV/ 195 | env.bak/ 196 | venv.bak/ 197 | 198 | # Spyder project settings 199 | .spyderproject 200 | .spyproject 201 | 202 | # Rope project settings 203 | .ropeproject 204 | 205 | # mkdocs documentation 206 | /site 207 | 208 | # mypy 209 | .mypy_cache/ 210 | 211 | ### VisualStudioCode ### 212 | .vscode/* 213 | !.vscode/settings.json 214 | !.vscode/tasks.json 215 | !.vscode/launch.json 216 | !.vscode/extensions.json 217 | .history 218 | 219 | ### Windows ### 220 | # Windows thumbnail cache files 221 | Thumbs.db 222 | ehthumbs.db 223 | ehthumbs_vista.db 224 | 225 | # Folder config file 226 | Desktop.ini 227 | 228 | # Recycle Bin used on file shares 229 | $RECYCLE.BIN/ 230 | 231 | # Windows Installer files 232 | *.cab 233 | *.msi 234 | *.msm 235 | *.msp 236 | 237 | # Windows shortcuts 238 | *.lnk 239 | 240 | # Build folder 241 | 242 | */build/* 243 | 244 | # End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode 245 | 246 | .aws-sam/ 247 | .idealambdas/db_schema/template-g.yaml 248 | .idea/ 249 | samconfig.toml 250 | 251 | -------------------------------------------------------------------------------- /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 | 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 | 2 | # Intro 3 | 4 | This project is companion source code to the AWS Database blog post: [Use Python SQLAlchemy ORM to interact with an Amazon Aurora database from a serverless application](https://aws.amazon.com/blogs/database/use-python-sqlalchemy-orm-to-interact-with-an-amazon-aurora-database-from-a-serverless-application/). Please read the blog post for details. 5 | 6 | In the diagram below, we depict the AWS architecture discussed in the blog as a reference. The various components depicted in the architecture can be deployed using the code in this repository. 7 | 8 | ![Alt text](docs/blog-sql-alchemy-solution-diagram.png?raw=true "Python SQLAlchemy in an AWS Bookstore Serverless Application") 9 | 10 | # Prerequisites 11 | 12 | In order to deploy the solution in this repository you'll need the following: 13 | 14 | * An AWS account 15 | * The latest version of the AWS Command Line Interface ([AWS CLI](https://aws.amazon.com/cli/)) configured and with permissions to deploy to the AWS account 16 | * The [AWS Serverless Model (SAM)](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) CLI 17 | * Python 3.8 18 | * Clone this repository into your local environment 19 | 20 | # Installing the required libraries in the Lambda Layer 21 | 22 | This project uses Lambda functions that depend on libraries deployed to a Lambda layer. So, the first step is to make sure these libraries are installed properly in the Lambda layer. 23 | 24 | Run the commands below in a sandbox environment similar to your Lambda function's environment. This is required as package `psycopg2-binary` is OS-dependent. If you prefer, you can use a Docker image for that (e.g., https://hub.docker.com/r/lambci/lambda/). 25 | 26 | ``` 27 | cd db_schema/db_schema_lambda_layer/ 28 | python -m pip install -r requirements.txt -t "python/" 29 | ``` 30 | 31 | # Deploying the solution 32 | Please check our [blog post](TODO) for details. 33 | 34 | ## Security 35 | 36 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 37 | 38 | ## License 39 | 40 | This library is licensed under the MIT-0 License. See the LICENSE file. 41 | 42 | -------------------------------------------------------------------------------- /bookstore/books/db_connection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | This script orchestrates the enablement and centralization of SecurityHub 18 | across an enterprise of AWS accounts. 19 | It takes in a list of AWS Account Numbers, iterates through each account and 20 | region to enable SecurityHub. 21 | It creates each account as a Member in the SecurityHub Master account. 22 | It invites and accepts the invite for each Member account. 23 | The Security Hub automation is based on the scripts published at 24 | https://github.com/awslabs/aws-securityhub-multiaccount-scripts 25 | """ 26 | 27 | from bookstore_utils import create_db_engine, create_db_session 28 | import boto3 29 | import json 30 | import os 31 | import logging 32 | 33 | logger = logging.getLogger(__name__) 34 | logger.setLevel(logging.ERROR) 35 | db_secret_name = os.getenv('AURORA_DB_SECRET_NAME') 36 | db_proxy_endpoint = os.getenv('AURORA_DB_PROXY_ENDPOINT') 37 | secrets_client = boto3.client('secretsmanager') 38 | 39 | # Get DB credentials from AWS Secrets Manager 40 | def get_db_secrets(): 41 | """ 42 | Return the secret string as a dictionary for secret name SECRET_NAME. 43 | """ 44 | secret_response = secrets_client.get_secret_value(SecretId=db_secret_name) 45 | secrets = json.loads(secret_response['SecretString']) 46 | return secrets 47 | 48 | # Get SQLAlchemy Session 49 | def get_db_session(): 50 | logger.info(f'Retrieving database access information from Secrets Manager\'s secret: "{db_secret_name}"') 51 | secrets = get_db_secrets() 52 | db_name = secrets['dbname'] 53 | db_conn_string = f"postgresql://{secrets['username']}:{secrets['password']}@{db_proxy_endpoint}:{secrets['port']}/{db_name}?sslmode=require" 54 | 55 | logger.info(f'Creating SQLAlchemy database engine for database: "{db_name}"') 56 | engine = create_db_engine(db_conn_string) 57 | session = create_db_session(engine) 58 | return session -------------------------------------------------------------------------------- /bookstore/books/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | This script orchestrates the enablement and centralization of SecurityHub 18 | across an enterprise of AWS accounts. 19 | It takes in a list of AWS Account Numbers, iterates through each account and 20 | region to enable SecurityHub. 21 | It creates each account as a Member in the SecurityHub Master account. 22 | It invites and accepts the invite for each Member account. 23 | The Security Hub automation is based on the scripts published at 24 | https://github.com/awslabs/aws-securityhub-multiaccount-scripts 25 | """ 26 | 27 | import json 28 | import logging 29 | import db_connection 30 | from bookstore_orm_objects import Book 31 | 32 | logger = logging.getLogger(__name__) 33 | logger.setLevel(logging.ERROR) 34 | 35 | session = db_connection.get_db_session() 36 | 37 | def get_books(): 38 | try: 39 | books = session.query(Book) 40 | results = [ 41 | { "id": book.id, 42 | "title": book.title, 43 | "author": book.author, 44 | "publisher": book.publisher, 45 | "year": book.year } 46 | for book in books 47 | ] 48 | 49 | return { 50 | "statusCode": 200, 51 | "body": json.dumps({ 52 | "books": results 53 | }) 54 | } 55 | except Exception as e: 56 | logger.debug(e) 57 | return { 58 | "statusCode": 500, 59 | "error": f"Error: {e}" 60 | } 61 | 62 | 63 | def put_book(event): 64 | try: 65 | req = json.loads(event['body']) 66 | book = Book( 67 | title=req['title'], 68 | author=req['author'], 69 | publisher=req['publisher'], 70 | year=int(req['year']) 71 | ) 72 | logger.debug(f"Inserting: {book}") 73 | session.add(book) 74 | session.commit() 75 | 76 | return { 77 | "statusCode": 200 78 | } 79 | except Exception as e: 80 | logger.debug(e) 81 | return { 82 | "statusCode": 500, 83 | "error": f"Error: {e}" 84 | } 85 | 86 | def lambda_handler(event, context): 87 | 88 | logger.debug(event) 89 | 90 | if (event['httpMethod'] == 'PUT'): 91 | response = put_book(event) 92 | elif (event['httpMethod'] == 'GET'): 93 | response = get_books(); 94 | else: 95 | logger.debug(f"No handler for http verb: {event['httpMethod']}") 96 | raise Exception(f"No handler for http verb: {event['httpMethod']}") 97 | 98 | return response -------------------------------------------------------------------------------- /bookstore/books/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-serverless-app-with-aurora-and-python-sql-alchemy-example/fc6807f820da78816e28f2057a3a499b9370601a/bookstore/books/requirements.txt -------------------------------------------------------------------------------- /bookstore/reviews/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | This script orchestrates the enablement and centralization of SecurityHub 18 | across an enterprise of AWS accounts. 19 | It takes in a list of AWS Account Numbers, iterates through each account and 20 | region to enable SecurityHub. 21 | It creates each account as a Member in the SecurityHub Master account. 22 | It invites and accepts the invite for each Member account. 23 | The Security Hub automation is based on the scripts published at 24 | https://github.com/awslabs/aws-securityhub-multiaccount-scripts 25 | """ 26 | 27 | import json 28 | 29 | def lambda_handler(event, context): 30 | """ 31 | Reviews Lambda function entry point 32 | """ 33 | 34 | # try: 35 | # ip = requests.get("http://checkip.amazonaws.com/") 36 | # except requests.RequestException as e: 37 | # # Send some context about this error to Lambda Logs 38 | # print(e) 39 | 40 | # raise e 41 | 42 | return { 43 | "statusCode": 200, 44 | "body": json.dumps({ 45 | "message": "Hello from Reviews Lambda", 46 | # "location": ip.text.replace("\n", "") 47 | }), 48 | } 49 | -------------------------------------------------------------------------------- /bookstore/reviews/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /bookstore/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: SAM Template for Simple Online Bookstore API 4 | 5 | Parameters: 6 | 7 | AuroraVPCStackName: 8 | Description: Name of the Aurora Database VPC Cloudformation stack 9 | Type: String 10 | AuroraDBSecretName: 11 | Description: Name of the Secrets Manager secret which containers the Aurora Database details 12 | Type: String 13 | AuroraRDSProxyStackName: 14 | Description: Name of the RDS Proxy Cloudformation stack 15 | Type: String 16 | SimpleBookstoreDBSchemaStackName: 17 | Description: Name of the Simple Bookstore DB schema Cloudformation stack 18 | Type: String 19 | 20 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 21 | Globals: 22 | Function: 23 | Timeout: 30 24 | 25 | Resources: 26 | BooksLambdaFunction: 27 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 28 | Properties: 29 | CodeUri: books/ 30 | Handler: main.lambda_handler 31 | Runtime: python3.8 32 | Layers: 33 | - {'Fn::ImportValue': !Sub '${SimpleBookstoreDBSchemaStackName}-DBSchemaLambdaLayerArn'} 34 | Role: !GetAtt LambdaRole.Arn 35 | VpcConfig: 36 | SecurityGroupIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SecretRotationLambdaSecurityGroup'}] 37 | SubnetIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SubnetsPrivate'}] 38 | Environment: 39 | Variables: 40 | AURORA_DB_SECRET_NAME: !Ref AuroraDBSecretName 41 | AURORA_DB_PROXY_ENDPOINT: 42 | Fn::ImportValue: 43 | !Sub '${AuroraRDSProxyStackName}-SOBRDSProxyEndpoint' 44 | Events: 45 | ListBooks: 46 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 47 | Properties: 48 | Path: /book 49 | Method: get 50 | AddBook: 51 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 52 | Properties: 53 | Path: /book 54 | Method: put 55 | ReviewsLambdaFunction: 56 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 57 | Properties: 58 | CodeUri: reviews/ 59 | Handler: main.lambda_handler 60 | Runtime: python3.8 61 | Layers: 62 | - {'Fn::ImportValue': !Sub '${SimpleBookstoreDBSchemaStackName}-DBSchemaLambdaLayerArn'} 63 | Role: !GetAtt LambdaRole.Arn 64 | VpcConfig: 65 | SecurityGroupIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SecretRotationLambdaSecurityGroup'}] 66 | SubnetIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SubnetsPrivate'}] 67 | Environment: 68 | Variables: 69 | AURORA_DB_SECRET_NAME: !Ref AuroraDBSecretName 70 | AURORA_DB_PROXY_ENDPOINT: 71 | Fn::ImportValue: 72 | !Sub '${AuroraRDSProxyStackName}-SOBRDSProxyEndpoint' 73 | Events: 74 | ListReviews: 75 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 76 | Properties: 77 | Path: /review 78 | Method: get 79 | AddBook: 80 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 81 | Properties: 82 | Path: /review 83 | Method: put 84 | 85 | # Lambda function role 86 | LambdaRole: 87 | Type: AWS::IAM::Role 88 | Properties: 89 | AssumeRolePolicyDocument: 90 | Version: 2012-10-17 91 | Statement: 92 | - Effect: Allow 93 | Principal: 94 | Service: 95 | - lambda.amazonaws.com 96 | Action: sts:AssumeRole 97 | Path: / 98 | Policies: 99 | - PolicyName: create-schema-lambda-policy 100 | PolicyDocument: 101 | Version: 2012-10-17 102 | Statement: 103 | - Effect: Allow 104 | Action: 105 | - secretsmanager:GetSecretValue 106 | - secretsmanager:PutResourcePolicy 107 | - secretsmanager:PutSecretValue 108 | - secretsmanager:DeleteSecret 109 | - secretsmanager:DescribeSecret 110 | - secretsmanager:TagResource 111 | - secretsmanager:CreateSecret 112 | - secretsmanager:ListSecrets 113 | - secretsmanager:GetRandomPassword 114 | Resource: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${AuroraDBSecretName}*" 115 | ManagedPolicyArns: 116 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole 117 | 118 | Outputs: 119 | 120 | BooksLambdaFunction: 121 | Description: Simple Online Bookstore database schema Lambda Arn 122 | Value: !Ref BooksLambdaFunction 123 | Export: 124 | Name: !Sub '${AWS::StackName}-BooksLambdaArn' 125 | 126 | ReviewsLambdaFunction: 127 | Description: Simple Online Bookstore database schema Lambda Arn 128 | Value: !Ref ReviewsLambdaFunction 129 | Export: 130 | Name: !Sub '${AWS::StackName}-ReviewsLambdaArn' 131 | 132 | BookstoreAPIEndpoint: 133 | Description: Simple Online Bookstore API Endpoint 134 | # ServerlessRestApi and ServerlessRestApiProdStage are implicit resources created by SAM and referenced here 135 | Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/${ServerlessRestApiProdStage}" 136 | Export: 137 | Name: !Sub '${AWS::StackName}-BookstoreAPIEndpoint' 138 | -------------------------------------------------------------------------------- /db_schema/.gitignore: -------------------------------------------------------------------------------- 1 | *.toml 2 | 3 | -------------------------------------------------------------------------------- /db_schema/db_schema_lambda/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | This script orchestrates the enablement and centralization of SecurityHub 18 | across an enterprise of AWS accounts. 19 | It takes in a list of AWS Account Numbers, iterates through each account and 20 | region to enable SecurityHub. 21 | It creates each account as a Member in the SecurityHub Master account. 22 | It invites and accepts the invite for each Member account. 23 | The Security Hub automation is based on the scripts published at 24 | https://github.com/awslabs/aws-securityhub-multiaccount-scripts 25 | """ 26 | 27 | import json 28 | import os 29 | import logging 30 | 31 | import boto3 32 | from bookstore_orm_objects import Base 33 | from bookstore_utils import create_db_engine 34 | from crhelper import CfnResource 35 | 36 | logger = logging.getLogger(__name__) 37 | cfn_helper = CfnResource(json_logging=False, log_level="DEBUG", boto_level="CRITICAL") 38 | db_secret_name = os.getenv('AURORA_DB_SECRET_NAME') 39 | db_proxy_endpoint = os.getenv('AURORA_DB_PROXY_ENDPOINT') 40 | secrets_client = boto3.client('secretsmanager') 41 | 42 | # Get DB credentials from AWS Secrets Manager 43 | def get_db_secrets(): 44 | """ 45 | Return the secret string as a dictionary for secret name SECRET_NAME. 46 | """ 47 | secret_response = secrets_client.get_secret_value(SecretId=db_secret_name) 48 | secrets = json.loads(secret_response['SecretString']) 49 | return secrets 50 | 51 | @cfn_helper.create 52 | @cfn_helper.update 53 | def create(event, context): 54 | """ 55 | DB Schema creation/update Lambda function entry point 56 | """ 57 | global resource_id 58 | 59 | logger.info(f'Retrieving database access information from Secrets Manager\'s secret: "{db_secret_name}"') 60 | secrets = get_db_secrets() 61 | db_name = secrets['dbname'] 62 | db_conn_string = f"postgresql://{secrets['username']}:{secrets['password']}@{db_proxy_endpoint}:{secrets['port']}/{db_name}?sslmode=require" 63 | 64 | logger.info(f'Creating SQLAlchemy database engine for database: "{db_name}"') 65 | engine = create_db_engine(db_conn_string) 66 | 67 | logger.info(f'Creating or Updating DB schema for database: "{db_name}"') 68 | Base.metadata.create_all(engine) 69 | 70 | return f'{db_name}-schema' 71 | 72 | @cfn_helper.delete 73 | def no_op(event, context): 74 | """ 75 | Needed for the custom resource to delete properly. 76 | """ 77 | logger.info('No action required when deleting this resource') 78 | 79 | def lambda_handler(event, context): 80 | """ 81 | Lambda entry point. 82 | """ 83 | cfn_helper(event, context) 84 | -------------------------------------------------------------------------------- /db_schema/db_schema_lambda/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-serverless-app-with-aurora-and-python-sql-alchemy-example/fc6807f820da78816e28f2057a3a499b9370601a/db_schema/db_schema_lambda/requirements.txt -------------------------------------------------------------------------------- /db_schema/db_schema_lambda_layer/Makefile: -------------------------------------------------------------------------------- 1 | build-SimpleBookStoreDBSchemaLambdaLayer: 2 | mkdir -p "$(ARTIFACTS_DIR)/python" 3 | cp *.py "$(ARTIFACTS_DIR)/python" 4 | pip install -r requirements.txt -t "$(ARTIFACTS_DIR)/python" -------------------------------------------------------------------------------- /db_schema/db_schema_lambda_layer/bookstore_orm_objects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | This script orchestrates the enablement and centralization of SecurityHub 18 | across an enterprise of AWS accounts. 19 | It takes in a list of AWS Account Numbers, iterates through each account and 20 | region to enable SecurityHub. 21 | It creates each account as a Member in the SecurityHub Master account. 22 | It invites and accepts the invite for each Member account. 23 | The Security Hub automation is based on the scripts published at 24 | https://github.com/awslabs/aws-securityhub-multiaccount-scripts 25 | """ 26 | 27 | from sqlalchemy import Column, Integer, String, SmallInteger 28 | from sqlalchemy import ForeignKey 29 | from sqlalchemy.ext.declarative import declarative_base 30 | from sqlalchemy.orm import relationship 31 | 32 | Base = declarative_base() 33 | 34 | 35 | def create_db_schema(engine): 36 | Base.metadata.create_all(engine) 37 | 38 | 39 | class Book(Base): 40 | """ 41 | Our Book class 42 | """ 43 | __tablename__ = 'book' 44 | 45 | id = Column(Integer, primary_key=True) 46 | title = Column(String(200), nullable=False) 47 | author = Column(String(100), nullable=False) 48 | publisher = Column(String(100), nullable=False) 49 | year = Column(Integer, nullable=False) 50 | 51 | def __repr__(self): 52 | return "" % ( 53 | self.title, self.author, self.publisher, self.year) 54 | 55 | def __init__(self, title, author, publisher, year): 56 | self.title = title 57 | self.author = author 58 | self.publisher = publisher 59 | self.year = year 60 | 61 | class Review(Base): 62 | """ 63 | Our Review class 64 | """ 65 | __tablename__ = 'review' 66 | id = Column(Integer, primary_key=True) 67 | reviewer = Column(String(100), nullable=False) 68 | rate = Column(SmallInteger, nullable=False) # 1-5 stars 69 | review = Column(String(500), nullable=True) 70 | book_id = Column(Integer, ForeignKey('book.id')) 71 | book = relationship("Book", back_populates="reviews") 72 | 73 | def __repr__(self): 74 | return "" % ( 75 | self.reviewer, self.rate, self.review, self.book_id) 76 | 77 | 78 | # 1 Book <-> 0 or more Reviews 79 | Book.reviews = relationship("Review", order_by=Review.id, back_populates="book") 80 | -------------------------------------------------------------------------------- /db_schema/db_schema_lambda_layer/bookstore_utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | SPDX-License-Identifier: MIT-0 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 13 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 14 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 15 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 16 | THE SOFTWARE. 17 | This script creates the connection to the database using sqlalchemy. 18 | """ 19 | 20 | from sqlalchemy import create_engine 21 | from sqlalchemy.orm import sessionmaker 22 | 23 | Session = None 24 | 25 | 26 | def create_db_engine(db_conn_string, debug_mode=False): 27 | return create_engine(db_conn_string, 28 | echo=debug_mode, 29 | pool_size=1, 30 | max_overflow=0, 31 | pool_recycle=3600, 32 | pool_pre_ping=True, 33 | pool_use_lifo=True) 34 | 35 | 36 | def create_db_session(engine): 37 | global Session 38 | if not Session: 39 | Session = sessionmaker(bind=engine) 40 | # todo: setup connection pooling properties 41 | return Session() 42 | -------------------------------------------------------------------------------- /db_schema/db_schema_lambda_layer/requirements.txt: -------------------------------------------------------------------------------- 1 | boto3 2 | psycopg2-binary 3 | sqlalchemy 4 | crhelper 5 | aws-psycopg2 -------------------------------------------------------------------------------- /db_schema/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Create the database schema for the Simple Online Bookstore via a schema Lambda and Lambda Layer 4 | 5 | Parameters: 6 | 7 | AuroraVPCStackName: 8 | Description: Name of the Aurora Database VPC Cloudformation stack 9 | Type: String 10 | AuroraDBSecretName: 11 | Description: Name of the Aurora Database secret 12 | Type: String 13 | AuroraRDSProxyStackName: 14 | Description: Name of the RDS Proxy Cloudformation stack 15 | Type: String 16 | 17 | Resources: 18 | 19 | # Triggers DB schema creation 20 | SimpleBookStoreDBSchemaLambdaFunctionTrigger: 21 | Type: Custom::SimpleBookStoreDBSchemaLambdaFunction 22 | Version: "1.0" 23 | Properties: 24 | ServiceToken: !GetAtt SimpleBookStoreDBSchemaLambdaFunction.Arn 25 | 26 | # Lambda function that will create the DB schema from SQLAlchemy models defined in the Lambda Layer 27 | SimpleBookStoreDBSchemaLambdaFunction: 28 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 29 | Properties: 30 | FunctionName: sob_schema_creation_lambda 31 | CodeUri: ./db_schema_lambda/ 32 | Handler: main.lambda_handler 33 | Runtime: python3.8 34 | Timeout: 30 35 | Layers: 36 | - !Ref SimpleBookStoreDBSchemaLambdaLayer 37 | Role: !GetAtt SimpleBookStoreDBSchemaLambdaFunctionRole.Arn 38 | VpcConfig: 39 | SecurityGroupIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SecretRotationLambdaSecurityGroup'}] 40 | SubnetIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SubnetsPrivate'}] 41 | Environment: 42 | Variables: 43 | AURORA_DB_SECRET_NAME: !Ref AuroraDBSecretName 44 | AURORA_DB_PROXY_ENDPOINT: 45 | Fn::ImportValue: 46 | !Sub '${AuroraRDSProxyStackName}-SOBRDSProxyEndpoint' 47 | 48 | 49 | # Lambda function role 50 | SimpleBookStoreDBSchemaLambdaFunctionRole: 51 | Type: AWS::IAM::Role 52 | Properties: 53 | AssumeRolePolicyDocument: 54 | Version: 2012-10-17 55 | Statement: 56 | - Effect: Allow 57 | Principal: 58 | Service: 59 | - lambda.amazonaws.com 60 | Action: sts:AssumeRole 61 | Path: / 62 | Policies: 63 | - PolicyName: create-schema-lambda-policy 64 | PolicyDocument: 65 | Version: 2012-10-17 66 | Statement: 67 | - Effect: Allow 68 | Action: 69 | - secretsmanager:GetSecretValue 70 | - secretsmanager:PutResourcePolicy 71 | - secretsmanager:PutSecretValue 72 | - secretsmanager:DeleteSecret 73 | - secretsmanager:DescribeSecret 74 | - secretsmanager:TagResource 75 | - secretsmanager:CreateSecret 76 | - secretsmanager:ListSecrets 77 | - secretsmanager:GetRandomPassword 78 | Resource: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:${AuroraDBSecretName}*" 79 | - Effect: Allow 80 | Action: 81 | - rds-db:connect 82 | Resource: 83 | Fn::ImportValue: 84 | !Sub '${AuroraRDSProxyStackName}-SOBRDSProxyArn' 85 | ManagedPolicyArns: 86 | - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole 87 | 88 | # Lambda layer that contains the SQLAlchemy ORM objects that will be used to create the schema and access the DB 89 | SimpleBookStoreDBSchemaLambdaLayer: 90 | Type: AWS::Serverless::LayerVersion 91 | Properties: 92 | LayerName: sqlalchemy-models-lambda-layer 93 | Description: SQLAlchemy models and utilities for the Simple Online Boostore 94 | ContentUri: ./db_schema_lambda_layer/ 95 | CompatibleRuntimes: 96 | - python3.8 97 | LicenseInfo: 'MIT' 98 | RetentionPolicy: Retain 99 | Metadata: 100 | BuildMethod: makefile 101 | 102 | Outputs: 103 | 104 | SimpleOnlineBookstoreDBSchemaLambdaArn: 105 | Description: Simple Online Bookstore database schema Lambda Arn 106 | Value: !Ref SimpleBookStoreDBSchemaLambdaFunction 107 | Export: 108 | Name: !Sub '${AWS::StackName}-DBSchemaLambdaArn' 109 | 110 | SimpleOnlineBookstoreDBSchemaLambdaLayer: 111 | Description: Simple Online Bookstore database schema Lambda layer Arn 112 | Value: !Ref SimpleBookStoreDBSchemaLambdaLayer 113 | Export: 114 | Name: !Sub '${AWS::StackName}-DBSchemaLambdaLayerArn' 115 | -------------------------------------------------------------------------------- /docs/blog-sql-alchemy-solution-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-serverless-app-with-aurora-and-python-sql-alchemy-example/fc6807f820da78816e28f2057a3a499b9370601a/docs/blog-sql-alchemy-solution-diagram.png -------------------------------------------------------------------------------- /rds_proxy/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: Create an IAM Role for the RDS Proxy to access the Simple Book Store Aurora Postgres Database 3 | 4 | Parameters: 5 | AuroraVPCStackName: 6 | Description: Name of the Aurora Database VPC Cloudformation stack 7 | Type: String 8 | ProxyName: 9 | Description: Name of the RDS proxy 10 | Type: String 11 | Default: SOBRDSProxy 12 | AuroraDBClusterId: 13 | Description: Cluster ID of the Aurora Database 14 | Type: String 15 | AuroraDBSecretArn: 16 | Description: ARN of the Aurora Database Secret 17 | Type: String 18 | 19 | Resources: 20 | SOBRDSProxy: 21 | Type: AWS::RDS::DBProxy 22 | Properties: 23 | DBProxyName: !Ref ProxyName 24 | EngineFamily: POSTGRESQL 25 | RequireTLS: true 26 | RoleArn: 27 | !GetAtt SOBRDSProxyIamRole.Arn 28 | Auth: 29 | - {AuthScheme: SECRETS, SecretArn: !Ref AuroraDBSecretArn, IAMAuth: DISABLED, Description: sob-db-proxy-auth} 30 | VpcSecurityGroupIds: [{'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SecretRotationLambdaSecurityGroup'}] 31 | VpcSubnetIds: !Split [',', {'Fn::ImportValue': !Sub '${AuroraVPCStackName}-SubnetsPrivate'}] 32 | 33 | SOBRDSProxyIamRole: 34 | Type: AWS::IAM::Role 35 | Properties: 36 | AssumeRolePolicyDocument: 37 | Version: 2012-10-17 38 | Statement: 39 | - Effect: Allow 40 | Principal: 41 | Service: 42 | - rds.amazonaws.com 43 | Action: sts:AssumeRole 44 | Path: / 45 | Policies: 46 | - PolicyName: rds-proxy-policy 47 | PolicyDocument: 48 | Version: 2012-10-17 49 | Statement: 50 | - Effect: Allow 51 | Action: 52 | - secretsmanager:GetSecretValue 53 | - secretsmanager:PutResourcePolicy 54 | - secretsmanager:PutSecretValue 55 | - secretsmanager:DeleteSecret 56 | - secretsmanager:DescribeSecret 57 | - secretsmanager:TagResource 58 | - secretsmanager:CreateSecret 59 | - secretsmanager:ListSecrets 60 | - secretsmanager:GetRandomPassword 61 | Resource: !Ref AuroraDBSecretArn 62 | 63 | SOBRDSProxyTargetGroup: 64 | Type: AWS::RDS::DBProxyTargetGroup 65 | Properties: 66 | DBClusterIdentifiers: 67 | - !Ref AuroraDBClusterId 68 | DBProxyName: !Ref SOBRDSProxy 69 | TargetGroupName: default 70 | 71 | 72 | Outputs: 73 | SimpleOnlineBookstoreRDSProxyRole: 74 | Description: Simple Online Bookstore RDS Proxy IAM Role 75 | Value: !Ref SOBRDSProxyIamRole 76 | Export: 77 | Name: !Sub '${AWS::StackName}-SOBRDSProxyIamRole' 78 | 79 | SimpleOnlineBookstoreRDSProxyEndpoint: 80 | Description: Simple Online Bookstore RDS Proxy Endpoint 81 | Value: !GetAtt SOBRDSProxy.Endpoint 82 | Export: 83 | Name: !Sub '${AWS::StackName}-SOBRDSProxyEndpoint' 84 | 85 | SimpleOnlineBookstoreRDSProxyArn: 86 | Description: Simple Online Bookstore RDS Proxy Arn 87 | Value: !GetAtt SOBRDSProxy.DBProxyArn 88 | Export: 89 | Name: !Sub '${AWS::StackName}-SOBRDSProxyArn' 90 | 91 | --------------------------------------------------------------------------------