├── .coveragerc ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── feature_request.md │ └── i-need-help.md ├── pull_request_template.md └── workflows │ └── main.yml ├── .gitignore ├── AUTHORS.rst ├── CODE_OF_CONDUCT.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── NOTICE ├── README.rst ├── boto_session_manager ├── __init__.py ├── _version.py ├── clients.py ├── docs │ └── __init__.py ├── exc.py ├── manager.py ├── sentinel.py └── services.py ├── codecov.yml ├── debug ├── .gitignore ├── clients.jinja2 ├── crawl_service_id_list.py ├── manager.py ├── services.jinja2 └── spec-file.json ├── docs ├── Makefile ├── make.bat └── source │ ├── _static │ ├── .custom-style.rst │ ├── boto_session_manager-favicon.ico │ ├── boto_session_manager-logo.png │ ├── css │ │ └── custom-style.css │ └── js │ │ └── sorttable.js │ ├── conf.py │ ├── index.rst │ └── release-history.rst ├── examples ├── auto_complete.py └── auto_refreshable_assumed_role.py ├── images ├── auto-complete.gif └── doc-link.gif ├── pygitrepo-config.json ├── readthedocs.yml ├── release-history.rst ├── requirements-dev.txt ├── requirements-doc.txt ├── requirements-test.txt ├── requirements.txt ├── setup.py └── tests ├── README.rst ├── all.py ├── test_import.py └── test_manager.py /.coveragerc: -------------------------------------------------------------------------------- 1 | # Coverage.py is a tool for measuring code coverage of Python programs. 2 | # for more info: https://coverage.readthedocs.io/en/latest/config.html 3 | [run] 4 | omit = 5 | boto_session_manager/_version.py 6 | boto_session_manager/cli.py 7 | boto_session_manager/clients.py 8 | boto_session_manager/sentinel.py 9 | boto_session_manager/docs/* 10 | boto_session_manager/tests/* 11 | 12 | [report] 13 | # Regexes for lines to exclude from consideration 14 | exclude_lines = 15 | # Have to re-enable the standard pragma 16 | pragma: no cover 17 | 18 | # Don't complain about missing debug-only code: 19 | def __repr__ 20 | if self\.debug 21 | 22 | # Don't complain if tests don't hit defensive assertion code: 23 | raise AssertionError 24 | raise NotImplementedError 25 | 26 | # Don't complain if non-runnable code isn't run: 27 | if 0: 28 | if __name__ == .__main__.: 29 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | *.py @MacHu-GWU 2 | /boto_session_manager/ @MacHu-GWU 3 | /tests/ @MacHu-GWU 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Report bugs to the author or the community, we appreciate your contribution! 4 | title: "[Bug]: describe the bug here" 5 | labels: bug 6 | assignees: MacHu-GWU 7 | 8 | --- 9 | 10 | ## Describe the Bug 11 | 12 | **What is the expected behavior** 13 | 14 | ... 15 | 16 | **What is the actual behavior** 17 | 18 | ... 19 | 20 | ## How to Reproduce this Bug 21 | 22 | **Code to reproduce the bug**: 23 | 24 | ```python 25 | import something 26 | ``` 27 | 28 | **Describe your Environment**: 29 | 30 | - Operation System: Windows / MacOS / Linux 31 | - What Python: CPython / Conda / PyPI 32 | - Python version: 3.7 / 3.8 / 3.9 / 3.10 33 | - Network environment: home / work with VPN / cloud 34 | 35 | ## Any Additional Information 36 | 37 | [add any additional information here] 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature] describe the feature you want here" 5 | labels: feature 6 | assignees: MacHu-GWU 7 | 8 | --- 9 | 10 | ## Describe the feature you want 11 | 12 | ## Why you believe it benefits the users 13 | 14 | [describe your solid reason here] 15 | 16 | ## Any Additional Information 17 | 18 | [add any additional information here] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/i-need-help.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: I need help 3 | about: Ask the author or the community for help! 4 | title: "[Help]: describe your question here" 5 | labels: question 6 | assignees: MacHu-GWU 7 | 8 | --- 9 | 10 | ## My Objective 11 | 12 | [describe your objective here] I am trying to do XYZ 13 | 14 | ## My Existing Work 15 | 16 | [you may have tried some codes, describe it here, tell me what is your expected output / behavior] 17 | 18 | I am expecting this code: 19 | 20 | ```python 21 | name = "Alice" 22 | print(f"Hello {name}") 23 | ``` 24 | 25 | gives me this output: 26 | 27 | ```python 28 | Hello Alice 29 | ``` 30 | 31 | However it raise an error: 32 | 33 | ```python 34 | ValueError 35 | ``` 36 | 37 | ## Any Additional Information 38 | 39 | [add any additional information here] 40 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Title 2 | 3 | Please **pick a concise, informative and complete title** for your PR. 4 | 5 | ## Motivation 6 | 7 | Please explain the motivation behind this PR in the description. 8 | 9 | ## Issue #, if available 10 | 11 | If you're adding a new feature, then consider opening an issue and discussing it with the maintainers (GitHub Username ``MacHu-GWU``) before you actually do the hard work. 12 | 13 | ## Description of changes: 14 | 15 | Please explain the changes you have made here. 16 | 17 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 18 | 19 | ## Tests 20 | 21 | If you're fixing a bug, consider [test-driven development](https://en.wikipedia.org/wiki/Test-driven_development): 22 | 23 | 1. Create a unit test that demonstrates the bug. The test should **fail**. 24 | 2. Implement your bug fix. 25 | 3. The test you created should now **pass**. 26 | 27 | If you're implementing a new feature, include unit tests for it. 28 | 29 | Make sure all existing unit tests pass. 30 | 31 | ## Work in progress 32 | 33 | If you're still working on your PR, include "WIP" in the title. 34 | We'll skip reviewing it for the time being. 35 | Once you're ready to review, remove the "WIP" from the title, and ping one of the maintainers (e.g. mpenkov). 36 | 37 | ## Checklist 38 | 39 | Before you create the PR, please make sure you have: 40 | 41 | - [ ] Picked a concise, informative and complete title 42 | - [ ] Clearly explained the motivation behind the PR 43 | - [ ] Linked to any existing issues that your PR will be solving 44 | - [ ] Included tests for any new functionality 45 | - [ ] Checked that all unit tests pass 46 | 47 | ## Workflow 48 | 49 | Please avoid rebasing and force-pushing to the branch of the PR once a review is in progress. 50 | Rebasing can make your commits look a bit cleaner, but it also makes life more difficult from the reviewer, because they are no longer able to distinguish between code that has already been reviewed, and unreviewed code. 51 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # comprehensive github action yml reference: https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions 2 | 3 | --- 4 | name: CI 5 | 6 | on: 7 | push: # any push event to master will trigger this 8 | branches: ["main"] 9 | pull_request: # any pull request to master will trigger this 10 | branches: ["main"] 11 | workflow_dispatch: # allows you to manually trigger run 12 | 13 | jobs: 14 | tests: 15 | name: "${{ matrix.os }} Python ${{ matrix.python-version }}" 16 | runs-on: "${{ matrix.os }}" # for all available VM runtime, see this: https://docs.github.com/en/free-pro-team@latest/actions/reference/specifications-for-github-hosted-runners 17 | env: # define environment variables 18 | USING_COVERAGE: "3.7,3.8,3.9,3.10" 19 | strategy: 20 | matrix: 21 | os: ["ubuntu-latest", "windows-latest"] 22 | python-version: ["3.7", "3.8", "3.9", "3.10"] 23 | # os: ["ubuntu-latest", ] # for debug only 24 | # python-version: ["3.6", ] # for debug only 25 | steps: 26 | - uses: "actions/checkout@v3" # https://github.com/marketplace/actions/checkout 27 | - uses: "actions/setup-python@v3" # https://github.com/marketplace/actions/setup-python 28 | with: 29 | python-version: "${{ matrix.python-version }}" 30 | - name: "Install dependencies on MacOS or Linux" 31 | if: matrix.os == 'ubuntu-latest' # for condition steps, you should put if at begin, and use single quote for logical expression 32 | run: | 33 | set -xe 34 | python -VV 35 | python -m site 36 | python -m pip install --upgrade pip setuptools wheel virtualenv 37 | pip install -r requirements.txt 38 | pip install -r requirements-test.txt 39 | pip install . 40 | - name: "Install dependencies on Windows" 41 | if: matrix.os == 'windows-latest' 42 | run: | 43 | python -m site 44 | python -m pip install --upgrade pip setuptools wheel virtualenv 45 | pip install -r requirements.txt 46 | pip install -r requirements-test.txt 47 | pip install . 48 | - name: "Run pytest" 49 | env: 50 | AWS_ACCESS_KEY_ID_FOR_GITHUB_CI: ${{ secrets.AWS_ACCESS_KEY_ID_FOR_GITHUB_CI }} 51 | AWS_SECRET_ACCESS_KEY_FOR_GITHUB_CI: ${{ secrets.AWS_SECRET_ACCESS_KEY_FOR_GITHUB_CI }} 52 | run: "python -m pytest tests --cov=boto_session_manager" 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User Settings 2 | boto_session_manager_venv/ 3 | boto_session_manager.egg-info/ 4 | boto_session_manager-*/ 5 | tmp/ 6 | 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | env/ 19 | build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | 34 | # PyCharm 35 | .idea/ 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | .pytest_cache/ 54 | nosetests.xml 55 | coverage.xml 56 | *,cover 57 | .hypothesis/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | 67 | # Flask instance folder 68 | instance/ 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # IPython Notebook 80 | .ipynb_checkpoints 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # celery beat schedule file 86 | celerybeat-schedule 87 | 88 | # dotenv 89 | .env 90 | 91 | # virtualenv 92 | venv/ 93 | ENV/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # ========================= 102 | # Operating System Files 103 | # ========================= 104 | 105 | # OSX 106 | # ========================= 107 | 108 | .DS_Store 109 | .AppleDouble 110 | .LSOverride 111 | 112 | # Thumbnails 113 | ._* 114 | 115 | # Files that might appear in the root of a volume 116 | .DocumentRevisions-V100 117 | .fseventsd 118 | .Spotlight-V100 119 | .TemporaryItems 120 | .Trashes 121 | .VolumeIcon.icns 122 | 123 | # Directories potentially created on remote AFP share 124 | .AppleDB 125 | .AppleDesktop 126 | Network Trash Folder 127 | Temporary Items 128 | .apdisk 129 | 130 | # Windows 131 | # ========================= 132 | 133 | # Windows image file caches 134 | Thumbs.db 135 | ehthumbs.db 136 | 137 | # Folder config file 138 | Desktop.ini 139 | 140 | # Recycle Bin used on file shares 141 | $RECYCLE.BIN/ 142 | 143 | # Windows Installer files 144 | *.cab 145 | *.msi 146 | *.msm 147 | *.msp 148 | 149 | # Windows shortcuts 150 | *.lnk 151 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Authors 2 | ============================================================================== 3 | 4 | 5 | The Creator 6 | ------------------------------------------------------------------------------ 7 | - Sanhe Hu `@MacHu-GWU `_ 8 | 9 | 10 | Maintainer Team 11 | ------------------------------------------------------------------------------ 12 | - Sanhe Hu `@MacHu-GWU `_ 13 | 14 | 15 | Contributors 16 | ------------------------------------------------------------------------------ 17 | - Sanhe Hu `@MacHu-GWU `_ 18 | - Theofilos Papapanagiotou `@theofpa `_ 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.rst: -------------------------------------------------------------------------------- 1 | Code of Conduct 2 | ============================================================================== 3 | This project has adopted the `Amazon Open Source Code of Conduct `_. 4 | For more information see the `Code of Conduct FAQ `_ or contact opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | .. _contributing-guidelines: 2 | 3 | Contributing Guidelines 4 | ============================================================================== 5 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 6 | documentation, we greatly value feedback and contributions from our community. 7 | 8 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 9 | information to effectively respond to your bug report or contribution. 10 | 11 | 12 | Reporting Bugs/Feature Requests 13 | ------------------------------------------------------------------------------ 14 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 15 | 16 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 17 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 18 | 19 | - A reproducible test case or series of steps 20 | - The version of our code being used 21 | - Any modifications you've made relevant to the bug 22 | - Anything unusual about your environment or deployment 23 | 24 | 25 | Contributing via Pull Requests 26 | ------------------------------------------------------------------------------ 27 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 28 | 29 | 1. You are working against the latest source on the *main* branch. 30 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 31 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 32 | 33 | To send us a pull request, please: 34 | 35 | 1. Fork the repository. 36 | 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. 37 | 3. Ensure local tests pass. 38 | 4. Commit to your fork using clear commit messages. 39 | 5. Send us a pull request, answering any default questions in the pull request interface. 40 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 41 | 42 | GitHub provides additional document on `forking a repository `_ and 43 | `creating a pull request `_. 44 | 45 | 46 | Finding contributions to work on 47 | ------------------------------------------------------------------------------ 48 | 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. 49 | 50 | 51 | Code of Conduct 52 | ------------------------------------------------------------------------------ 53 | This project has adopted the `Amazon Open Source Code of Conduct `_. 54 | 55 | For more information see the `Code of Conduct FAQ `_ or contact opensource-codeofconduct@amazon.com with any additional questions or comments. 56 | 57 | 58 | Security issue notifications 59 | ------------------------------------------------------------------------------ 60 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our `vulnerability reporting page `_. Please do **not** create a public github issue. 61 | 62 | 63 | Licensing 64 | ------------------------------------------------------------------------------ 65 | See the `LICENSE <./LICENSE>`_ file for our project's licensing. We will ask you to confirm the licensing of your contribution. 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # A MANIFEST.in file can be added in a project to define the list of files 2 | # to include in the distribution built by the sdist command. 3 | # 4 | # For more info: https://docs.python.org/2/distutils/sourcedist.html#manifest-related-options 5 | 6 | include boto_session_manager 7 | include *.txt 8 | include *.rst 9 | exclude *.pyc 10 | exclude *.pyo 11 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | boto_session_manager 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://github.com/aws-samples/boto-session-manager-project/workflows/CI/badge.svg 2 | :target: https://github.com/aws-samples/boto-session-manager-project/actions?query=workflow:CI 3 | 4 | .. image:: https://img.shields.io/pypi/v/boto_session_manager.svg 5 | :target: https://pypi.python.org/pypi/boto_session_manager 6 | 7 | .. image:: https://img.shields.io/pypi/l/boto_session_manager.svg 8 | :target: https://pypi.python.org/pypi/boto_session_manager 9 | 10 | .. image:: https://img.shields.io/pypi/pyversions/boto_session_manager.svg 11 | :target: https://pypi.python.org/pypi/boto_session_manager 12 | 13 | .. image:: https://img.shields.io/pypi/dm/boto_session_manager.svg 14 | :target: https://pypi.python.org/pypi/boto_session_manager 15 | 16 | .. image:: https://img.shields.io/badge/STAR_Me_on_GitHub!--None.svg?style=social 17 | :target: https://github.com/aws-samples/boto-session-manager-project 18 | 19 | ------ 20 | 21 | .. image:: https://img.shields.io/badge/Link-Install-blue.svg 22 | :target: `install`_ 23 | 24 | .. image:: https://img.shields.io/badge/Link-GitHub-blue.svg 25 | :target: https://github.com/aws-samples/boto-session-manager-project 26 | 27 | .. image:: https://img.shields.io/badge/Link-Submit_Issue-blue.svg 28 | :target: https://github.com/aws-samples/boto-session-manager-project/issues 29 | 30 | .. image:: https://img.shields.io/badge/Link-Request_Feature-blue.svg 31 | :target: https://github.com/aws-samples/boto-session-manager-project/issues 32 | 33 | .. image:: https://img.shields.io/badge/Link-Download-blue.svg 34 | :target: https://pypi.org/pypi/boto_session_manager#files 35 | 36 | 37 | Welcome to ``boto_session_manager`` Documentation 38 | ============================================================================== 39 | 40 | 41 | About ``boto_session_manager`` 42 | ------------------------------------------------------------------------------ 43 | ``boto_session_manager`` is a light weight, zero dependency python library that simplify managing your AWS boto3 session in your application code. It bring auto complete and type hint to the default ``boto3`` SDK, and provide smooth development experience with the following goodies: 44 | 45 | - boto3 Client auto complete 46 | - Cached boto3 Client 47 | - Assume IAM role in application code 48 | - Set temporary credential for AWS Cli 49 | 50 | Additionally, if you use `boto3-stubs `_ and you did ``pip install "boto3-stubs[all]"``, then ``boto_session_manager`` comes with the auto complete and type hint for all boto3 methods out-of-the-box, without any extra configuration (such as `explicit type annotations `_) 51 | 52 | 53 | Feature 54 | ------------------------------------------------------------------------------ 55 | **Boto Client Auto Complete** 56 | 57 | Provide an Enum class to access the aws service name to create boto client. 58 | 59 | .. code-block:: python 60 | 61 | from boto_session_manager import BotoSesManager, AwsServiceEnum 62 | 63 | bsm = BotoSesManager() 64 | s3_client = bsm.s3_client 65 | 66 | .. image:: https://user-images.githubusercontent.com/6800411/227536578-839191b7-e1b3-4f92-92b3-9dd5309ea307.gif 67 | 68 | One click to jump to the documentation: 69 | 70 | .. image:: https://user-images.githubusercontent.com/6800411/227536582-8e743936-95a8-4697-b382-72007ff72198.gif 71 | 72 | Client method auto complete: 73 | 74 | .. image:: https://user-images.githubusercontent.com/6800411/227536584-bdbc10d0-bb1a-458d-9248-5d5646a910de.gif 75 | 76 | Arguments type hint: 77 | 78 | .. image:: https://user-images.githubusercontent.com/6800411/227537394-9a494249-1899-4a76-98a7-41ff7a3ac4a6.gif 79 | 80 | Note: you have to do ``pip install "boto3-stubs[all]"`` to enable "Client method auto complete" and "Arguments type hint" features. 81 | 82 | **Cached Client** 83 | 84 | Once an boto session is defined, each AWS Service client should be created only once in most of the case. ``boto_session_manager.BotoSesManager.get_client(service_name)`` allow you to fetch the client object from cache if possible. 85 | 86 | .. code-block:: python 87 | 88 | from boto_session_manager import BotoSesManager, AwsServiceEnum 89 | 90 | bsm = BotoSesManager() 91 | s3_client1 = bsm.get_client(AwsServiceEnum.S3) 92 | s3_client2 = bsm.get_client(AwsServiceEnum.S3) 93 | assert id(s3_client1) = id(s3_client2) 94 | 95 | Or you can just do: 96 | 97 | .. code-block:: python 98 | 99 | bsm.s3_client.list_buckets() # it cache the client when needed 100 | 101 | **Assume Role** 102 | 103 | Create another boto session manager based on an assumed IAM role. Allow you to check if it is expired and maybe renew later. 104 | 105 | .. code-block:: python 106 | 107 | bsm_assumed = bsm.assume_role("arn:aws:iam::111122223333:role/your-assume-role-name") 108 | sts_client = bsm_assumed.get_client(AwsServiceEnum.sts) 109 | print(sts_client.get_caller_identity()) 110 | 111 | print(bsm_assumed.is_expired()) 112 | 113 | From ``1.5.1``, it adds support for auto-refreshable assumed role (Beta). Note that it is using ``AssumeRoleCredentialFetcher`` and ``DeferredRefreshableCredentials`` from botocore, which is not public API officially supported by botocore. This API may be unstable. 114 | 115 | .. code-block:: python 116 | 117 | bsm_assumed = bsm.assume_role( 118 | "arn:aws:iam::111122223333:role/your-assume-role-name", 119 | duration_seconds=900, 120 | auto_refresh=True, 121 | ) 122 | 123 | # even though the duration seconds is only 15 minutes, 124 | # but it can keep running for 1 hour. 125 | tick = 60 126 | sleep = 60 127 | for i in range(tick): 128 | time.sleep(sleep) 129 | print("elapsed {} seconds".format((i + 1) * sleep)) 130 | print("Account id = {}".format(bsm_new.sts_client.get_caller_identity()["Account"])) 131 | 132 | **AWS CLI context manager** 133 | 134 | You explicitly defined a boto session manager that is not the same as the default one used by your AWS CLI. The ``boto_session_manager.BotoSesManager.awscli()`` context manager can temporarily set your default AWS CLI credential as the same as the one you defined, and automatically revert it back. 135 | 136 | .. code-block:: python 137 | 138 | # explicitly define a boto session manager 139 | bsm = BotoSesManager( 140 | profile_name="my_aws_profile", 141 | ) 142 | 143 | with bsm.awscli(): 144 | # now the default AWS CLI credential is the same as the ``bsm`` you defined 145 | 146 | Here's a more detailed example: 147 | 148 | .. code-block:: python 149 | 150 | import os 151 | from boto_session_manager import BotoSesManager 152 | 153 | def print_default_aws_cli_credential(): 154 | print("AWS_ACCESS_KEY_ID =", os.environ.get("AWS_ACCESS_KEY_ID")) 155 | print("AWS_SECRET_ACCESS_KEY =", os.environ.get("AWS_SECRET_ACCESS_KEY")) 156 | print("AWS_SESSION_TOKEN =", os.environ.get("AWS_SESSION_TOKEN")) 157 | print("AWS_REGION =", os.environ.get("AWS_REGION")) 158 | 159 | print("--- before ---") 160 | print_default_aws_cli_credential() 161 | 162 | bsm = BotoSesManager(profile_name="aws_data_lab_open_source_us_east_1") 163 | with bsm.awscli(): 164 | print("--- within awscli() context manager ---") 165 | print_default_aws_cli_credential() 166 | 167 | print("--- after ---") 168 | print_default_aws_cli_credential() 169 | 170 | # --- before --- 171 | # AWS_ACCESS_KEY_ID = None 172 | # AWS_SECRET_ACCESS_KEY = None 173 | # AWS_SESSION_TOKEN = None 174 | # AWS_REGION = None 175 | # --- within awscli() context manager --- 176 | # AWS_ACCESS_KEY_ID = ABCDEFG... 177 | # AWS_SECRET_ACCESS_KEY = ABCDEFG... 178 | # AWS_SESSION_TOKEN = ABCDEFG... 179 | # AWS_REGION = us-east-1 180 | # --- after --- 181 | # AWS_ACCESS_KEY_ID = None 182 | # AWS_SECRET_ACCESS_KEY = None 183 | # AWS_SESSION_TOKEN = None 184 | # AWS_REGION = None 185 | 186 | 187 | .. _install: 188 | 189 | Install 190 | ------------------------------------------------------------------------------ 191 | 192 | ``boto_session_manager`` is released on PyPI, so all you need is: 193 | 194 | .. code-block:: console 195 | 196 | $ pip install boto_session_manager 197 | 198 | To upgrade to latest version: 199 | 200 | .. code-block:: console 201 | 202 | $ pip install --upgrade boto_session_manager 203 | -------------------------------------------------------------------------------- /boto_session_manager/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Usage:: 5 | 6 | >>> from boto_session_manager import BotoSesManager, AwsServiceEnum 7 | >>> bsm = BotoSesManager() 8 | >>> s3_client1 = bsm.s3_client 9 | >>> s3_client2 = bsm.s3_client 10 | >>> id(s3_client1) == id(s3_client2) 11 | True 12 | """ 13 | 14 | from ._version import __version__ 15 | 16 | try: 17 | from .manager import BotoSesManager 18 | from .services import AwsServiceEnum 19 | except ImportError: # pragma: no cover 20 | pass 21 | except: # pragma: no cover 22 | raise 23 | 24 | __short_description__ = "Provides an alternative, or maybe a more user friendly way to use the native boto3 API." 25 | __license__ = "Apache License, Version 2.0" 26 | __author__ = "Sanhe Hu" 27 | __author_email__ = "husanhe@gmail.com" 28 | __maintainer__ = "Sanhe Hu" 29 | __maintainer_email__ = "sanhehu@amazon.com" 30 | __github_username__ = "aws-samples" 31 | -------------------------------------------------------------------------------- /boto_session_manager/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.5.1" 2 | 3 | if __name__ == "__main__": # pragma: no cover 4 | print(__version__) 5 | -------------------------------------------------------------------------------- /boto_session_manager/docs/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | doc_data = dict() 4 | -------------------------------------------------------------------------------- /boto_session_manager/exc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class NoBotocoreCredentialError(Exception): 5 | pass 6 | -------------------------------------------------------------------------------- /boto_session_manager/manager.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Manage the underlying boto3 session and client. 5 | """ 6 | 7 | import typing as T 8 | import os 9 | import uuid 10 | import contextlib 11 | from datetime import datetime, timezone, timedelta 12 | 13 | try: 14 | import boto3 15 | import boto3.session 16 | import botocore.session 17 | except ImportError as e: # pragma: no cover 18 | print("You probably need to install 'boto3' first.") 19 | 20 | try: 21 | from botocore.credentials import ( 22 | AssumeRoleCredentialFetcher, 23 | DeferredRefreshableCredentials, 24 | ) 25 | except ImportError as e: # pragma: no cover 26 | print("The auto refreshable assume role session would not work.") 27 | 28 | if T.TYPE_CHECKING: # pragma: no cover 29 | from botocore.client import BaseClient 30 | from boto3.resources.base import ServiceResource 31 | 32 | from .services import AwsServiceEnum 33 | from .clients import ClientMixin 34 | from .sentinel import NOTHING, resolve_kwargs 35 | from .exc import NoBotocoreCredentialError 36 | 37 | 38 | class BotoSesManager(ClientMixin): 39 | """ 40 | Boto3 session and client manager that use cache to create low level client. 41 | 42 | .. note:: 43 | 44 | boto3.session.Session is a static object that won't talk to AWS endpoint. 45 | also session.client("s3") won't talk to AWS endpoint right away. The 46 | authentication only happen when a concrete API request called. 47 | 48 | .. versionadded:: 0.0.1 49 | 50 | .. versionchanged:: 0.0.4 51 | 52 | add ``default_client_kwargs`` arguments that set default keyword 53 | arguments for ``boto3.session.Session.client`` method. 54 | """ 55 | 56 | def __init__( 57 | self, 58 | aws_access_key_id: T.Optional[str] = NOTHING, 59 | aws_secret_access_key: T.Optional[str] = NOTHING, 60 | aws_session_token: T.Optional[str] = NOTHING, 61 | region_name: T.Optional[str] = NOTHING, 62 | botocore_session: T.Optional["botocore.session.Session"] = NOTHING, 63 | profile_name: str = NOTHING, 64 | default_client_kwargs: dict = NOTHING, 65 | expiration_time: datetime = NOTHING, 66 | ): 67 | self.aws_access_key_id = aws_access_key_id 68 | self.aws_secret_access_key = aws_secret_access_key 69 | self.aws_session_token = aws_session_token 70 | self.region_name = region_name 71 | if botocore_session is not NOTHING: # pragma: no cover 72 | if not isinstance(botocore_session, botocore.session.Session): 73 | raise TypeError 74 | self.botocore_session: T.Optional["botocore.session.Session"] = botocore_session 75 | self.profile_name = profile_name 76 | self.expiration_time: datetime 77 | if expiration_time is NOTHING: 78 | self.expiration_time = datetime.utcnow().replace( 79 | tzinfo=timezone.utc 80 | ) + timedelta(days=365) 81 | else: 82 | self.expiration_time = expiration_time 83 | if default_client_kwargs is NOTHING: 84 | default_client_kwargs = dict() 85 | self.default_client_kwargs = default_client_kwargs 86 | 87 | self._boto_ses_cache: T.Optional["boto3.session.Session"] = NOTHING 88 | self._client_cache: T.Dict[str, "BaseClient"] = dict() 89 | self._resource_cache: T.Dict[str, "ServiceResource"] = dict() 90 | self._aws_account_id_cache: T.Optional[str] = NOTHING 91 | self._aws_region_cache: T.Optional[str] = NOTHING 92 | 93 | @property 94 | def boto_ses(self) -> "boto3.session.Session": 95 | """ 96 | Get boto3 session from metadata. 97 | 98 | .. versionadded:: 1.0.2 99 | """ 100 | if self._boto_ses_cache is NOTHING: 101 | self._boto_ses_cache = boto3.session.Session( 102 | **resolve_kwargs( 103 | aws_access_key_id=self.aws_access_key_id, 104 | aws_secret_access_key=self.aws_secret_access_key, 105 | aws_session_token=self.aws_session_token, 106 | region_name=self.region_name, 107 | botocore_session=self.botocore_session, 108 | profile_name=self.profile_name, 109 | ) 110 | ) 111 | return self._boto_ses_cache 112 | 113 | @property 114 | def aws_account_id(self) -> str: 115 | """ 116 | Get current aws account id of the boto session. 117 | 118 | .. versionadded:: 1.0.1 119 | """ 120 | if self._aws_account_id_cache is NOTHING: 121 | sts_client = self.get_client(AwsServiceEnum.STS) 122 | self._aws_account_id_cache = sts_client.get_caller_identity()["Account"] 123 | return self._aws_account_id_cache 124 | 125 | @property 126 | def aws_region(self) -> str: 127 | """ 128 | Get current aws region of the boto session. 129 | 130 | .. versionadded:: 0.0.1 131 | """ 132 | if self._aws_region_cache is NOTHING: 133 | self._aws_region_cache = self.boto_ses.region_name 134 | return self._aws_region_cache 135 | 136 | def get_client( 137 | self, 138 | service_name: str, 139 | region_name: str = NOTHING, 140 | api_version: str = NOTHING, 141 | use_ssl: bool = True, 142 | verify: T.Union[bool, str] = NOTHING, 143 | endpoint_url: str = NOTHING, 144 | aws_access_key_id: str = NOTHING, 145 | aws_secret_access_key: str = NOTHING, 146 | aws_session_token: str = NOTHING, 147 | config=None, 148 | ) -> "BaseClient": 149 | """ 150 | Get aws boto client using cache. 151 | 152 | .. versionadded:: 0.0.1 153 | 154 | .. versionchanged:: 0.0.3 155 | 156 | add additional keyword arguments pass to 157 | ``boto3.session.Session.client()`` method. 158 | """ 159 | try: 160 | return self._client_cache[service_name] 161 | except KeyError: 162 | client_kwargs = resolve_kwargs( 163 | region_name=region_name, 164 | api_version=api_version, 165 | use_ssl=use_ssl, 166 | verify=verify, 167 | endpoint_url=endpoint_url, 168 | aws_access_key_id=aws_access_key_id, 169 | aws_secret_access_key=aws_secret_access_key, 170 | aws_session_token=aws_session_token, 171 | config=config, 172 | ) 173 | kwargs = dict(self.default_client_kwargs) 174 | if self.default_client_kwargs: # pragma: no cover 175 | kwargs.update(client_kwargs) 176 | client = self.boto_ses.client(service_name, **kwargs) 177 | self._client_cache[service_name] = client 178 | return client 179 | 180 | def get_resource( 181 | self, 182 | service_name: str, 183 | region_name: str = NOTHING, 184 | api_version: str = NOTHING, 185 | use_ssl: bool = True, 186 | verify: T.Union[bool, str] = NOTHING, 187 | endpoint_url: str = NOTHING, 188 | aws_access_key_id: str = NOTHING, 189 | aws_secret_access_key: str = NOTHING, 190 | aws_session_token: str = NOTHING, 191 | config=NOTHING, 192 | ) -> "ServiceResource": 193 | """ 194 | Get aws boto service resource using cache 195 | 196 | .. versionadded:: 0.0.2 197 | 198 | .. versionchanged:: 0.0.3 199 | 200 | add additional keyword arguments pass to 201 | ``boto3.session.Session.resource()`` method. 202 | """ 203 | try: 204 | return self._resource_cache[service_name] 205 | except KeyError: 206 | resource_kwargs = resolve_kwargs( 207 | region_name=region_name, 208 | api_version=api_version, 209 | use_ssl=use_ssl, 210 | verify=verify, 211 | endpoint_url=endpoint_url, 212 | aws_access_key_id=aws_access_key_id, 213 | aws_secret_access_key=aws_secret_access_key, 214 | aws_session_token=aws_session_token, 215 | config=config, 216 | ) 217 | kwargs = dict(self.default_client_kwargs) 218 | if self.default_client_kwargs: 219 | kwargs.update(resource_kwargs) 220 | resource = self.boto_ses.resource(service_name, **kwargs) 221 | self._resource_cache[service_name] = resource 222 | return resource 223 | 224 | def assume_role( 225 | self, 226 | role_arn: str, 227 | role_session_name: str = NOTHING, 228 | duration_seconds: int = 3600, 229 | tags: T.Optional[T.List[T.Dict[str, str]]] = NOTHING, 230 | transitive_tag_keys: T.Optional[T.List[str]] = NOTHING, 231 | external_id: str = NOTHING, 232 | mfa_serial_number: str = NOTHING, 233 | mfa_token: str = NOTHING, 234 | source_identity: str = NOTHING, 235 | auto_refresh: bool = False, 236 | ) -> "BotoSesManager": 237 | """ 238 | Assume an IAM role, create another :class`BotoSessionManager` and return. 239 | 240 | :param auto_refresh: if True, the assumed role will be refreshed automatically. 241 | 242 | .. versionadded:: 0.0.1 243 | 244 | .. versionchanged:: 1.5.1 245 | 246 | add ``auto_refresh`` argument. note that it is using 247 | ``AssumeRoleCredentialFetcher`` and ``DeferredRefreshableCredentials`` 248 | from botocore, which is not public API officially supported by botocore. 249 | """ 250 | if role_session_name is NOTHING: 251 | role_session_name = uuid.uuid4().hex 252 | 253 | # this branch cannot be tested regularly 254 | # it is tested in a separate integration test environment. 255 | if auto_refresh: # pragma: no cover 256 | botocore_session = self.boto_ses._session 257 | credentials = botocore_session.get_credentials() 258 | # the get_credentials() method can return None 259 | # raise error explicitly 260 | if not credentials: 261 | raise NoBotocoreCredentialError 262 | 263 | credential_fetcher = AssumeRoleCredentialFetcher( 264 | client_creator=botocore_session.create_client, 265 | source_credentials=credentials, 266 | role_arn=role_arn, 267 | extra_args=resolve_kwargs( 268 | RoleSessionName=role_session_name, 269 | DurationSeconds=duration_seconds, 270 | Tags=tags, 271 | TransitiveTagKeys=transitive_tag_keys, 272 | external_id=external_id, 273 | SerialNumber=mfa_serial_number, 274 | TokenCode=mfa_token, 275 | SourceIdentity=source_identity, 276 | ), 277 | ) 278 | 279 | assumed_role_credentials = DeferredRefreshableCredentials( 280 | refresh_using=credential_fetcher.fetch_credentials, 281 | method="assume-role", 282 | ) 283 | assumed_role_botocore_session: "botocore.session.Session" = botocore.session.get_session() 284 | assumed_role_botocore_session._credentials = assumed_role_credentials 285 | return BotoSesManager( 286 | botocore_session=assumed_role_botocore_session, 287 | # ensure that the new boto session manager with assumed role 288 | # is using the same AWS region as this one 289 | region_name=self.aws_region, 290 | expiration_time=datetime(2099, 12, 31, 23, 59, 59, tzinfo=timezone.utc), 291 | default_client_kwargs=self.default_client_kwargs, 292 | ) 293 | else: 294 | assume_role_kwargs = resolve_kwargs( 295 | RoleArn=role_arn, 296 | RoleSessionName=role_session_name, 297 | DurationSeconds=duration_seconds, 298 | Tags=tags, 299 | TransitiveTagKeys=transitive_tag_keys, 300 | external_id=external_id, 301 | SerialNumber=mfa_serial_number, 302 | TokenCode=mfa_token, 303 | SourceIdentity=source_identity, 304 | ) 305 | sts_client = self.get_client(AwsServiceEnum.STS) 306 | res = sts_client.assume_role(**assume_role_kwargs) 307 | expiration_time = res["Credentials"]["Expiration"] 308 | bsm = self.__class__( 309 | aws_access_key_id=res["Credentials"]["AccessKeyId"], 310 | aws_secret_access_key=res["Credentials"]["SecretAccessKey"], 311 | aws_session_token=res["Credentials"]["SessionToken"], 312 | expiration_time=expiration_time, 313 | default_client_kwargs=self.default_client_kwargs, 314 | ) 315 | return bsm 316 | 317 | def is_expired(self, delta: int = 0) -> bool: 318 | """ 319 | Check if this boto session is expired. 320 | 321 | .. versionadded:: 0.0.1 322 | """ 323 | return ( 324 | datetime.utcnow().replace(tzinfo=timezone.utc) + timedelta(seconds=delta) 325 | ) >= self.expiration_time 326 | 327 | @contextlib.contextmanager 328 | def awscli( 329 | self, 330 | duration_seconds: int = 900, 331 | serial_number: T.Optional[str] = NOTHING, 332 | token_code: T.Optional[str] = NOTHING, 333 | ) -> "BotoSesManager": 334 | """ 335 | Temporarily set up environment variable to pass the boto session 336 | credential to AWS CLI. 337 | 338 | Example:: 339 | 340 | import subprocess 341 | 342 | bsm = BotoSesManager(...) 343 | 344 | with bsm.awscli(): 345 | subprocess.run(["aws", "sts", "get-caller-identity"]) 346 | 347 | Reference: 348 | 349 | - https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html 350 | 351 | .. versionadded:: 1.2.1 352 | """ 353 | # record the existing env var state 354 | env_names = [ 355 | "AWS_ACCESS_KEY_ID", 356 | "AWS_SECRET_ACCESS_KEY", 357 | "AWS_SESSION_TOKEN", 358 | "AWS_REGION", 359 | "AWS_PROFILE", 360 | ] 361 | env_var = dict() 362 | for env_name in env_names: 363 | if env_name in os.environ: # pragma: no cover 364 | env_var[env_name] = os.environ[env_name] 365 | else: 366 | env_var[env_name] = None 367 | 368 | # set environment variable for aws cli 369 | if ( 370 | (self.aws_access_key_id is not NOTHING) 371 | and (self.aws_secret_access_key is not NOTHING) 372 | and (self.aws_access_key_id is not NOTHING) 373 | ): 374 | os.environ["AWS_ACCESS_KEY_ID"] = self.aws_access_key_id 375 | os.environ["AWS_SECRET_ACCESS_KEY"] = self.aws_secret_access_key 376 | os.environ["AWS_SESSION_TOKEN"] = self.aws_session_token 377 | os.environ["AWS_REGION"] = self.aws_region 378 | else: 379 | kwargs = dict( 380 | DurationSeconds=duration_seconds, 381 | ) 382 | if serial_number is not NOTHING: # pragma: no cover 383 | kwargs["SerialNumber"] = serial_number 384 | if token_code is not NOTHING: # pragma: no cover 385 | kwargs["TokenCode"] = token_code 386 | response = self.sts_client.get_session_token(**kwargs) 387 | os.environ["AWS_ACCESS_KEY_ID"] = response["Credentials"]["AccessKeyId"] 388 | os.environ["AWS_SECRET_ACCESS_KEY"] = response["Credentials"][ 389 | "SecretAccessKey" 390 | ] 391 | os.environ["AWS_SESSION_TOKEN"] = response["Credentials"]["SessionToken"] 392 | os.environ["AWS_REGION"] = self.aws_region 393 | try: 394 | yield self 395 | finally: 396 | # recover previous existing env var 397 | for env_name, env_value in env_var.items(): 398 | if env_value is None: 399 | if env_name in os.environ: 400 | os.environ.pop(env_name) 401 | else: 402 | os.environ[env_name] = env_value 403 | 404 | def clear_cache(self): 405 | """ 406 | Clear all the boto session and boto client cache. 407 | """ 408 | self._boto_ses_cache = NOTHING 409 | self._client_cache.clear() 410 | self._resource_cache.clear() 411 | self._aws_account_id_cache = NOTHING 412 | self._aws_region_cache = NOTHING 413 | -------------------------------------------------------------------------------- /boto_session_manager/sentinel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import typing as T 4 | import sys 5 | 6 | 7 | def make_sentinel(name="_MISSING", var_name=None): # pragma: no cover 8 | """Creates and returns a new **instance** of a new class, suitable for 9 | usage as a "sentinel", a kind of singleton often used to indicate 10 | a value is missing when ``None`` is a valid input. 11 | Args: 12 | name (str): Name of the Sentinel 13 | var_name (str): Set this name to the name of the variable in 14 | its respective module enable pickleability. Note: 15 | pickleable sentinels should be global constants at the top 16 | level of their module. 17 | >>> make_sentinel(var_name='_MISSING') 18 | _MISSING 19 | The most common use cases here in boltons are as default values 20 | for optional function arguments, partly because of its 21 | less-confusing appearance in automatically generated 22 | documentation. Sentinels also function well as placeholders in queues 23 | and linked lists. 24 | .. note:: 25 | By design, additional calls to ``make_sentinel`` with the same 26 | values will not produce equivalent objects. 27 | >>> make_sentinel('TEST') == make_sentinel('TEST') 28 | False 29 | >>> type(make_sentinel('TEST')) == type(make_sentinel('TEST')) 30 | False 31 | """ 32 | 33 | class Sentinel(object): 34 | def __init__(self): 35 | self.name = name 36 | self.var_name = var_name 37 | 38 | def __repr__(self): 39 | if self.var_name: 40 | return self.var_name 41 | return "%s(%r)" % (self.__class__.__name__, self.name) 42 | 43 | if var_name: 44 | 45 | def __reduce__(self): 46 | return self.var_name 47 | 48 | def __nonzero__(self): 49 | return False 50 | 51 | __bool__ = __nonzero__ 52 | 53 | if var_name: 54 | frame = sys._getframe(1) 55 | module = frame.f_globals.get("__name__") 56 | if not module or module not in sys.modules: 57 | raise ValueError( 58 | "Pickleable sentinel objects (with var_name) can only" 59 | " be created from top-level module scopes" 60 | ) 61 | Sentinel.__module__ = module 62 | 63 | return Sentinel() 64 | 65 | 66 | NOTHING = make_sentinel(name="NOTHING") 67 | 68 | 69 | def resolve_kwargs( 70 | _mapper: T.Optional[T.Dict[str, str]] = None, 71 | **kwargs, 72 | ) -> dict: 73 | if _mapper is None: # pragma: no cover 74 | _mapper = dict() 75 | return { 76 | _mapper.get(key, key): value 77 | for key, value in kwargs.items() 78 | if value is not NOTHING 79 | } 80 | -------------------------------------------------------------------------------- /boto_session_manager/services.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class AwsServiceEnum: 5 | AccessAnalyzer = "accessanalyzer" 6 | Account = "account" 7 | ACM = "acm" 8 | ACMPCA = "acm-pca" 9 | AlexaForBusiness = "alexaforbusiness" 10 | PrometheusService = "amp" 11 | Amplify = "amplify" 12 | AmplifyBackend = "amplifybackend" 13 | AmplifyUIBuilder = "amplifyuibuilder" 14 | APIGateway = "apigateway" 15 | ApiGatewayManagementApi = "apigatewaymanagementapi" 16 | ApiGatewayV2 = "apigatewayv2" 17 | AppConfig = "appconfig" 18 | AppConfigData = "appconfigdata" 19 | Appflow = "appflow" 20 | AppIntegrationsService = "appintegrations" 21 | ApplicationAutoScaling = "application-autoscaling" 22 | ApplicationInsights = "application-insights" 23 | ApplicationCostProfiler = "applicationcostprofiler" 24 | AppMesh = "appmesh" 25 | AppRunner = "apprunner" 26 | AppStream = "appstream" 27 | AppSync = "appsync" 28 | ARCZonalShift = "arc-zonal-shift" 29 | Athena = "athena" 30 | AuditManager = "auditmanager" 31 | AutoScaling = "autoscaling" 32 | AutoScalingPlans = "autoscaling-plans" 33 | Backup = "backup" 34 | BackupGateway = "backup-gateway" 35 | BackupStorage = "backupstorage" 36 | Batch = "batch" 37 | BillingConductor = "billingconductor" 38 | Braket = "braket" 39 | Budgets = "budgets" 40 | CostExplorer = "ce" 41 | Chime = "chime" 42 | ChimeSDKIdentity = "chime-sdk-identity" 43 | ChimeSDKMediaPipelines = "chime-sdk-media-pipelines" 44 | ChimeSDKMeetings = "chime-sdk-meetings" 45 | ChimeSDKMessaging = "chime-sdk-messaging" 46 | ChimeSDKVoice = "chime-sdk-voice" 47 | CleanRoomsService = "cleanrooms" 48 | Cloud9 = "cloud9" 49 | CloudControlApi = "cloudcontrol" 50 | CloudDirectory = "clouddirectory" 51 | CloudFormation = "cloudformation" 52 | CloudFront = "cloudfront" 53 | CloudHSM = "cloudhsm" 54 | CloudHSMV2 = "cloudhsmv2" 55 | CloudSearch = "cloudsearch" 56 | CloudSearchDomain = "cloudsearchdomain" 57 | CloudTrail = "cloudtrail" 58 | CloudTrailDataService = "cloudtrail-data" 59 | CloudWatch = "cloudwatch" 60 | CodeArtifact = "codeartifact" 61 | CodeBuild = "codebuild" 62 | CodeCatalyst = "codecatalyst" 63 | CodeCommit = "codecommit" 64 | CodeDeploy = "codedeploy" 65 | CodeGuruReviewer = "codeguru-reviewer" 66 | CodeGuruProfiler = "codeguruprofiler" 67 | CodePipeline = "codepipeline" 68 | CodeStar = "codestar" 69 | CodeStarconnections = "codestar-connections" 70 | CodeStarNotifications = "codestar-notifications" 71 | CognitoIdentity = "cognito-identity" 72 | CognitoIdentityProvider = "cognito-idp" 73 | CognitoSync = "cognito-sync" 74 | Comprehend = "comprehend" 75 | ComprehendMedical = "comprehendmedical" 76 | ComputeOptimizer = "compute-optimizer" 77 | ConfigService = "config" 78 | Connect = "connect" 79 | ConnectContactLens = "connect-contact-lens" 80 | ConnectCampaignService = "connectcampaigns" 81 | ConnectCases = "connectcases" 82 | ConnectParticipant = "connectparticipant" 83 | ControlTower = "controltower" 84 | CostandUsageReportService = "cur" 85 | CustomerProfiles = "customer-profiles" 86 | GlueDataBrew = "databrew" 87 | DataExchange = "dataexchange" 88 | DataPipeline = "datapipeline" 89 | DataSync = "datasync" 90 | DAX = "dax" 91 | Detective = "detective" 92 | DeviceFarm = "devicefarm" 93 | DevOpsGuru = "devops-guru" 94 | DirectConnect = "directconnect" 95 | ApplicationDiscoveryService = "discovery" 96 | DLM = "dlm" 97 | DatabaseMigrationService = "dms" 98 | DocDB = "docdb" 99 | DocDBElastic = "docdb-elastic" 100 | drs = "drs" 101 | DirectoryService = "ds" 102 | DynamoDB = "dynamodb" 103 | DynamoDBStreams = "dynamodbstreams" 104 | EBS = "ebs" 105 | EC2 = "ec2" 106 | EC2InstanceConnect = "ec2-instance-connect" 107 | ECR = "ecr" 108 | ECRPublic = "ecr-public" 109 | ECS = "ecs" 110 | EFS = "efs" 111 | EKS = "eks" 112 | ElasticInference = "elastic-inference" 113 | ElastiCache = "elasticache" 114 | ElasticBeanstalk = "elasticbeanstalk" 115 | ElasticTranscoder = "elastictranscoder" 116 | ElasticLoadBalancing = "elb" 117 | ElasticLoadBalancingv2 = "elbv2" 118 | EMR = "emr" 119 | EMRContainers = "emr-containers" 120 | EMRServerless = "emr-serverless" 121 | ElasticsearchService = "es" 122 | EventBridge = "events" 123 | CloudWatchEvidently = "evidently" 124 | finspace = "finspace" 125 | FinSpaceData = "finspace-data" 126 | Firehose = "firehose" 127 | FIS = "fis" 128 | FMS = "fms" 129 | ForecastService = "forecast" 130 | ForecastQueryService = "forecastquery" 131 | FraudDetector = "frauddetector" 132 | FSx = "fsx" 133 | GameLift = "gamelift" 134 | GameSparks = "gamesparks" 135 | Glacier = "glacier" 136 | GlobalAccelerator = "globalaccelerator" 137 | Glue = "glue" 138 | ManagedGrafana = "grafana" 139 | Greengrass = "greengrass" 140 | GreengrassV2 = "greengrassv2" 141 | GroundStation = "groundstation" 142 | GuardDuty = "guardduty" 143 | Health = "health" 144 | HealthLake = "healthlake" 145 | Honeycode = "honeycode" 146 | IAM = "iam" 147 | IdentityStore = "identitystore" 148 | imagebuilder = "imagebuilder" 149 | ImportExport = "importexport" 150 | Inspector = "inspector" 151 | Inspector2 = "inspector2" 152 | CloudWatchInternetMonitor = "internetmonitor" 153 | IoT = "iot" 154 | IoTDataPlane = "iot-data" 155 | IoTJobsDataPlane = "iot-jobs-data" 156 | IoTRoboRunner = "iot-roborunner" 157 | IoT1ClickDevicesService = "iot1click-devices" 158 | IoT1ClickProjects = "iot1click-projects" 159 | IoTAnalytics = "iotanalytics" 160 | IoTDeviceAdvisor = "iotdeviceadvisor" 161 | IoTEvents = "iotevents" 162 | IoTEventsData = "iotevents-data" 163 | IoTFleetHub = "iotfleethub" 164 | IoTFleetWise = "iotfleetwise" 165 | IoTSecureTunneling = "iotsecuretunneling" 166 | IoTSiteWise = "iotsitewise" 167 | IoTThingsGraph = "iotthingsgraph" 168 | IoTTwinMaker = "iottwinmaker" 169 | IoTWireless = "iotwireless" 170 | IVS = "ivs" 171 | ivschat = "ivschat" 172 | Kafka = "kafka" 173 | KafkaConnect = "kafkaconnect" 174 | kendra = "kendra" 175 | KendraRanking = "kendra-ranking" 176 | Keyspaces = "keyspaces" 177 | Kinesis = "kinesis" 178 | KinesisVideoArchivedMedia = "kinesis-video-archived-media" 179 | KinesisVideoMedia = "kinesis-video-media" 180 | KinesisVideoSignalingChannels = "kinesis-video-signaling" 181 | KinesisVideoWebRTCStorage = "kinesis-video-webrtc-storage" 182 | KinesisAnalytics = "kinesisanalytics" 183 | KinesisAnalyticsV2 = "kinesisanalyticsv2" 184 | KinesisVideo = "kinesisvideo" 185 | KMS = "kms" 186 | LakeFormation = "lakeformation" 187 | Lambda = "lambda" 188 | LexModelBuildingService = "lex-models" 189 | LexRuntimeService = "lex-runtime" 190 | LexModelsV2 = "lexv2-models" 191 | LexRuntimeV2 = "lexv2-runtime" 192 | LicenseManager = "license-manager" 193 | LicenseManagerLinuxSubscriptions = "license-manager-linux-subscriptions" 194 | LicenseManagerUserSubscriptions = "license-manager-user-subscriptions" 195 | Lightsail = "lightsail" 196 | LocationService = "location" 197 | CloudWatchLogs = "logs" 198 | LookoutEquipment = "lookoutequipment" 199 | LookoutMetrics = "lookoutmetrics" 200 | LookoutforVision = "lookoutvision" 201 | MainframeModernization = "m2" 202 | MachineLearning = "machinelearning" 203 | Macie = "macie" 204 | Macie2 = "macie2" 205 | ManagedBlockchain = "managedblockchain" 206 | MarketplaceCatalog = "marketplace-catalog" 207 | MarketplaceEntitlementService = "marketplace-entitlement" 208 | MarketplaceCommerceAnalytics = "marketplacecommerceanalytics" 209 | MediaConnect = "mediaconnect" 210 | MediaConvert = "mediaconvert" 211 | MediaLive = "medialive" 212 | MediaPackage = "mediapackage" 213 | MediaPackageVod = "mediapackage-vod" 214 | MediaStore = "mediastore" 215 | MediaStoreData = "mediastore-data" 216 | MediaTailor = "mediatailor" 217 | MemoryDB = "memorydb" 218 | MarketplaceMetering = "meteringmarketplace" 219 | MigrationHub = "mgh" 220 | mgn = "mgn" 221 | MigrationHubRefactorSpaces = "migration-hub-refactor-spaces" 222 | MigrationHubConfig = "migrationhub-config" 223 | MigrationHubOrchestrator = "migrationhuborchestrator" 224 | MigrationHubStrategyRecommendations = "migrationhubstrategy" 225 | Mobile = "mobile" 226 | MQ = "mq" 227 | MTurk = "mturk" 228 | MWAA = "mwaa" 229 | Neptune = "neptune" 230 | NetworkFirewall = "network-firewall" 231 | NetworkManager = "networkmanager" 232 | NimbleStudio = "nimble" 233 | CloudWatchObservabilityAccessManager = "oam" 234 | Omics = "omics" 235 | OpenSearchService = "opensearch" 236 | OpenSearchServiceServerless = "opensearchserverless" 237 | OpsWorks = "opsworks" 238 | OpsWorksCM = "opsworkscm" 239 | Organizations = "organizations" 240 | Outposts = "outposts" 241 | Panorama = "panorama" 242 | Personalize = "personalize" 243 | PersonalizeEvents = "personalize-events" 244 | PersonalizeRuntime = "personalize-runtime" 245 | PI = "pi" 246 | Pinpoint = "pinpoint" 247 | PinpointEmail = "pinpoint-email" 248 | PinpointSMSVoice = "pinpoint-sms-voice" 249 | PinpointSMSVoiceV2 = "pinpoint-sms-voice-v2" 250 | EventBridgePipes = "pipes" 251 | Polly = "polly" 252 | Pricing = "pricing" 253 | Private5G = "privatenetworks" 254 | Proton = "proton" 255 | QLDB = "qldb" 256 | QLDBSession = "qldb-session" 257 | QuickSight = "quicksight" 258 | RAM = "ram" 259 | RecycleBin = "rbin" 260 | RDS = "rds" 261 | RDSDataService = "rds-data" 262 | Redshift = "redshift" 263 | RedshiftDataAPIService = "redshift-data" 264 | RedshiftServerless = "redshift-serverless" 265 | Rekognition = "rekognition" 266 | ResilienceHub = "resiliencehub" 267 | ResourceExplorer = "resource-explorer-2" 268 | ResourceGroups = "resource-groups" 269 | ResourceGroupsTaggingAPI = "resourcegroupstaggingapi" 270 | RoboMaker = "robomaker" 271 | IAMRolesAnywhere = "rolesanywhere" 272 | Route53 = "route53" 273 | Route53RecoveryCluster = "route53-recovery-cluster" 274 | Route53RecoveryControlConfig = "route53-recovery-control-config" 275 | Route53RecoveryReadiness = "route53-recovery-readiness" 276 | Route53Domains = "route53domains" 277 | Route53Resolver = "route53resolver" 278 | CloudWatchRUM = "rum" 279 | S3 = "s3" 280 | S3Control = "s3control" 281 | S3Outposts = "s3outposts" 282 | SageMaker = "sagemaker" 283 | AugmentedAIRuntime = "sagemaker-a2i-runtime" 284 | SagemakerEdgeManager = "sagemaker-edge" 285 | SageMakerFeatureStoreRuntime = "sagemaker-featurestore-runtime" 286 | SageMakergeospatialcapabilities = "sagemaker-geospatial" 287 | SageMakerMetrics = "sagemaker-metrics" 288 | SageMakerRuntime = "sagemaker-runtime" 289 | SavingsPlans = "savingsplans" 290 | EventBridgeScheduler = "scheduler" 291 | Schemas = "schemas" 292 | SimpleDB = "sdb" 293 | SecretsManager = "secretsmanager" 294 | SecurityHub = "securityhub" 295 | SecurityLake = "securitylake" 296 | ServerlessApplicationRepository = "serverlessrepo" 297 | ServiceQuotas = "service-quotas" 298 | ServiceCatalog = "servicecatalog" 299 | AppRegistry = "servicecatalog-appregistry" 300 | ServiceDiscovery = "servicediscovery" 301 | SES = "ses" 302 | SESV2 = "sesv2" 303 | Shield = "shield" 304 | signer = "signer" 305 | SimSpaceWeaver = "simspaceweaver" 306 | SMS = "sms" 307 | PinpointSMSVoice = "sms-voice" 308 | SnowDeviceManagement = "snow-device-management" 309 | Snowball = "snowball" 310 | SNS = "sns" 311 | SQS = "sqs" 312 | SSM = "ssm" 313 | SSMContacts = "ssm-contacts" 314 | SSMIncidents = "ssm-incidents" 315 | SsmSap = "ssm-sap" 316 | SSO = "sso" 317 | SSOAdmin = "sso-admin" 318 | SSOOIDC = "sso-oidc" 319 | SFN = "stepfunctions" 320 | StorageGateway = "storagegateway" 321 | STS = "sts" 322 | Support = "support" 323 | SupportApp = "support-app" 324 | SWF = "swf" 325 | Synthetics = "synthetics" 326 | Textract = "textract" 327 | TimestreamQuery = "timestream-query" 328 | TimestreamWrite = "timestream-write" 329 | TelcoNetworkBuilder = "tnb" 330 | TranscribeService = "transcribe" 331 | Transfer = "transfer" 332 | Translate = "translate" 333 | VoiceID = "voice-id" 334 | WAF = "waf" 335 | WAFRegional = "waf-regional" 336 | WAFV2 = "wafv2" 337 | WellArchitected = "wellarchitected" 338 | ConnectWisdomService = "wisdom" 339 | WorkDocs = "workdocs" 340 | WorkLink = "worklink" 341 | WorkMail = "workmail" 342 | WorkMailMessageFlow = "workmailmessageflow" 343 | WorkSpaces = "workspaces" 344 | WorkSpacesWeb = "workspaces-web" 345 | XRay = "xray" -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "0...100" 8 | 9 | parsers: 10 | gcov: 11 | branch_detection: 12 | conditional: yes 13 | loop: yes 14 | method: no 15 | macro: no 16 | 17 | comment: 18 | layout: "reach,diff,flags,files,footer" 19 | behavior: default 20 | require_changes: no 21 | -------------------------------------------------------------------------------- /debug/.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | -------------------------------------------------------------------------------- /debug/clients.jinja2: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import typing as T 4 | 5 | from .services import AwsServiceEnum 6 | 7 | if T.TYPE_CHECKING: 8 | from .manager import BotoSesManager 9 | {%- for aws_service in aws_service_list %} 10 | import mypy_boto3_{{ aws_service.service_id_snake_case }} 11 | {%- endfor %} 12 | 13 | class ClientMixin: 14 | {% for aws_service in aws_service_list %} 15 | @property 16 | def {{ aws_service.name_snake_case }}_client(self: "BotoSesManager") -> "mypy_boto3_{{ aws_service.service_id_snake_case }}.Client": 17 | """ 18 | Ref: {{ aws_service.doc_url }} 19 | """ 20 | return self.get_client(AwsServiceEnum.{{ aws_service.name }}) 21 | {% endfor %} 22 | -------------------------------------------------------------------------------- /debug/crawl_service_id_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Crawl `boto3 documentation site `_, 5 | find all client id and automatically generate code for this library. 6 | """ 7 | 8 | import typing as T 9 | 10 | import re 11 | import json 12 | import dataclasses 13 | from functools import cached_property 14 | 15 | import jinja2 16 | import requests 17 | from jinja2 import Template 18 | from bs4 import BeautifulSoup 19 | from diskcache import Cache 20 | from pathlib_mate import Path 21 | 22 | 23 | # ------------------------------------------------------------------------------ 24 | dir_here = Path.dir_here(__file__) 25 | dir_project_root = dir_here.parent 26 | dir_cache = Path(dir_here, ".cache") 27 | 28 | cache = Cache(dir_cache.abspath) 29 | cache_expire = 24 * 3600 # expires in 1 day 30 | 31 | path_spec_file_json = Path(dir_here, "spec-file.json") 32 | path_services_py = Path(dir_project_root, "boto_session_manager", "services.py") 33 | path_clients_py = Path(dir_project_root, "boto_session_manager", "clients.py") 34 | path_services_template = Path(dir_here, "services.jinja2") 35 | path_clients_template = Path(dir_here, "clients.jinja2") 36 | # ------------------------------------------------------------------------------ 37 | 38 | 39 | def get_html_with_cache(url: str) -> str: 40 | """ 41 | Crawl the URL HTML content, store it to the disk cache. 42 | 43 | :param url: webpage URL 44 | :return: HTML 45 | """ 46 | if url in cache: 47 | return cache.get(url) 48 | else: 49 | html = requests.get(url).text 50 | cache.set(url, html, expire=cache_expire) 51 | return html 52 | 53 | 54 | @dataclasses.dataclass 55 | class AWSService: 56 | """ 57 | ServiceHomepage Dataclass 58 | 59 | :param name: it is the clickable text in the boto3 document homepage sidebar. 60 | For example, for Elastic Block Storage service, the url is 61 | https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ebs.html, 62 | the clickable text in the sidebar is "EBS". 63 | :param href_name: the last part of the document url. 64 | For example, for Elastic Block Storage service, the url is 65 | https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ebs.html, 66 | the last part is "ebs". 67 | :param doc_url: the boto3 document url 68 | :param service_id: the service id that can be used in boto3.client("${service_id}") 69 | """ 70 | 71 | name: str = dataclasses.field() 72 | href_name: str = dataclasses.field() 73 | doc_url: str = dataclasses.field() 74 | service_id: str = dataclasses.field() 75 | 76 | @property 77 | def name_snake_case(self) -> str: 78 | return self.name.lower().replace("-", "_") 79 | 80 | @property 81 | def service_id_snake_case(self) -> str: 82 | return self.service_id.lower().replace("-", "_") 83 | 84 | @property 85 | def service_id_camel_case(self) -> str: 86 | return "".join([ 87 | word[0].upper() + word[1:] 88 | for word in self.name.lower().split("-") 89 | ]) 90 | 91 | 92 | def crawl_service_page(html: str) -> str: 93 | """ 94 | Given an AWS Service boto3 API documentation webpage HTML, 95 | 96 | extract the string token that used to create boto3 client. 97 | 98 | For example, given EBS boto3 API doc at https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ebs.html 99 | it returns "ebs", so we can use boto3.client("ebs") to create ebs client. 100 | 101 | :param html: 102 | 103 | :return: service id 104 | """ 105 | pattern = re.compile("client = boto3.client\('[\d\D]*'\)") 106 | soup = BeautifulSoup(html, "html.parser") 107 | div_client = soup.find("div", class_="highlight-default notranslate") 108 | service_id = None 109 | for line in div_client.text.split("\n"): 110 | res = re.findall(pattern, line) 111 | if len(res): 112 | service_id = res[0].split("'")[1] 113 | return service_id 114 | 115 | 116 | def crawl_home_page(html: str): 117 | """ 118 | get all AWS Service boto3 api homepage from the boto3 doc homepage, 119 | from its sidebar. 120 | """ 121 | aws_service_list = list() 122 | soup = BeautifulSoup(html, "html.parser") 123 | ul = soup.find("ul", class_="current") 124 | for a in ul.find_all("a", class_="reference internal"): 125 | # make sure the link is an aws service link 126 | if "#" not in a.attrs["href"]: 127 | href_name = a.attrs["href"] 128 | name = a.text 129 | doc_url = f"https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/{href_name}" 130 | aws_service = AWSService( 131 | name=name, 132 | href_name=href_name, 133 | doc_url=doc_url, 134 | service_id="", 135 | ) 136 | # print(aws_service) 137 | aws_service_list.append(aws_service) 138 | return aws_service_list 139 | 140 | 141 | def get_all_aws_service() -> T.List[AWSService]: 142 | """ 143 | get all AWS Service boto3 api homepage from the boto3 doc homepage, 144 | from its sidebar. 145 | """ 146 | url_available_services = "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/index.html" 147 | html = get_html_with_cache(url_available_services) 148 | aws_service_list = crawl_home_page(html) 149 | return aws_service_list 150 | 151 | 152 | def step1_crawl_spec_file_data(): 153 | # -------------------------------------------------------------------------- 154 | # get all AWS Service boto3 api homepage from the boto3 doc homepage, 155 | # from its sidebar. 156 | # -------------------------------------------------------------------------- 157 | print("get all AWS service ...") 158 | url_available_services = "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/index.html" 159 | html = get_html_with_cache(url_available_services) 160 | aws_service_list = crawl_home_page(html) 161 | print(f"found {len(aws_service_list)} AWS Services that has boto3 API") 162 | 163 | 164 | # -------------------------------------------------------------------------- 165 | # iterate through all service page, and extract service_id 166 | # -------------------------------------------------------------------------- 167 | print("extract service_id ...") 168 | for aws_service in aws_service_list: 169 | print(f"working on {aws_service.doc_url} ...") 170 | html = get_html_with_cache(aws_service.doc_url) 171 | service__id = crawl_service_page(html) 172 | aws_service.service_id = service__id 173 | 174 | # -------------------------------------------------------------------------- 175 | # dump service data 176 | # -------------------------------------------------------------------------- 177 | print("dump service data ...") 178 | spec_file_data: T.List[T.Dict[str, str]] = [ 179 | dataclasses.asdict(aws_service) 180 | for aws_service in aws_service_list 181 | ] 182 | path_spec_file_json.write_text(json.dumps(spec_file_data, indent=4)) 183 | 184 | 185 | # import mypy_boto3_s3 186 | # python -m pip install 'boto3-stubs-lite[essential]'` 187 | import boto3 188 | 189 | s3_client = boto3.session.Session().client("s3") 190 | 191 | 192 | def step2_generate_code(): 193 | spec_file_data = json.loads(path_spec_file_json.read_text()) 194 | 195 | # Generate services.py and clients.py 196 | services_py_lines = [ 197 | "# -*- coding: utf-8 -*-", 198 | "", 199 | "", 200 | "class AwsServiceEnum:", 201 | ] 202 | clients_py_lines = [ 203 | "# -*- coding: utf-8 -*-", 204 | "", 205 | "import typing as T", 206 | "", 207 | "from .services import AwsServiceEnum", 208 | "", 209 | "if T.TYPE_CHECKING:", 210 | " from .manager import BotoSesManager", 211 | "", 212 | "class ClientMixin:", 213 | ] 214 | 215 | aws_service_list = [ 216 | AWSService(**dct) 217 | for dct in spec_file_data 218 | ] 219 | 220 | # Generate clients.py 221 | # clients_py_lines.extend( 222 | # [ 223 | # f" @property", 224 | # f' def {name_snake_case}_client(self: "BotoSesManager"):', 225 | # f' """', 226 | # f" Ref: {doc_url}", 227 | # f' """', 228 | # f" return self.get_client(AwsServiceEnum.{name})", 229 | # f"", 230 | # ] 231 | # ) 232 | # services_py_lines.append("") 233 | 234 | path_services_py.write_text( 235 | jinja2.Template(path_services_template.read_text()).render( 236 | aws_service_list=aws_service_list, 237 | ) 238 | ) 239 | path_clients_py.write_text( 240 | jinja2.Template(path_clients_template.read_text()).render( 241 | aws_service_list=aws_service_list, 242 | ) 243 | ) 244 | # path_clients_py.write_text("\n".join(clients_py_lines)) 245 | 246 | 247 | # step1_crawl_spec_file_data() 248 | step2_generate_code() 249 | -------------------------------------------------------------------------------- /debug/manager.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/boto-session-manager-project/81ee0ac3756d8032570df699d535367b856fcc5e/debug/manager.py -------------------------------------------------------------------------------- /debug/services.jinja2: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class AwsServiceEnum: 5 | {%- for aws_service in aws_service_list %} 6 | {{ aws_service.name }} = "{{ aws_service.service_id }}" 7 | {%- endfor %} 8 | -------------------------------------------------------------------------------- /debug/spec-file.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "AccessAnalyzer", 4 | "href_name": "accessanalyzer.html", 5 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/accessanalyzer.html", 6 | "service_id": "accessanalyzer" 7 | }, 8 | { 9 | "name": "Account", 10 | "href_name": "account.html", 11 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/account.html", 12 | "service_id": "account" 13 | }, 14 | { 15 | "name": "ACM", 16 | "href_name": "acm.html", 17 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/acm.html", 18 | "service_id": "acm" 19 | }, 20 | { 21 | "name": "ACMPCA", 22 | "href_name": "acm-pca.html", 23 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/acm-pca.html", 24 | "service_id": "acm-pca" 25 | }, 26 | { 27 | "name": "AlexaForBusiness", 28 | "href_name": "alexaforbusiness.html", 29 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/alexaforbusiness.html", 30 | "service_id": "alexaforbusiness" 31 | }, 32 | { 33 | "name": "PrometheusService", 34 | "href_name": "amp.html", 35 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/amp.html", 36 | "service_id": "amp" 37 | }, 38 | { 39 | "name": "Amplify", 40 | "href_name": "amplify.html", 41 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/amplify.html", 42 | "service_id": "amplify" 43 | }, 44 | { 45 | "name": "AmplifyBackend", 46 | "href_name": "amplifybackend.html", 47 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/amplifybackend.html", 48 | "service_id": "amplifybackend" 49 | }, 50 | { 51 | "name": "AmplifyUIBuilder", 52 | "href_name": "amplifyuibuilder.html", 53 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/amplifyuibuilder.html", 54 | "service_id": "amplifyuibuilder" 55 | }, 56 | { 57 | "name": "APIGateway", 58 | "href_name": "apigateway.html", 59 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/apigateway.html", 60 | "service_id": "apigateway" 61 | }, 62 | { 63 | "name": "ApiGatewayManagementApi", 64 | "href_name": "apigatewaymanagementapi.html", 65 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/apigatewaymanagementapi.html", 66 | "service_id": "apigatewaymanagementapi" 67 | }, 68 | { 69 | "name": "ApiGatewayV2", 70 | "href_name": "apigatewayv2.html", 71 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/apigatewayv2.html", 72 | "service_id": "apigatewayv2" 73 | }, 74 | { 75 | "name": "AppConfig", 76 | "href_name": "appconfig.html", 77 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appconfig.html", 78 | "service_id": "appconfig" 79 | }, 80 | { 81 | "name": "AppConfigData", 82 | "href_name": "appconfigdata.html", 83 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appconfigdata.html", 84 | "service_id": "appconfigdata" 85 | }, 86 | { 87 | "name": "Appflow", 88 | "href_name": "appflow.html", 89 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appflow.html", 90 | "service_id": "appflow" 91 | }, 92 | { 93 | "name": "AppIntegrationsService", 94 | "href_name": "appintegrations.html", 95 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appintegrations.html", 96 | "service_id": "appintegrations" 97 | }, 98 | { 99 | "name": "ApplicationAutoScaling", 100 | "href_name": "application-autoscaling.html", 101 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/application-autoscaling.html", 102 | "service_id": "application-autoscaling" 103 | }, 104 | { 105 | "name": "ApplicationInsights", 106 | "href_name": "application-insights.html", 107 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/application-insights.html", 108 | "service_id": "application-insights" 109 | }, 110 | { 111 | "name": "ApplicationCostProfiler", 112 | "href_name": "applicationcostprofiler.html", 113 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/applicationcostprofiler.html", 114 | "service_id": "applicationcostprofiler" 115 | }, 116 | { 117 | "name": "AppMesh", 118 | "href_name": "appmesh.html", 119 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appmesh.html", 120 | "service_id": "appmesh" 121 | }, 122 | { 123 | "name": "AppRunner", 124 | "href_name": "apprunner.html", 125 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/apprunner.html", 126 | "service_id": "apprunner" 127 | }, 128 | { 129 | "name": "AppStream", 130 | "href_name": "appstream.html", 131 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appstream.html", 132 | "service_id": "appstream" 133 | }, 134 | { 135 | "name": "AppSync", 136 | "href_name": "appsync.html", 137 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appsync.html", 138 | "service_id": "appsync" 139 | }, 140 | { 141 | "name": "ARCZonalShift", 142 | "href_name": "arc-zonal-shift.html", 143 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/arc-zonal-shift.html", 144 | "service_id": "arc-zonal-shift" 145 | }, 146 | { 147 | "name": "Athena", 148 | "href_name": "athena.html", 149 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/athena.html", 150 | "service_id": "athena" 151 | }, 152 | { 153 | "name": "AuditManager", 154 | "href_name": "auditmanager.html", 155 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/auditmanager.html", 156 | "service_id": "auditmanager" 157 | }, 158 | { 159 | "name": "AutoScaling", 160 | "href_name": "autoscaling.html", 161 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/autoscaling.html", 162 | "service_id": "autoscaling" 163 | }, 164 | { 165 | "name": "AutoScalingPlans", 166 | "href_name": "autoscaling-plans.html", 167 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/autoscaling-plans.html", 168 | "service_id": "autoscaling-plans" 169 | }, 170 | { 171 | "name": "Backup", 172 | "href_name": "backup.html", 173 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backup.html", 174 | "service_id": "backup" 175 | }, 176 | { 177 | "name": "BackupGateway", 178 | "href_name": "backup-gateway.html", 179 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backup-gateway.html", 180 | "service_id": "backup-gateway" 181 | }, 182 | { 183 | "name": "BackupStorage", 184 | "href_name": "backupstorage.html", 185 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/backupstorage.html", 186 | "service_id": "backupstorage" 187 | }, 188 | { 189 | "name": "Batch", 190 | "href_name": "batch.html", 191 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/batch.html", 192 | "service_id": "batch" 193 | }, 194 | { 195 | "name": "BillingConductor", 196 | "href_name": "billingconductor.html", 197 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/billingconductor.html", 198 | "service_id": "billingconductor" 199 | }, 200 | { 201 | "name": "Braket", 202 | "href_name": "braket.html", 203 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/braket.html", 204 | "service_id": "braket" 205 | }, 206 | { 207 | "name": "Budgets", 208 | "href_name": "budgets.html", 209 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/budgets.html", 210 | "service_id": "budgets" 211 | }, 212 | { 213 | "name": "CostExplorer", 214 | "href_name": "ce.html", 215 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ce.html", 216 | "service_id": "ce" 217 | }, 218 | { 219 | "name": "Chime", 220 | "href_name": "chime.html", 221 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/chime.html", 222 | "service_id": "chime" 223 | }, 224 | { 225 | "name": "ChimeSDKIdentity", 226 | "href_name": "chime-sdk-identity.html", 227 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/chime-sdk-identity.html", 228 | "service_id": "chime-sdk-identity" 229 | }, 230 | { 231 | "name": "ChimeSDKMediaPipelines", 232 | "href_name": "chime-sdk-media-pipelines.html", 233 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/chime-sdk-media-pipelines.html", 234 | "service_id": "chime-sdk-media-pipelines" 235 | }, 236 | { 237 | "name": "ChimeSDKMeetings", 238 | "href_name": "chime-sdk-meetings.html", 239 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/chime-sdk-meetings.html", 240 | "service_id": "chime-sdk-meetings" 241 | }, 242 | { 243 | "name": "ChimeSDKMessaging", 244 | "href_name": "chime-sdk-messaging.html", 245 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/chime-sdk-messaging.html", 246 | "service_id": "chime-sdk-messaging" 247 | }, 248 | { 249 | "name": "ChimeSDKVoice", 250 | "href_name": "chime-sdk-voice.html", 251 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/chime-sdk-voice.html", 252 | "service_id": "chime-sdk-voice" 253 | }, 254 | { 255 | "name": "CleanRoomsService", 256 | "href_name": "cleanrooms.html", 257 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cleanrooms.html", 258 | "service_id": "cleanrooms" 259 | }, 260 | { 261 | "name": "Cloud9", 262 | "href_name": "cloud9.html", 263 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloud9.html", 264 | "service_id": "cloud9" 265 | }, 266 | { 267 | "name": "CloudControlApi", 268 | "href_name": "cloudcontrol.html", 269 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudcontrol.html", 270 | "service_id": "cloudcontrol" 271 | }, 272 | { 273 | "name": "CloudDirectory", 274 | "href_name": "clouddirectory.html", 275 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/clouddirectory.html", 276 | "service_id": "clouddirectory" 277 | }, 278 | { 279 | "name": "CloudFormation", 280 | "href_name": "cloudformation.html", 281 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudformation.html", 282 | "service_id": "cloudformation" 283 | }, 284 | { 285 | "name": "CloudFront", 286 | "href_name": "cloudfront.html", 287 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudfront.html", 288 | "service_id": "cloudfront" 289 | }, 290 | { 291 | "name": "CloudHSM", 292 | "href_name": "cloudhsm.html", 293 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudhsm.html", 294 | "service_id": "cloudhsm" 295 | }, 296 | { 297 | "name": "CloudHSMV2", 298 | "href_name": "cloudhsmv2.html", 299 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudhsmv2.html", 300 | "service_id": "cloudhsmv2" 301 | }, 302 | { 303 | "name": "CloudSearch", 304 | "href_name": "cloudsearch.html", 305 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudsearch.html", 306 | "service_id": "cloudsearch" 307 | }, 308 | { 309 | "name": "CloudSearchDomain", 310 | "href_name": "cloudsearchdomain.html", 311 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudsearchdomain.html", 312 | "service_id": "cloudsearchdomain" 313 | }, 314 | { 315 | "name": "CloudTrail", 316 | "href_name": "cloudtrail.html", 317 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudtrail.html", 318 | "service_id": "cloudtrail" 319 | }, 320 | { 321 | "name": "CloudTrailDataService", 322 | "href_name": "cloudtrail-data.html", 323 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudtrail-data.html", 324 | "service_id": "cloudtrail-data" 325 | }, 326 | { 327 | "name": "CloudWatch", 328 | "href_name": "cloudwatch.html", 329 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html", 330 | "service_id": "cloudwatch" 331 | }, 332 | { 333 | "name": "CodeArtifact", 334 | "href_name": "codeartifact.html", 335 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codeartifact.html", 336 | "service_id": "codeartifact" 337 | }, 338 | { 339 | "name": "CodeBuild", 340 | "href_name": "codebuild.html", 341 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codebuild.html", 342 | "service_id": "codebuild" 343 | }, 344 | { 345 | "name": "CodeCatalyst", 346 | "href_name": "codecatalyst.html", 347 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecatalyst.html", 348 | "service_id": "codecatalyst" 349 | }, 350 | { 351 | "name": "CodeCommit", 352 | "href_name": "codecommit.html", 353 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codecommit.html", 354 | "service_id": "codecommit" 355 | }, 356 | { 357 | "name": "CodeDeploy", 358 | "href_name": "codedeploy.html", 359 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codedeploy.html", 360 | "service_id": "codedeploy" 361 | }, 362 | { 363 | "name": "CodeGuruReviewer", 364 | "href_name": "codeguru-reviewer.html", 365 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codeguru-reviewer.html", 366 | "service_id": "codeguru-reviewer" 367 | }, 368 | { 369 | "name": "CodeGuruProfiler", 370 | "href_name": "codeguruprofiler.html", 371 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codeguruprofiler.html", 372 | "service_id": "codeguruprofiler" 373 | }, 374 | { 375 | "name": "CodePipeline", 376 | "href_name": "codepipeline.html", 377 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codepipeline.html", 378 | "service_id": "codepipeline" 379 | }, 380 | { 381 | "name": "CodeStar", 382 | "href_name": "codestar.html", 383 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codestar.html", 384 | "service_id": "codestar" 385 | }, 386 | { 387 | "name": "CodeStarconnections", 388 | "href_name": "codestar-connections.html", 389 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codestar-connections.html", 390 | "service_id": "codestar-connections" 391 | }, 392 | { 393 | "name": "CodeStarNotifications", 394 | "href_name": "codestar-notifications.html", 395 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codestar-notifications.html", 396 | "service_id": "codestar-notifications" 397 | }, 398 | { 399 | "name": "CognitoIdentity", 400 | "href_name": "cognito-identity.html", 401 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-identity.html", 402 | "service_id": "cognito-identity" 403 | }, 404 | { 405 | "name": "CognitoIdentityProvider", 406 | "href_name": "cognito-idp.html", 407 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp.html", 408 | "service_id": "cognito-idp" 409 | }, 410 | { 411 | "name": "CognitoSync", 412 | "href_name": "cognito-sync.html", 413 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-sync.html", 414 | "service_id": "cognito-sync" 415 | }, 416 | { 417 | "name": "Comprehend", 418 | "href_name": "comprehend.html", 419 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/comprehend.html", 420 | "service_id": "comprehend" 421 | }, 422 | { 423 | "name": "ComprehendMedical", 424 | "href_name": "comprehendmedical.html", 425 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/comprehendmedical.html", 426 | "service_id": "comprehendmedical" 427 | }, 428 | { 429 | "name": "ComputeOptimizer", 430 | "href_name": "compute-optimizer.html", 431 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/compute-optimizer.html", 432 | "service_id": "compute-optimizer" 433 | }, 434 | { 435 | "name": "ConfigService", 436 | "href_name": "config.html", 437 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/config.html", 438 | "service_id": "config" 439 | }, 440 | { 441 | "name": "Connect", 442 | "href_name": "connect.html", 443 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/connect.html", 444 | "service_id": "connect" 445 | }, 446 | { 447 | "name": "ConnectContactLens", 448 | "href_name": "connect-contact-lens.html", 449 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/connect-contact-lens.html", 450 | "service_id": "connect-contact-lens" 451 | }, 452 | { 453 | "name": "ConnectCampaignService", 454 | "href_name": "connectcampaigns.html", 455 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/connectcampaigns.html", 456 | "service_id": "connectcampaigns" 457 | }, 458 | { 459 | "name": "ConnectCases", 460 | "href_name": "connectcases.html", 461 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/connectcases.html", 462 | "service_id": "connectcases" 463 | }, 464 | { 465 | "name": "ConnectParticipant", 466 | "href_name": "connectparticipant.html", 467 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/connectparticipant.html", 468 | "service_id": "connectparticipant" 469 | }, 470 | { 471 | "name": "ControlTower", 472 | "href_name": "controltower.html", 473 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/controltower.html", 474 | "service_id": "controltower" 475 | }, 476 | { 477 | "name": "CostandUsageReportService", 478 | "href_name": "cur.html", 479 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cur.html", 480 | "service_id": "cur" 481 | }, 482 | { 483 | "name": "CustomerProfiles", 484 | "href_name": "customer-profiles.html", 485 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/customer-profiles.html", 486 | "service_id": "customer-profiles" 487 | }, 488 | { 489 | "name": "GlueDataBrew", 490 | "href_name": "databrew.html", 491 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/databrew.html", 492 | "service_id": "databrew" 493 | }, 494 | { 495 | "name": "DataExchange", 496 | "href_name": "dataexchange.html", 497 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dataexchange.html", 498 | "service_id": "dataexchange" 499 | }, 500 | { 501 | "name": "DataPipeline", 502 | "href_name": "datapipeline.html", 503 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/datapipeline.html", 504 | "service_id": "datapipeline" 505 | }, 506 | { 507 | "name": "DataSync", 508 | "href_name": "datasync.html", 509 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/datasync.html", 510 | "service_id": "datasync" 511 | }, 512 | { 513 | "name": "DAX", 514 | "href_name": "dax.html", 515 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dax.html", 516 | "service_id": "dax" 517 | }, 518 | { 519 | "name": "Detective", 520 | "href_name": "detective.html", 521 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/detective.html", 522 | "service_id": "detective" 523 | }, 524 | { 525 | "name": "DeviceFarm", 526 | "href_name": "devicefarm.html", 527 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/devicefarm.html", 528 | "service_id": "devicefarm" 529 | }, 530 | { 531 | "name": "DevOpsGuru", 532 | "href_name": "devops-guru.html", 533 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/devops-guru.html", 534 | "service_id": "devops-guru" 535 | }, 536 | { 537 | "name": "DirectConnect", 538 | "href_name": "directconnect.html", 539 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/directconnect.html", 540 | "service_id": "directconnect" 541 | }, 542 | { 543 | "name": "ApplicationDiscoveryService", 544 | "href_name": "discovery.html", 545 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/discovery.html", 546 | "service_id": "discovery" 547 | }, 548 | { 549 | "name": "DLM", 550 | "href_name": "dlm.html", 551 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dlm.html", 552 | "service_id": "dlm" 553 | }, 554 | { 555 | "name": "DatabaseMigrationService", 556 | "href_name": "dms.html", 557 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dms.html", 558 | "service_id": "dms" 559 | }, 560 | { 561 | "name": "DocDB", 562 | "href_name": "docdb.html", 563 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/docdb.html", 564 | "service_id": "docdb" 565 | }, 566 | { 567 | "name": "DocDBElastic", 568 | "href_name": "docdb-elastic.html", 569 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/docdb-elastic.html", 570 | "service_id": "docdb-elastic" 571 | }, 572 | { 573 | "name": "drs", 574 | "href_name": "drs.html", 575 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/drs.html", 576 | "service_id": "drs" 577 | }, 578 | { 579 | "name": "DirectoryService", 580 | "href_name": "ds.html", 581 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ds.html", 582 | "service_id": "ds" 583 | }, 584 | { 585 | "name": "DynamoDB", 586 | "href_name": "dynamodb.html", 587 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html", 588 | "service_id": "dynamodb" 589 | }, 590 | { 591 | "name": "DynamoDBStreams", 592 | "href_name": "dynamodbstreams.html", 593 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodbstreams.html", 594 | "service_id": "dynamodbstreams" 595 | }, 596 | { 597 | "name": "EBS", 598 | "href_name": "ebs.html", 599 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ebs.html", 600 | "service_id": "ebs" 601 | }, 602 | { 603 | "name": "EC2", 604 | "href_name": "ec2.html", 605 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html", 606 | "service_id": "ec2" 607 | }, 608 | { 609 | "name": "EC2InstanceConnect", 610 | "href_name": "ec2-instance-connect.html", 611 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2-instance-connect.html", 612 | "service_id": "ec2-instance-connect" 613 | }, 614 | { 615 | "name": "ECR", 616 | "href_name": "ecr.html", 617 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecr.html", 618 | "service_id": "ecr" 619 | }, 620 | { 621 | "name": "ECRPublic", 622 | "href_name": "ecr-public.html", 623 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecr-public.html", 624 | "service_id": "ecr-public" 625 | }, 626 | { 627 | "name": "ECS", 628 | "href_name": "ecs.html", 629 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecs.html", 630 | "service_id": "ecs" 631 | }, 632 | { 633 | "name": "EFS", 634 | "href_name": "efs.html", 635 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/efs.html", 636 | "service_id": "efs" 637 | }, 638 | { 639 | "name": "EKS", 640 | "href_name": "eks.html", 641 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/eks.html", 642 | "service_id": "eks" 643 | }, 644 | { 645 | "name": "ElasticInference", 646 | "href_name": "elastic-inference.html", 647 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elastic-inference.html", 648 | "service_id": "elastic-inference" 649 | }, 650 | { 651 | "name": "ElastiCache", 652 | "href_name": "elasticache.html", 653 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elasticache.html", 654 | "service_id": "elasticache" 655 | }, 656 | { 657 | "name": "ElasticBeanstalk", 658 | "href_name": "elasticbeanstalk.html", 659 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elasticbeanstalk.html", 660 | "service_id": "elasticbeanstalk" 661 | }, 662 | { 663 | "name": "ElasticTranscoder", 664 | "href_name": "elastictranscoder.html", 665 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elastictranscoder.html", 666 | "service_id": "elastictranscoder" 667 | }, 668 | { 669 | "name": "ElasticLoadBalancing", 670 | "href_name": "elb.html", 671 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elb.html", 672 | "service_id": "elb" 673 | }, 674 | { 675 | "name": "ElasticLoadBalancingv2", 676 | "href_name": "elbv2.html", 677 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/elbv2.html", 678 | "service_id": "elbv2" 679 | }, 680 | { 681 | "name": "EMR", 682 | "href_name": "emr.html", 683 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/emr.html", 684 | "service_id": "emr" 685 | }, 686 | { 687 | "name": "EMRContainers", 688 | "href_name": "emr-containers.html", 689 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/emr-containers.html", 690 | "service_id": "emr-containers" 691 | }, 692 | { 693 | "name": "EMRServerless", 694 | "href_name": "emr-serverless.html", 695 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/emr-serverless.html", 696 | "service_id": "emr-serverless" 697 | }, 698 | { 699 | "name": "ElasticsearchService", 700 | "href_name": "es.html", 701 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/es.html", 702 | "service_id": "es" 703 | }, 704 | { 705 | "name": "EventBridge", 706 | "href_name": "events.html", 707 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/events.html", 708 | "service_id": "events" 709 | }, 710 | { 711 | "name": "CloudWatchEvidently", 712 | "href_name": "evidently.html", 713 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/evidently.html", 714 | "service_id": "evidently" 715 | }, 716 | { 717 | "name": "finspace", 718 | "href_name": "finspace.html", 719 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/finspace.html", 720 | "service_id": "finspace" 721 | }, 722 | { 723 | "name": "FinSpaceData", 724 | "href_name": "finspace-data.html", 725 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/finspace-data.html", 726 | "service_id": "finspace-data" 727 | }, 728 | { 729 | "name": "Firehose", 730 | "href_name": "firehose.html", 731 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/firehose.html", 732 | "service_id": "firehose" 733 | }, 734 | { 735 | "name": "FIS", 736 | "href_name": "fis.html", 737 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/fis.html", 738 | "service_id": "fis" 739 | }, 740 | { 741 | "name": "FMS", 742 | "href_name": "fms.html", 743 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/fms.html", 744 | "service_id": "fms" 745 | }, 746 | { 747 | "name": "ForecastService", 748 | "href_name": "forecast.html", 749 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/forecast.html", 750 | "service_id": "forecast" 751 | }, 752 | { 753 | "name": "ForecastQueryService", 754 | "href_name": "forecastquery.html", 755 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/forecastquery.html", 756 | "service_id": "forecastquery" 757 | }, 758 | { 759 | "name": "FraudDetector", 760 | "href_name": "frauddetector.html", 761 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/frauddetector.html", 762 | "service_id": "frauddetector" 763 | }, 764 | { 765 | "name": "FSx", 766 | "href_name": "fsx.html", 767 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/fsx.html", 768 | "service_id": "fsx" 769 | }, 770 | { 771 | "name": "GameLift", 772 | "href_name": "gamelift.html", 773 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/gamelift.html", 774 | "service_id": "gamelift" 775 | }, 776 | { 777 | "name": "GameSparks", 778 | "href_name": "gamesparks.html", 779 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/gamesparks.html", 780 | "service_id": "gamesparks" 781 | }, 782 | { 783 | "name": "Glacier", 784 | "href_name": "glacier.html", 785 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/glacier.html", 786 | "service_id": "glacier" 787 | }, 788 | { 789 | "name": "GlobalAccelerator", 790 | "href_name": "globalaccelerator.html", 791 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/globalaccelerator.html", 792 | "service_id": "globalaccelerator" 793 | }, 794 | { 795 | "name": "Glue", 796 | "href_name": "glue.html", 797 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/glue.html", 798 | "service_id": "glue" 799 | }, 800 | { 801 | "name": "ManagedGrafana", 802 | "href_name": "grafana.html", 803 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/grafana.html", 804 | "service_id": "grafana" 805 | }, 806 | { 807 | "name": "Greengrass", 808 | "href_name": "greengrass.html", 809 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/greengrass.html", 810 | "service_id": "greengrass" 811 | }, 812 | { 813 | "name": "GreengrassV2", 814 | "href_name": "greengrassv2.html", 815 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/greengrassv2.html", 816 | "service_id": "greengrassv2" 817 | }, 818 | { 819 | "name": "GroundStation", 820 | "href_name": "groundstation.html", 821 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/groundstation.html", 822 | "service_id": "groundstation" 823 | }, 824 | { 825 | "name": "GuardDuty", 826 | "href_name": "guardduty.html", 827 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/guardduty.html", 828 | "service_id": "guardduty" 829 | }, 830 | { 831 | "name": "Health", 832 | "href_name": "health.html", 833 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/health.html", 834 | "service_id": "health" 835 | }, 836 | { 837 | "name": "HealthLake", 838 | "href_name": "healthlake.html", 839 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/healthlake.html", 840 | "service_id": "healthlake" 841 | }, 842 | { 843 | "name": "Honeycode", 844 | "href_name": "honeycode.html", 845 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/honeycode.html", 846 | "service_id": "honeycode" 847 | }, 848 | { 849 | "name": "IAM", 850 | "href_name": "iam.html", 851 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iam.html", 852 | "service_id": "iam" 853 | }, 854 | { 855 | "name": "IdentityStore", 856 | "href_name": "identitystore.html", 857 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/identitystore.html", 858 | "service_id": "identitystore" 859 | }, 860 | { 861 | "name": "imagebuilder", 862 | "href_name": "imagebuilder.html", 863 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/imagebuilder.html", 864 | "service_id": "imagebuilder" 865 | }, 866 | { 867 | "name": "ImportExport", 868 | "href_name": "importexport.html", 869 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/importexport.html", 870 | "service_id": "importexport" 871 | }, 872 | { 873 | "name": "Inspector", 874 | "href_name": "inspector.html", 875 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/inspector.html", 876 | "service_id": "inspector" 877 | }, 878 | { 879 | "name": "Inspector2", 880 | "href_name": "inspector2.html", 881 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/inspector2.html", 882 | "service_id": "inspector2" 883 | }, 884 | { 885 | "name": "CloudWatchInternetMonitor", 886 | "href_name": "internetmonitor.html", 887 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/internetmonitor.html", 888 | "service_id": "internetmonitor" 889 | }, 890 | { 891 | "name": "IoT", 892 | "href_name": "iot.html", 893 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot.html", 894 | "service_id": "iot" 895 | }, 896 | { 897 | "name": "IoTDataPlane", 898 | "href_name": "iot-data.html", 899 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot-data.html", 900 | "service_id": "iot-data" 901 | }, 902 | { 903 | "name": "IoTJobsDataPlane", 904 | "href_name": "iot-jobs-data.html", 905 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot-jobs-data.html", 906 | "service_id": "iot-jobs-data" 907 | }, 908 | { 909 | "name": "IoTRoboRunner", 910 | "href_name": "iot-roborunner.html", 911 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot-roborunner.html", 912 | "service_id": "iot-roborunner" 913 | }, 914 | { 915 | "name": "IoT1ClickDevicesService", 916 | "href_name": "iot1click-devices.html", 917 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot1click-devices.html", 918 | "service_id": "iot1click-devices" 919 | }, 920 | { 921 | "name": "IoT1ClickProjects", 922 | "href_name": "iot1click-projects.html", 923 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot1click-projects.html", 924 | "service_id": "iot1click-projects" 925 | }, 926 | { 927 | "name": "IoTAnalytics", 928 | "href_name": "iotanalytics.html", 929 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotanalytics.html", 930 | "service_id": "iotanalytics" 931 | }, 932 | { 933 | "name": "IoTDeviceAdvisor", 934 | "href_name": "iotdeviceadvisor.html", 935 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotdeviceadvisor.html", 936 | "service_id": "iotdeviceadvisor" 937 | }, 938 | { 939 | "name": "IoTEvents", 940 | "href_name": "iotevents.html", 941 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotevents.html", 942 | "service_id": "iotevents" 943 | }, 944 | { 945 | "name": "IoTEventsData", 946 | "href_name": "iotevents-data.html", 947 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotevents-data.html", 948 | "service_id": "iotevents-data" 949 | }, 950 | { 951 | "name": "IoTFleetHub", 952 | "href_name": "iotfleethub.html", 953 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotfleethub.html", 954 | "service_id": "iotfleethub" 955 | }, 956 | { 957 | "name": "IoTFleetWise", 958 | "href_name": "iotfleetwise.html", 959 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotfleetwise.html", 960 | "service_id": "iotfleetwise" 961 | }, 962 | { 963 | "name": "IoTSecureTunneling", 964 | "href_name": "iotsecuretunneling.html", 965 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotsecuretunneling.html", 966 | "service_id": "iotsecuretunneling" 967 | }, 968 | { 969 | "name": "IoTSiteWise", 970 | "href_name": "iotsitewise.html", 971 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotsitewise.html", 972 | "service_id": "iotsitewise" 973 | }, 974 | { 975 | "name": "IoTThingsGraph", 976 | "href_name": "iotthingsgraph.html", 977 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotthingsgraph.html", 978 | "service_id": "iotthingsgraph" 979 | }, 980 | { 981 | "name": "IoTTwinMaker", 982 | "href_name": "iottwinmaker.html", 983 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iottwinmaker.html", 984 | "service_id": "iottwinmaker" 985 | }, 986 | { 987 | "name": "IoTWireless", 988 | "href_name": "iotwireless.html", 989 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iotwireless.html", 990 | "service_id": "iotwireless" 991 | }, 992 | { 993 | "name": "IVS", 994 | "href_name": "ivs.html", 995 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ivs.html", 996 | "service_id": "ivs" 997 | }, 998 | { 999 | "name": "ivschat", 1000 | "href_name": "ivschat.html", 1001 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ivschat.html", 1002 | "service_id": "ivschat" 1003 | }, 1004 | { 1005 | "name": "Kafka", 1006 | "href_name": "kafka.html", 1007 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kafka.html", 1008 | "service_id": "kafka" 1009 | }, 1010 | { 1011 | "name": "KafkaConnect", 1012 | "href_name": "kafkaconnect.html", 1013 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kafkaconnect.html", 1014 | "service_id": "kafkaconnect" 1015 | }, 1016 | { 1017 | "name": "kendra", 1018 | "href_name": "kendra.html", 1019 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kendra.html", 1020 | "service_id": "kendra" 1021 | }, 1022 | { 1023 | "name": "KendraRanking", 1024 | "href_name": "kendra-ranking.html", 1025 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kendra-ranking.html", 1026 | "service_id": "kendra-ranking" 1027 | }, 1028 | { 1029 | "name": "Keyspaces", 1030 | "href_name": "keyspaces.html", 1031 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/keyspaces.html", 1032 | "service_id": "keyspaces" 1033 | }, 1034 | { 1035 | "name": "Kinesis", 1036 | "href_name": "kinesis.html", 1037 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesis.html", 1038 | "service_id": "kinesis" 1039 | }, 1040 | { 1041 | "name": "KinesisVideoArchivedMedia", 1042 | "href_name": "kinesis-video-archived-media.html", 1043 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesis-video-archived-media.html", 1044 | "service_id": "kinesis-video-archived-media" 1045 | }, 1046 | { 1047 | "name": "KinesisVideoMedia", 1048 | "href_name": "kinesis-video-media.html", 1049 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesis-video-media.html", 1050 | "service_id": "kinesis-video-media" 1051 | }, 1052 | { 1053 | "name": "KinesisVideoSignalingChannels", 1054 | "href_name": "kinesis-video-signaling.html", 1055 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesis-video-signaling.html", 1056 | "service_id": "kinesis-video-signaling" 1057 | }, 1058 | { 1059 | "name": "KinesisVideoWebRTCStorage", 1060 | "href_name": "kinesis-video-webrtc-storage.html", 1061 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesis-video-webrtc-storage.html", 1062 | "service_id": "kinesis-video-webrtc-storage" 1063 | }, 1064 | { 1065 | "name": "KinesisAnalytics", 1066 | "href_name": "kinesisanalytics.html", 1067 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesisanalytics.html", 1068 | "service_id": "kinesisanalytics" 1069 | }, 1070 | { 1071 | "name": "KinesisAnalyticsV2", 1072 | "href_name": "kinesisanalyticsv2.html", 1073 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesisanalyticsv2.html", 1074 | "service_id": "kinesisanalyticsv2" 1075 | }, 1076 | { 1077 | "name": "KinesisVideo", 1078 | "href_name": "kinesisvideo.html", 1079 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kinesisvideo.html", 1080 | "service_id": "kinesisvideo" 1081 | }, 1082 | { 1083 | "name": "KMS", 1084 | "href_name": "kms.html", 1085 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/kms.html", 1086 | "service_id": "kms" 1087 | }, 1088 | { 1089 | "name": "LakeFormation", 1090 | "href_name": "lakeformation.html", 1091 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lakeformation.html", 1092 | "service_id": "lakeformation" 1093 | }, 1094 | { 1095 | "name": "Lambda", 1096 | "href_name": "lambda.html", 1097 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lambda.html", 1098 | "service_id": "lambda" 1099 | }, 1100 | { 1101 | "name": "LexModelBuildingService", 1102 | "href_name": "lex-models.html", 1103 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lex-models.html", 1104 | "service_id": "lex-models" 1105 | }, 1106 | { 1107 | "name": "LexRuntimeService", 1108 | "href_name": "lex-runtime.html", 1109 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lex-runtime.html", 1110 | "service_id": "lex-runtime" 1111 | }, 1112 | { 1113 | "name": "LexModelsV2", 1114 | "href_name": "lexv2-models.html", 1115 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lexv2-models.html", 1116 | "service_id": "lexv2-models" 1117 | }, 1118 | { 1119 | "name": "LexRuntimeV2", 1120 | "href_name": "lexv2-runtime.html", 1121 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lexv2-runtime.html", 1122 | "service_id": "lexv2-runtime" 1123 | }, 1124 | { 1125 | "name": "LicenseManager", 1126 | "href_name": "license-manager.html", 1127 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/license-manager.html", 1128 | "service_id": "license-manager" 1129 | }, 1130 | { 1131 | "name": "LicenseManagerLinuxSubscriptions", 1132 | "href_name": "license-manager-linux-subscriptions.html", 1133 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/license-manager-linux-subscriptions.html", 1134 | "service_id": "license-manager-linux-subscriptions" 1135 | }, 1136 | { 1137 | "name": "LicenseManagerUserSubscriptions", 1138 | "href_name": "license-manager-user-subscriptions.html", 1139 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/license-manager-user-subscriptions.html", 1140 | "service_id": "license-manager-user-subscriptions" 1141 | }, 1142 | { 1143 | "name": "Lightsail", 1144 | "href_name": "lightsail.html", 1145 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lightsail.html", 1146 | "service_id": "lightsail" 1147 | }, 1148 | { 1149 | "name": "LocationService", 1150 | "href_name": "location.html", 1151 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/location.html", 1152 | "service_id": "location" 1153 | }, 1154 | { 1155 | "name": "CloudWatchLogs", 1156 | "href_name": "logs.html", 1157 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/logs.html", 1158 | "service_id": "logs" 1159 | }, 1160 | { 1161 | "name": "LookoutEquipment", 1162 | "href_name": "lookoutequipment.html", 1163 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lookoutequipment.html", 1164 | "service_id": "lookoutequipment" 1165 | }, 1166 | { 1167 | "name": "LookoutMetrics", 1168 | "href_name": "lookoutmetrics.html", 1169 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lookoutmetrics.html", 1170 | "service_id": "lookoutmetrics" 1171 | }, 1172 | { 1173 | "name": "LookoutforVision", 1174 | "href_name": "lookoutvision.html", 1175 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/lookoutvision.html", 1176 | "service_id": "lookoutvision" 1177 | }, 1178 | { 1179 | "name": "MainframeModernization", 1180 | "href_name": "m2.html", 1181 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/m2.html", 1182 | "service_id": "m2" 1183 | }, 1184 | { 1185 | "name": "MachineLearning", 1186 | "href_name": "machinelearning.html", 1187 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/machinelearning.html", 1188 | "service_id": "machinelearning" 1189 | }, 1190 | { 1191 | "name": "Macie", 1192 | "href_name": "macie.html", 1193 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/macie.html", 1194 | "service_id": "macie" 1195 | }, 1196 | { 1197 | "name": "Macie2", 1198 | "href_name": "macie2.html", 1199 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/macie2.html", 1200 | "service_id": "macie2" 1201 | }, 1202 | { 1203 | "name": "ManagedBlockchain", 1204 | "href_name": "managedblockchain.html", 1205 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/managedblockchain.html", 1206 | "service_id": "managedblockchain" 1207 | }, 1208 | { 1209 | "name": "MarketplaceCatalog", 1210 | "href_name": "marketplace-catalog.html", 1211 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/marketplace-catalog.html", 1212 | "service_id": "marketplace-catalog" 1213 | }, 1214 | { 1215 | "name": "MarketplaceEntitlementService", 1216 | "href_name": "marketplace-entitlement.html", 1217 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/marketplace-entitlement.html", 1218 | "service_id": "marketplace-entitlement" 1219 | }, 1220 | { 1221 | "name": "MarketplaceCommerceAnalytics", 1222 | "href_name": "marketplacecommerceanalytics.html", 1223 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/marketplacecommerceanalytics.html", 1224 | "service_id": "marketplacecommerceanalytics" 1225 | }, 1226 | { 1227 | "name": "MediaConnect", 1228 | "href_name": "mediaconnect.html", 1229 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediaconnect.html", 1230 | "service_id": "mediaconnect" 1231 | }, 1232 | { 1233 | "name": "MediaConvert", 1234 | "href_name": "mediaconvert.html", 1235 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediaconvert.html", 1236 | "service_id": "mediaconvert" 1237 | }, 1238 | { 1239 | "name": "MediaLive", 1240 | "href_name": "medialive.html", 1241 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/medialive.html", 1242 | "service_id": "medialive" 1243 | }, 1244 | { 1245 | "name": "MediaPackage", 1246 | "href_name": "mediapackage.html", 1247 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediapackage.html", 1248 | "service_id": "mediapackage" 1249 | }, 1250 | { 1251 | "name": "MediaPackageVod", 1252 | "href_name": "mediapackage-vod.html", 1253 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediapackage-vod.html", 1254 | "service_id": "mediapackage-vod" 1255 | }, 1256 | { 1257 | "name": "MediaStore", 1258 | "href_name": "mediastore.html", 1259 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediastore.html", 1260 | "service_id": "mediastore" 1261 | }, 1262 | { 1263 | "name": "MediaStoreData", 1264 | "href_name": "mediastore-data.html", 1265 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediastore-data.html", 1266 | "service_id": "mediastore-data" 1267 | }, 1268 | { 1269 | "name": "MediaTailor", 1270 | "href_name": "mediatailor.html", 1271 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediatailor.html", 1272 | "service_id": "mediatailor" 1273 | }, 1274 | { 1275 | "name": "MemoryDB", 1276 | "href_name": "memorydb.html", 1277 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/memorydb.html", 1278 | "service_id": "memorydb" 1279 | }, 1280 | { 1281 | "name": "MarketplaceMetering", 1282 | "href_name": "meteringmarketplace.html", 1283 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/meteringmarketplace.html", 1284 | "service_id": "meteringmarketplace" 1285 | }, 1286 | { 1287 | "name": "MigrationHub", 1288 | "href_name": "mgh.html", 1289 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mgh.html", 1290 | "service_id": "mgh" 1291 | }, 1292 | { 1293 | "name": "mgn", 1294 | "href_name": "mgn.html", 1295 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mgn.html", 1296 | "service_id": "mgn" 1297 | }, 1298 | { 1299 | "name": "MigrationHubRefactorSpaces", 1300 | "href_name": "migration-hub-refactor-spaces.html", 1301 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/migration-hub-refactor-spaces.html", 1302 | "service_id": "migration-hub-refactor-spaces" 1303 | }, 1304 | { 1305 | "name": "MigrationHubConfig", 1306 | "href_name": "migrationhub-config.html", 1307 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/migrationhub-config.html", 1308 | "service_id": "migrationhub-config" 1309 | }, 1310 | { 1311 | "name": "MigrationHubOrchestrator", 1312 | "href_name": "migrationhuborchestrator.html", 1313 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/migrationhuborchestrator.html", 1314 | "service_id": "migrationhuborchestrator" 1315 | }, 1316 | { 1317 | "name": "MigrationHubStrategyRecommendations", 1318 | "href_name": "migrationhubstrategy.html", 1319 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/migrationhubstrategy.html", 1320 | "service_id": "migrationhubstrategy" 1321 | }, 1322 | { 1323 | "name": "Mobile", 1324 | "href_name": "mobile.html", 1325 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mobile.html", 1326 | "service_id": "mobile" 1327 | }, 1328 | { 1329 | "name": "MQ", 1330 | "href_name": "mq.html", 1331 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mq.html", 1332 | "service_id": "mq" 1333 | }, 1334 | { 1335 | "name": "MTurk", 1336 | "href_name": "mturk.html", 1337 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mturk.html", 1338 | "service_id": "mturk" 1339 | }, 1340 | { 1341 | "name": "MWAA", 1342 | "href_name": "mwaa.html", 1343 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mwaa.html", 1344 | "service_id": "mwaa" 1345 | }, 1346 | { 1347 | "name": "Neptune", 1348 | "href_name": "neptune.html", 1349 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/neptune.html", 1350 | "service_id": "neptune" 1351 | }, 1352 | { 1353 | "name": "NetworkFirewall", 1354 | "href_name": "network-firewall.html", 1355 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/network-firewall.html", 1356 | "service_id": "network-firewall" 1357 | }, 1358 | { 1359 | "name": "NetworkManager", 1360 | "href_name": "networkmanager.html", 1361 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/networkmanager.html", 1362 | "service_id": "networkmanager" 1363 | }, 1364 | { 1365 | "name": "NimbleStudio", 1366 | "href_name": "nimble.html", 1367 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/nimble.html", 1368 | "service_id": "nimble" 1369 | }, 1370 | { 1371 | "name": "CloudWatchObservabilityAccessManager", 1372 | "href_name": "oam.html", 1373 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/oam.html", 1374 | "service_id": "oam" 1375 | }, 1376 | { 1377 | "name": "Omics", 1378 | "href_name": "omics.html", 1379 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/omics.html", 1380 | "service_id": "omics" 1381 | }, 1382 | { 1383 | "name": "OpenSearchService", 1384 | "href_name": "opensearch.html", 1385 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/opensearch.html", 1386 | "service_id": "opensearch" 1387 | }, 1388 | { 1389 | "name": "OpenSearchServiceServerless", 1390 | "href_name": "opensearchserverless.html", 1391 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/opensearchserverless.html", 1392 | "service_id": "opensearchserverless" 1393 | }, 1394 | { 1395 | "name": "OpsWorks", 1396 | "href_name": "opsworks.html", 1397 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/opsworks.html", 1398 | "service_id": "opsworks" 1399 | }, 1400 | { 1401 | "name": "OpsWorksCM", 1402 | "href_name": "opsworkscm.html", 1403 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/opsworkscm.html", 1404 | "service_id": "opsworkscm" 1405 | }, 1406 | { 1407 | "name": "Organizations", 1408 | "href_name": "organizations.html", 1409 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/organizations.html", 1410 | "service_id": "organizations" 1411 | }, 1412 | { 1413 | "name": "Outposts", 1414 | "href_name": "outposts.html", 1415 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/outposts.html", 1416 | "service_id": "outposts" 1417 | }, 1418 | { 1419 | "name": "Panorama", 1420 | "href_name": "panorama.html", 1421 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/panorama.html", 1422 | "service_id": "panorama" 1423 | }, 1424 | { 1425 | "name": "Personalize", 1426 | "href_name": "personalize.html", 1427 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/personalize.html", 1428 | "service_id": "personalize" 1429 | }, 1430 | { 1431 | "name": "PersonalizeEvents", 1432 | "href_name": "personalize-events.html", 1433 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/personalize-events.html", 1434 | "service_id": "personalize-events" 1435 | }, 1436 | { 1437 | "name": "PersonalizeRuntime", 1438 | "href_name": "personalize-runtime.html", 1439 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/personalize-runtime.html", 1440 | "service_id": "personalize-runtime" 1441 | }, 1442 | { 1443 | "name": "PI", 1444 | "href_name": "pi.html", 1445 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pi.html", 1446 | "service_id": "pi" 1447 | }, 1448 | { 1449 | "name": "Pinpoint", 1450 | "href_name": "pinpoint.html", 1451 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pinpoint.html", 1452 | "service_id": "pinpoint" 1453 | }, 1454 | { 1455 | "name": "PinpointEmail", 1456 | "href_name": "pinpoint-email.html", 1457 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pinpoint-email.html", 1458 | "service_id": "pinpoint-email" 1459 | }, 1460 | { 1461 | "name": "PinpointSMSVoice", 1462 | "href_name": "pinpoint-sms-voice.html", 1463 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pinpoint-sms-voice.html", 1464 | "service_id": "pinpoint-sms-voice" 1465 | }, 1466 | { 1467 | "name": "PinpointSMSVoiceV2", 1468 | "href_name": "pinpoint-sms-voice-v2.html", 1469 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pinpoint-sms-voice-v2.html", 1470 | "service_id": "pinpoint-sms-voice-v2" 1471 | }, 1472 | { 1473 | "name": "EventBridgePipes", 1474 | "href_name": "pipes.html", 1475 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pipes.html", 1476 | "service_id": "pipes" 1477 | }, 1478 | { 1479 | "name": "Polly", 1480 | "href_name": "polly.html", 1481 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/polly.html", 1482 | "service_id": "polly" 1483 | }, 1484 | { 1485 | "name": "Pricing", 1486 | "href_name": "pricing.html", 1487 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/pricing.html", 1488 | "service_id": "pricing" 1489 | }, 1490 | { 1491 | "name": "Private5G", 1492 | "href_name": "privatenetworks.html", 1493 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/privatenetworks.html", 1494 | "service_id": "privatenetworks" 1495 | }, 1496 | { 1497 | "name": "Proton", 1498 | "href_name": "proton.html", 1499 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/proton.html", 1500 | "service_id": "proton" 1501 | }, 1502 | { 1503 | "name": "QLDB", 1504 | "href_name": "qldb.html", 1505 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/qldb.html", 1506 | "service_id": "qldb" 1507 | }, 1508 | { 1509 | "name": "QLDBSession", 1510 | "href_name": "qldb-session.html", 1511 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/qldb-session.html", 1512 | "service_id": "qldb-session" 1513 | }, 1514 | { 1515 | "name": "QuickSight", 1516 | "href_name": "quicksight.html", 1517 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/quicksight.html", 1518 | "service_id": "quicksight" 1519 | }, 1520 | { 1521 | "name": "RAM", 1522 | "href_name": "ram.html", 1523 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ram.html", 1524 | "service_id": "ram" 1525 | }, 1526 | { 1527 | "name": "RecycleBin", 1528 | "href_name": "rbin.html", 1529 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rbin.html", 1530 | "service_id": "rbin" 1531 | }, 1532 | { 1533 | "name": "RDS", 1534 | "href_name": "rds.html", 1535 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds.html", 1536 | "service_id": "rds" 1537 | }, 1538 | { 1539 | "name": "RDSDataService", 1540 | "href_name": "rds-data.html", 1541 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rds-data.html", 1542 | "service_id": "rds-data" 1543 | }, 1544 | { 1545 | "name": "Redshift", 1546 | "href_name": "redshift.html", 1547 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/redshift.html", 1548 | "service_id": "redshift" 1549 | }, 1550 | { 1551 | "name": "RedshiftDataAPIService", 1552 | "href_name": "redshift-data.html", 1553 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/redshift-data.html", 1554 | "service_id": "redshift-data" 1555 | }, 1556 | { 1557 | "name": "RedshiftServerless", 1558 | "href_name": "redshift-serverless.html", 1559 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/redshift-serverless.html", 1560 | "service_id": "redshift-serverless" 1561 | }, 1562 | { 1563 | "name": "Rekognition", 1564 | "href_name": "rekognition.html", 1565 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rekognition.html", 1566 | "service_id": "rekognition" 1567 | }, 1568 | { 1569 | "name": "ResilienceHub", 1570 | "href_name": "resiliencehub.html", 1571 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/resiliencehub.html", 1572 | "service_id": "resiliencehub" 1573 | }, 1574 | { 1575 | "name": "ResourceExplorer", 1576 | "href_name": "resource-explorer-2.html", 1577 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/resource-explorer-2.html", 1578 | "service_id": "resource-explorer-2" 1579 | }, 1580 | { 1581 | "name": "ResourceGroups", 1582 | "href_name": "resource-groups.html", 1583 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/resource-groups.html", 1584 | "service_id": "resource-groups" 1585 | }, 1586 | { 1587 | "name": "ResourceGroupsTaggingAPI", 1588 | "href_name": "resourcegroupstaggingapi.html", 1589 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/resourcegroupstaggingapi.html", 1590 | "service_id": "resourcegroupstaggingapi" 1591 | }, 1592 | { 1593 | "name": "RoboMaker", 1594 | "href_name": "robomaker.html", 1595 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/robomaker.html", 1596 | "service_id": "robomaker" 1597 | }, 1598 | { 1599 | "name": "IAMRolesAnywhere", 1600 | "href_name": "rolesanywhere.html", 1601 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rolesanywhere.html", 1602 | "service_id": "rolesanywhere" 1603 | }, 1604 | { 1605 | "name": "Route53", 1606 | "href_name": "route53.html", 1607 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53.html", 1608 | "service_id": "route53" 1609 | }, 1610 | { 1611 | "name": "Route53RecoveryCluster", 1612 | "href_name": "route53-recovery-cluster.html", 1613 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53-recovery-cluster.html", 1614 | "service_id": "route53-recovery-cluster" 1615 | }, 1616 | { 1617 | "name": "Route53RecoveryControlConfig", 1618 | "href_name": "route53-recovery-control-config.html", 1619 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53-recovery-control-config.html", 1620 | "service_id": "route53-recovery-control-config" 1621 | }, 1622 | { 1623 | "name": "Route53RecoveryReadiness", 1624 | "href_name": "route53-recovery-readiness.html", 1625 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53-recovery-readiness.html", 1626 | "service_id": "route53-recovery-readiness" 1627 | }, 1628 | { 1629 | "name": "Route53Domains", 1630 | "href_name": "route53domains.html", 1631 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53domains.html", 1632 | "service_id": "route53domains" 1633 | }, 1634 | { 1635 | "name": "Route53Resolver", 1636 | "href_name": "route53resolver.html", 1637 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/route53resolver.html", 1638 | "service_id": "route53resolver" 1639 | }, 1640 | { 1641 | "name": "CloudWatchRUM", 1642 | "href_name": "rum.html", 1643 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/rum.html", 1644 | "service_id": "rum" 1645 | }, 1646 | { 1647 | "name": "S3", 1648 | "href_name": "s3.html", 1649 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html", 1650 | "service_id": "s3" 1651 | }, 1652 | { 1653 | "name": "S3Control", 1654 | "href_name": "s3control.html", 1655 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3control.html", 1656 | "service_id": "s3control" 1657 | }, 1658 | { 1659 | "name": "S3Outposts", 1660 | "href_name": "s3outposts.html", 1661 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3outposts.html", 1662 | "service_id": "s3outposts" 1663 | }, 1664 | { 1665 | "name": "SageMaker", 1666 | "href_name": "sagemaker.html", 1667 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html", 1668 | "service_id": "sagemaker" 1669 | }, 1670 | { 1671 | "name": "AugmentedAIRuntime", 1672 | "href_name": "sagemaker-a2i-runtime.html", 1673 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-a2i-runtime.html", 1674 | "service_id": "sagemaker-a2i-runtime" 1675 | }, 1676 | { 1677 | "name": "SagemakerEdgeManager", 1678 | "href_name": "sagemaker-edge.html", 1679 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-edge.html", 1680 | "service_id": "sagemaker-edge" 1681 | }, 1682 | { 1683 | "name": "SageMakerFeatureStoreRuntime", 1684 | "href_name": "sagemaker-featurestore-runtime.html", 1685 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-featurestore-runtime.html", 1686 | "service_id": "sagemaker-featurestore-runtime" 1687 | }, 1688 | { 1689 | "name": "SageMakergeospatialcapabilities", 1690 | "href_name": "sagemaker-geospatial.html", 1691 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-geospatial.html", 1692 | "service_id": "sagemaker-geospatial" 1693 | }, 1694 | { 1695 | "name": "SageMakerMetrics", 1696 | "href_name": "sagemaker-metrics.html", 1697 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-metrics.html", 1698 | "service_id": "sagemaker-metrics" 1699 | }, 1700 | { 1701 | "name": "SageMakerRuntime", 1702 | "href_name": "sagemaker-runtime.html", 1703 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker-runtime.html", 1704 | "service_id": "sagemaker-runtime" 1705 | }, 1706 | { 1707 | "name": "SavingsPlans", 1708 | "href_name": "savingsplans.html", 1709 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/savingsplans.html", 1710 | "service_id": "savingsplans" 1711 | }, 1712 | { 1713 | "name": "EventBridgeScheduler", 1714 | "href_name": "scheduler.html", 1715 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/scheduler.html", 1716 | "service_id": "scheduler" 1717 | }, 1718 | { 1719 | "name": "Schemas", 1720 | "href_name": "schemas.html", 1721 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/schemas.html", 1722 | "service_id": "schemas" 1723 | }, 1724 | { 1725 | "name": "SimpleDB", 1726 | "href_name": "sdb.html", 1727 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sdb.html", 1728 | "service_id": "sdb" 1729 | }, 1730 | { 1731 | "name": "SecretsManager", 1732 | "href_name": "secretsmanager.html", 1733 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html", 1734 | "service_id": "secretsmanager" 1735 | }, 1736 | { 1737 | "name": "SecurityHub", 1738 | "href_name": "securityhub.html", 1739 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/securityhub.html", 1740 | "service_id": "securityhub" 1741 | }, 1742 | { 1743 | "name": "SecurityLake", 1744 | "href_name": "securitylake.html", 1745 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/securitylake.html", 1746 | "service_id": "securitylake" 1747 | }, 1748 | { 1749 | "name": "ServerlessApplicationRepository", 1750 | "href_name": "serverlessrepo.html", 1751 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/serverlessrepo.html", 1752 | "service_id": "serverlessrepo" 1753 | }, 1754 | { 1755 | "name": "ServiceQuotas", 1756 | "href_name": "service-quotas.html", 1757 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/service-quotas.html", 1758 | "service_id": "service-quotas" 1759 | }, 1760 | { 1761 | "name": "ServiceCatalog", 1762 | "href_name": "servicecatalog.html", 1763 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/servicecatalog.html", 1764 | "service_id": "servicecatalog" 1765 | }, 1766 | { 1767 | "name": "AppRegistry", 1768 | "href_name": "servicecatalog-appregistry.html", 1769 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/servicecatalog-appregistry.html", 1770 | "service_id": "servicecatalog-appregistry" 1771 | }, 1772 | { 1773 | "name": "ServiceDiscovery", 1774 | "href_name": "servicediscovery.html", 1775 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/servicediscovery.html", 1776 | "service_id": "servicediscovery" 1777 | }, 1778 | { 1779 | "name": "SES", 1780 | "href_name": "ses.html", 1781 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses.html", 1782 | "service_id": "ses" 1783 | }, 1784 | { 1785 | "name": "SESV2", 1786 | "href_name": "sesv2.html", 1787 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sesv2.html", 1788 | "service_id": "sesv2" 1789 | }, 1790 | { 1791 | "name": "Shield", 1792 | "href_name": "shield.html", 1793 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/shield.html", 1794 | "service_id": "shield" 1795 | }, 1796 | { 1797 | "name": "signer", 1798 | "href_name": "signer.html", 1799 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/signer.html", 1800 | "service_id": "signer" 1801 | }, 1802 | { 1803 | "name": "SimSpaceWeaver", 1804 | "href_name": "simspaceweaver.html", 1805 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/simspaceweaver.html", 1806 | "service_id": "simspaceweaver" 1807 | }, 1808 | { 1809 | "name": "SMS", 1810 | "href_name": "sms.html", 1811 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sms.html", 1812 | "service_id": "sms" 1813 | }, 1814 | { 1815 | "name": "PinpointSMSVoice", 1816 | "href_name": "sms-voice.html", 1817 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sms-voice.html", 1818 | "service_id": "sms-voice" 1819 | }, 1820 | { 1821 | "name": "SnowDeviceManagement", 1822 | "href_name": "snow-device-management.html", 1823 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/snow-device-management.html", 1824 | "service_id": "snow-device-management" 1825 | }, 1826 | { 1827 | "name": "Snowball", 1828 | "href_name": "snowball.html", 1829 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/snowball.html", 1830 | "service_id": "snowball" 1831 | }, 1832 | { 1833 | "name": "SNS", 1834 | "href_name": "sns.html", 1835 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sns.html", 1836 | "service_id": "sns" 1837 | }, 1838 | { 1839 | "name": "SQS", 1840 | "href_name": "sqs.html", 1841 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sqs.html", 1842 | "service_id": "sqs" 1843 | }, 1844 | { 1845 | "name": "SSM", 1846 | "href_name": "ssm.html", 1847 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm.html", 1848 | "service_id": "ssm" 1849 | }, 1850 | { 1851 | "name": "SSMContacts", 1852 | "href_name": "ssm-contacts.html", 1853 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm-contacts.html", 1854 | "service_id": "ssm-contacts" 1855 | }, 1856 | { 1857 | "name": "SSMIncidents", 1858 | "href_name": "ssm-incidents.html", 1859 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm-incidents.html", 1860 | "service_id": "ssm-incidents" 1861 | }, 1862 | { 1863 | "name": "SsmSap", 1864 | "href_name": "ssm-sap.html", 1865 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ssm-sap.html", 1866 | "service_id": "ssm-sap" 1867 | }, 1868 | { 1869 | "name": "SSO", 1870 | "href_name": "sso.html", 1871 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sso.html", 1872 | "service_id": "sso" 1873 | }, 1874 | { 1875 | "name": "SSOAdmin", 1876 | "href_name": "sso-admin.html", 1877 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sso-admin.html", 1878 | "service_id": "sso-admin" 1879 | }, 1880 | { 1881 | "name": "SSOOIDC", 1882 | "href_name": "sso-oidc.html", 1883 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sso-oidc.html", 1884 | "service_id": "sso-oidc" 1885 | }, 1886 | { 1887 | "name": "SFN", 1888 | "href_name": "stepfunctions.html", 1889 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/stepfunctions.html", 1890 | "service_id": "stepfunctions" 1891 | }, 1892 | { 1893 | "name": "StorageGateway", 1894 | "href_name": "storagegateway.html", 1895 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/storagegateway.html", 1896 | "service_id": "storagegateway" 1897 | }, 1898 | { 1899 | "name": "STS", 1900 | "href_name": "sts.html", 1901 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sts.html", 1902 | "service_id": "sts" 1903 | }, 1904 | { 1905 | "name": "Support", 1906 | "href_name": "support.html", 1907 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/support.html", 1908 | "service_id": "support" 1909 | }, 1910 | { 1911 | "name": "SupportApp", 1912 | "href_name": "support-app.html", 1913 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/support-app.html", 1914 | "service_id": "support-app" 1915 | }, 1916 | { 1917 | "name": "SWF", 1918 | "href_name": "swf.html", 1919 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/swf.html", 1920 | "service_id": "swf" 1921 | }, 1922 | { 1923 | "name": "Synthetics", 1924 | "href_name": "synthetics.html", 1925 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/synthetics.html", 1926 | "service_id": "synthetics" 1927 | }, 1928 | { 1929 | "name": "Textract", 1930 | "href_name": "textract.html", 1931 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/textract.html", 1932 | "service_id": "textract" 1933 | }, 1934 | { 1935 | "name": "TimestreamQuery", 1936 | "href_name": "timestream-query.html", 1937 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/timestream-query.html", 1938 | "service_id": "timestream-query" 1939 | }, 1940 | { 1941 | "name": "TimestreamWrite", 1942 | "href_name": "timestream-write.html", 1943 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/timestream-write.html", 1944 | "service_id": "timestream-write" 1945 | }, 1946 | { 1947 | "name": "TelcoNetworkBuilder", 1948 | "href_name": "tnb.html", 1949 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/tnb.html", 1950 | "service_id": "tnb" 1951 | }, 1952 | { 1953 | "name": "TranscribeService", 1954 | "href_name": "transcribe.html", 1955 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/transcribe.html", 1956 | "service_id": "transcribe" 1957 | }, 1958 | { 1959 | "name": "Transfer", 1960 | "href_name": "transfer.html", 1961 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/transfer.html", 1962 | "service_id": "transfer" 1963 | }, 1964 | { 1965 | "name": "Translate", 1966 | "href_name": "translate.html", 1967 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/translate.html", 1968 | "service_id": "translate" 1969 | }, 1970 | { 1971 | "name": "VoiceID", 1972 | "href_name": "voice-id.html", 1973 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/voice-id.html", 1974 | "service_id": "voice-id" 1975 | }, 1976 | { 1977 | "name": "WAF", 1978 | "href_name": "waf.html", 1979 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/waf.html", 1980 | "service_id": "waf" 1981 | }, 1982 | { 1983 | "name": "WAFRegional", 1984 | "href_name": "waf-regional.html", 1985 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/waf-regional.html", 1986 | "service_id": "waf-regional" 1987 | }, 1988 | { 1989 | "name": "WAFV2", 1990 | "href_name": "wafv2.html", 1991 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/wafv2.html", 1992 | "service_id": "wafv2" 1993 | }, 1994 | { 1995 | "name": "WellArchitected", 1996 | "href_name": "wellarchitected.html", 1997 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/wellarchitected.html", 1998 | "service_id": "wellarchitected" 1999 | }, 2000 | { 2001 | "name": "ConnectWisdomService", 2002 | "href_name": "wisdom.html", 2003 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/wisdom.html", 2004 | "service_id": "wisdom" 2005 | }, 2006 | { 2007 | "name": "WorkDocs", 2008 | "href_name": "workdocs.html", 2009 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/workdocs.html", 2010 | "service_id": "workdocs" 2011 | }, 2012 | { 2013 | "name": "WorkLink", 2014 | "href_name": "worklink.html", 2015 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/worklink.html", 2016 | "service_id": "worklink" 2017 | }, 2018 | { 2019 | "name": "WorkMail", 2020 | "href_name": "workmail.html", 2021 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/workmail.html", 2022 | "service_id": "workmail" 2023 | }, 2024 | { 2025 | "name": "WorkMailMessageFlow", 2026 | "href_name": "workmailmessageflow.html", 2027 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/workmailmessageflow.html", 2028 | "service_id": "workmailmessageflow" 2029 | }, 2030 | { 2031 | "name": "WorkSpaces", 2032 | "href_name": "workspaces.html", 2033 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/workspaces.html", 2034 | "service_id": "workspaces" 2035 | }, 2036 | { 2037 | "name": "WorkSpacesWeb", 2038 | "href_name": "workspaces-web.html", 2039 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/workspaces-web.html", 2040 | "service_id": "workspaces-web" 2041 | }, 2042 | { 2043 | "name": "XRay", 2044 | "href_name": "xray.html", 2045 | "doc_url": "https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/xray.html", 2046 | "service_id": "xray" 2047 | } 2048 | ] -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = boto_session_manager 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | set SPHINXPROJ=boto_session_manager 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/source/_static/.custom-style.rst: -------------------------------------------------------------------------------- 1 | .. role:: black 2 | .. role:: gray 3 | .. role:: grey 4 | .. role:: silver 5 | .. role:: white 6 | .. role:: maroon 7 | .. role:: red 8 | .. role:: magenta 9 | .. role:: fuchsia 10 | .. role:: pink 11 | .. role:: orange 12 | .. role:: yellow 13 | .. role:: lime 14 | .. role:: green 15 | .. role:: olive 16 | .. role:: teal 17 | .. role:: cyan 18 | .. role:: aqua 19 | .. role:: blue 20 | .. role:: navy 21 | .. role:: purple 22 | 23 | .. role:: under 24 | .. role:: over 25 | .. role:: blink 26 | .. role:: line 27 | .. role:: strike 28 | 29 | .. role:: it 30 | .. role:: ob 31 | 32 | .. role:: small 33 | .. role:: large 34 | -------------------------------------------------------------------------------- /docs/source/_static/boto_session_manager-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/boto-session-manager-project/81ee0ac3756d8032570df699d535367b856fcc5e/docs/source/_static/boto_session_manager-favicon.ico -------------------------------------------------------------------------------- /docs/source/_static/boto_session_manager-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/boto-session-manager-project/81ee0ac3756d8032570df699d535367b856fcc5e/docs/source/_static/boto_session_manager-logo.png -------------------------------------------------------------------------------- /docs/source/_static/css/custom-style.css: -------------------------------------------------------------------------------- 1 | /* additional style */ 2 | .black { 3 | color: black; 4 | } 5 | 6 | .gray { 7 | color: gray; 8 | } 9 | 10 | .grey { 11 | color: gray; 12 | } 13 | 14 | .silver { 15 | color: silver; 16 | } 17 | 18 | .white { 19 | color: white; 20 | } 21 | 22 | .maroon { 23 | color: maroon; 24 | } 25 | 26 | .red { 27 | color: red; 28 | } 29 | 30 | .magenta { 31 | color: magenta; 32 | } 33 | 34 | .fuchsia { 35 | color: fuchsia; 36 | } 37 | 38 | .pink { 39 | color: pink; 40 | } 41 | 42 | .orange { 43 | color: orange; 44 | } 45 | 46 | .yellow { 47 | color: yellow; 48 | } 49 | 50 | .lime { 51 | color: lime; 52 | } 53 | 54 | .green { 55 | color: green; 56 | } 57 | 58 | .olive { 59 | color: olive; 60 | } 61 | 62 | .teal { 63 | color: teal; 64 | } 65 | 66 | .cyan { 67 | color: cyan; 68 | } 69 | 70 | .aqua { 71 | color: aqua; 72 | } 73 | 74 | .blue { 75 | color: blue; 76 | } 77 | 78 | .navy { 79 | color: navy; 80 | } 81 | 82 | .purple { 83 | color: purple; 84 | } 85 | 86 | .under { 87 | text-decoration: underline; 88 | } 89 | 90 | .over { 91 | text-decoration: overline; 92 | } 93 | 94 | .blink { 95 | text-decoration: blink; 96 | } 97 | 98 | .line { 99 | text-decoration: line-through; 100 | } 101 | 102 | .strike { 103 | text-decoration: line-through; 104 | } 105 | 106 | .it { 107 | font-style: italic; 108 | } 109 | 110 | .ob { 111 | font-style: oblique; 112 | } 113 | 114 | .small { 115 | font-size: small; 116 | } 117 | 118 | .large { 119 | font-size: large; 120 | } 121 | 122 | /* sort table library */ 123 | table.sortable th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after { 124 | content: " \25B4\25BE" 125 | } 126 | table.sortable tbody tr:nth-child(2n) td { 127 | background: #ffffff; 128 | } 129 | table.sortable tbody tr:nth-child(2n+1) td { 130 | background: #f2f2f2; 131 | } 132 | -------------------------------------------------------------------------------- /docs/source/_static/js/sorttable.js: -------------------------------------------------------------------------------- 1 | /* 2 | SortTable 3 | version 2 4 | 7th April 2007 5 | Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ 6 | 7 | Instructions: 8 | Download this file 9 | Add to your HTML 10 | Add class="sortable" to any table you'd like to make sortable 11 | Click on the headers to sort 12 | 13 | Thanks to many, many people for contributions and suggestions. 14 | Licenced as X11: http://www.kryogenix.org/code/browser/licence.html 15 | This basically means: do what you want with it. 16 | */ 17 | 18 | 19 | var stIsIE = /*@cc_on!@*/false; 20 | 21 | sorttable = { 22 | init: function() { 23 | // quit if this function has already been called 24 | if (arguments.callee.done) return; 25 | // flag this function so we don't do the same thing twice 26 | arguments.callee.done = true; 27 | // kill the timer 28 | if (_timer) clearInterval(_timer); 29 | 30 | if (!document.createElement || !document.getElementsByTagName) return; 31 | 32 | sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; 33 | 34 | forEach(document.getElementsByTagName('table'), function(table) { 35 | if (table.className.search(/\bsortable\b/) != -1) { 36 | sorttable.makeSortable(table); 37 | } 38 | }); 39 | 40 | }, 41 | 42 | makeSortable: function(table) { 43 | if (table.getElementsByTagName('thead').length == 0) { 44 | // table doesn't have a tHead. Since it should have, create one and 45 | // put the first table row in it. 46 | the = document.createElement('thead'); 47 | the.appendChild(table.rows[0]); 48 | table.insertBefore(the,table.firstChild); 49 | } 50 | // Safari doesn't support table.tHead, sigh 51 | if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; 52 | 53 | if (table.tHead.rows.length != 1) return; // can't cope with two header rows 54 | 55 | // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as 56 | // "total" rows, for example). This is B&R, since what you're supposed 57 | // to do is put them in a tfoot. So, if there are sortbottom rows, 58 | // for backwards compatibility, move them to tfoot (creating it if needed). 59 | sortbottomrows = []; 60 | for (var i=0; i5' : ' ▴'; 104 | this.appendChild(sortrevind); 105 | return; 106 | } 107 | if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { 108 | // if we're already sorted by this column in reverse, just 109 | // re-reverse the table, which is quicker 110 | sorttable.reverse(this.sorttable_tbody); 111 | this.className = this.className.replace('sorttable_sorted_reverse', 112 | 'sorttable_sorted'); 113 | this.removeChild(document.getElementById('sorttable_sortrevind')); 114 | sortfwdind = document.createElement('span'); 115 | sortfwdind.id = "sorttable_sortfwdind"; 116 | sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; 117 | this.appendChild(sortfwdind); 118 | return; 119 | } 120 | 121 | // remove sorttable_sorted classes 122 | theadrow = this.parentNode; 123 | forEach(theadrow.childNodes, function(cell) { 124 | if (cell.nodeType == 1) { // an element 125 | cell.className = cell.className.replace('sorttable_sorted_reverse',''); 126 | cell.className = cell.className.replace('sorttable_sorted',''); 127 | } 128 | }); 129 | sortfwdind = document.getElementById('sorttable_sortfwdind'); 130 | if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } 131 | sortrevind = document.getElementById('sorttable_sortrevind'); 132 | if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } 133 | 134 | this.className += ' sorttable_sorted'; 135 | sortfwdind = document.createElement('span'); 136 | sortfwdind.id = "sorttable_sortfwdind"; 137 | sortfwdind.innerHTML = stIsIE ? ' 6' : ' ▾'; 138 | this.appendChild(sortfwdind); 139 | 140 | // build an array to sort. This is a Schwartzian transform thing, 141 | // i.e., we "decorate" each row with the actual sort key, 142 | // sort based on the sort keys, and then put the rows back in order 143 | // which is a lot faster because you only do getInnerText once per row 144 | row_array = []; 145 | col = this.sorttable_columnindex; 146 | rows = this.sorttable_tbody.rows; 147 | for (var j=0; j 12) { 184 | // definitely dd/mm 185 | return sorttable.sort_ddmm; 186 | } else if (second > 12) { 187 | return sorttable.sort_mmdd; 188 | } else { 189 | // looks like a date, but we can't tell which, so assume 190 | // that it's dd/mm (English imperialism!) and keep looking 191 | sortfn = sorttable.sort_ddmm; 192 | } 193 | } 194 | } 195 | } 196 | return sortfn; 197 | }, 198 | 199 | getInnerText: function(node) { 200 | // gets the text we want to use for sorting for a cell. 201 | // strips leading and trailing whitespace. 202 | // this is *not* a generic getInnerText function; it's special to sorttable. 203 | // for example, you can override the cell text with a customkey attribute. 204 | // it also gets .value for fields. 205 | 206 | if (!node) return ""; 207 | 208 | hasInputs = (typeof node.getElementsByTagName == 'function') && 209 | node.getElementsByTagName('input').length; 210 | 211 | if (node.getAttribute("sorttable_customkey") != null) { 212 | return node.getAttribute("sorttable_customkey"); 213 | } 214 | else if (typeof node.textContent != 'undefined' && !hasInputs) { 215 | return node.textContent.replace(/^\s+|\s+$/g, ''); 216 | } 217 | else if (typeof node.innerText != 'undefined' && !hasInputs) { 218 | return node.innerText.replace(/^\s+|\s+$/g, ''); 219 | } 220 | else if (typeof node.text != 'undefined' && !hasInputs) { 221 | return node.text.replace(/^\s+|\s+$/g, ''); 222 | } 223 | else { 224 | switch (node.nodeType) { 225 | case 3: 226 | if (node.nodeName.toLowerCase() == 'input') { 227 | return node.value.replace(/^\s+|\s+$/g, ''); 228 | } 229 | case 4: 230 | return node.nodeValue.replace(/^\s+|\s+$/g, ''); 231 | break; 232 | case 1: 233 | case 11: 234 | var innerText = ''; 235 | for (var i = 0; i < node.childNodes.length; i++) { 236 | innerText += sorttable.getInnerText(node.childNodes[i]); 237 | } 238 | return innerText.replace(/^\s+|\s+$/g, ''); 239 | break; 240 | default: 241 | return ''; 242 | } 243 | } 244 | }, 245 | 246 | reverse: function(tbody) { 247 | // reverse the rows in a tbody 248 | newrows = []; 249 | for (var i=0; i=0; i--) { 253 | tbody.appendChild(newrows[i]); 254 | } 255 | delete newrows; 256 | }, 257 | 258 | /* sort functions 259 | each sort function takes two parameters, a and b 260 | you are comparing a[0] and b[0] */ 261 | sort_numeric: function(a,b) { 262 | aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); 263 | if (isNaN(aa)) aa = 0; 264 | bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); 265 | if (isNaN(bb)) bb = 0; 266 | return aa-bb; 267 | }, 268 | sort_alpha: function(a,b) { 269 | if (a[0]==b[0]) return 0; 270 | if (a[0] 0 ) { 316 | var q = list[i]; list[i] = list[i+1]; list[i+1] = q; 317 | swap = true; 318 | } 319 | } // for 320 | t--; 321 | 322 | if (!swap) break; 323 | 324 | for(var i = t; i > b; --i) { 325 | if ( comp_func(list[i], list[i-1]) < 0 ) { 326 | var q = list[i]; list[i] = list[i-1]; list[i-1] = q; 327 | swap = true; 328 | } 329 | } // for 330 | b++; 331 | 332 | } // while(swap) 333 | } 334 | } 335 | 336 | /* ****************************************************************** 337 | Supporting functions: bundled here to avoid depending on a library 338 | ****************************************************************** */ 339 | 340 | // Dean Edwards/Matthias Miller/John Resig 341 | 342 | /* for Mozilla/Opera9 */ 343 | if (document.addEventListener) { 344 | document.addEventListener("DOMContentLoaded", sorttable.init, false); 345 | } 346 | 347 | /* for Internet Explorer */ 348 | /*@cc_on @*/ 349 | /*@if (@_win32) 350 | document.write("