├── .gitignore ├── .travis.yml ├── AUTHORS.rst ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── sshless ├── __init__.py ├── __main__.py ├── cli.py ├── core.py └── util.py ├── test ├── DOC.rst ├── README.rst ├── cassettes │ ├── test_cmd_with_instances_ids[sshless_cmd0].yaml │ ├── test_list_commands[7ce628a4-e96c-4a52-88be-027750b6bf58].yaml │ ├── test_list_commands[empty].yaml │ ├── test_list_commands_ok[7ce628a4-e96c-4a52-88be-027750b6bf58].yaml │ └── test_list_commands_ok[empty].yaml ├── conftest.py ├── resources │ └── diagram.png ├── terraform │ ├── azure.tf │ ├── ec2.tf │ ├── iam-service.tf │ ├── iam.tf │ ├── legacy.tf │ ├── main.tf │ ├── run.sh │ ├── ssm.tf │ ├── user_data.tpl │ ├── vpc.bak │ └── vpc.tf ├── test_cli.py ├── test_core_sshless.py └── test_util.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .pytest_cache 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | 24 | **/.terraform 25 | **/terraform.tfstate* 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | test-results.xml 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | 59 | # Sphinx documentation 60 | docs/_build/ 61 | 62 | # PyBuilder 63 | target/ 64 | 65 | # Sceptre directories users may have for testing 66 | /config 67 | /templates 68 | 69 | ### OSX ### 70 | *.DS_Store 71 | .AppleDouble 72 | .LSOverride 73 | 74 | # Icon must end with two \r 75 | Icon 76 | 77 | 78 | # Thumbnails 79 | ._* 80 | 81 | # Files that might appear in the root of a volume 82 | .DocumentRevisions-V100 83 | .fseventsd 84 | .Spotlight-V100 85 | .TemporaryItems 86 | .Trashes 87 | .VolumeIcon.icns 88 | .com.apple.timemachine.donotpresent 89 | 90 | # Directories potentially created on remote AFP share 91 | .AppleDB 92 | .AppleDesktop 93 | Network Trash Folder 94 | Temporary Items 95 | .apdisk 96 | 97 | ### PyCharm ### 98 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 99 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 100 | 101 | # User-specific stuff: 102 | .idea/ 103 | *.iml 104 | 105 | ## File-based project format: 106 | *.iws 107 | 108 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: '3.6' 3 | install: 4 | - pip install tox pbr 5 | script: 6 | - tox 7 | deploy: 8 | provider: pypi 9 | user: 10 | secure: TXy96Jq8M+KJf4d9vd9tkRsg+56wR3ugH+N8nyfBqxCd6XI0D/1SHJVr659n4nBTWr9nMrQAay/V31fbwjZ1xJEFB03ID6RKy03LUdAqsyU+681jY1VOdP1mkyVnUtpWTc+LTHTmVsv3yMlJ1s47Pj70aR7bcHrABvQ6TUN4adlFLmL3LuG2Ty+GDtJwyQ+6hBIhCC8MOZv3Y62YNMuPXPvc5ZdMIqUXel/sJiH9glyEGtVS7ghM2eLMKQd1sEKbndt82AmZZyKgyZ/Hjs5Y7Tq3IsuydsGtVVkXOfMP9Ps12L+VrnHaI07l8yvao/MRVXDX1L9cTdahH/g0JoOjfHIyjWiYz89TZ8pMhTFZJiV2FrIz9phAHGXMPis++n/49Bo+JMNbOh12pO6kyIBme0NUELKKp6dyJV68iG7be7W9xX6aAdlkc+/qqW/ZJ6FfPreWsm0EwtJ1mv4SewYkOyLIFdRrvKT+acaI9lycEk013Sfdl0TjXcFqMuNPSFSzLzZCmN70KtZ7b3YGdFhrko4OP408uIbaxh8Pk+8u/nmWGRxVx8QBmGR67CD5SAAm0rvZ86AAab8e+SXP5liychGt3HJh8adj8X9T62Soj743au0QnVJoCKXajjd3hGtrqKd2EqGp1hrkviMnhtA4UllDxUHl+sC6gOJgN17GQfc= 11 | password: 12 | secure: Ss+aPrsx3uL5buIqmSVDeOLHtS/PY74hReRhaVS0NYuF2JypXaRjkxs9ufWm0fIv5uv9L47dd/bCmpk/MAPAhhKxU4uhEtOkxgRXEO9bCwEJWw/RCkGAMnk+XpBsNylOXGBWCAntBP0fi/HK5WA1KVnzvFyauV5ZC0BIs2vRTHt54DRBQf17L/2IhOSbonJzkeJrtvcdOnVKTgkcNESsIgf859kDznHmjwvplu24VVSfVjIhV3v72uZzVkQNShwZVMfAxDD08kvr6HAS49Q7e5xnrKxqbw0OJIOK8A2d/IPb7ebbOH6O6StBdzeNqJFiD7YJN/wbemZQZEMkKuKDVk3HVqVLHO7eYpqBjKwBKsDglMc1ZxahNuj+jyHnKYnoCglZehKUWb95w5ejFjxl2K0osVWqq9MdokLoet9gA+uLXfnDGMeJcxKze973nhJU8xpIJ7xS0Tf+ffaTlb1OC6xFTq76Tb6OMOp6y1OFMD9Iy2aNQSFp6K3qNo4WKrNixubv7Ipl/G59WrvvQKbajYi0QBaX4EHnfZyCPOSKFEcWLeRM+TBI1QsD2S81RuPdZoICRMEFRvp0jWTS4RVJDWHfeHr/C07cGm/477oAmwQx4g5ESUeLwkFz7HrLliTnK17Z2d/ELd0OvFYFBPoWajjclt6ynVJGhLiZg6wipeY= 13 | on: 14 | branch: master 15 | tags: true 16 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Owner 6 | ----- 7 | 8 | * Giulio Calzolari `@giuliocalzo `_ 9 | 10 | Contributors 11 | ------------ 12 | 13 | * nassim `@Grindizer `_ 14 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | CHANGES 2 | ======= 3 | 4 | 1.0.0 5 | ----- 6 | 7 | * Initial Commit 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | Hello! Thank you for choosing to help contribute to one of the Cloudreach OpenSource projects. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies. 6 | 7 | - `How to Contribute`_ 8 | 9 | - `Report Bugs`_ 10 | - `Enhancement Proposal`_ 11 | - `Contributing Code`_ 12 | 13 | - `Get Started`_ 14 | - `Credits`_ 15 | 16 | Code of Conduct 17 | --------------- 18 | 19 | This project adheres to the Contributor Covenant `code of conduct `_. By participating, you are expected to uphold this code. Please report unacceptable behaviour to oss@cloudreach.com. 20 | 21 | How to Contribute 22 | ----------------- 23 | 24 | Report Bugs 25 | *********** 26 | 27 | Note: DO NOT include your credentials in ANY code examples, descriptions, or media you make public. 28 | 29 | 30 | Before submitting a bug, please check our `issues page `_ to see if it's already been reported. 31 | 32 | When reporting a bug, fill out the required template, and please include as much detail as possible as it helps us resolve issues faster. 33 | 34 | 35 | Enhancement Proposal 36 | ******************** 37 | 38 | Enhancement proposals should: 39 | 40 | * Use a descriptive title. 41 | * Provide a step-by-step description of the suggested enhancement. 42 | * Provide specific examples to demonstrate the steps. 43 | * Describe the current behaviour and explain which behaviour you expected to see instead. 44 | * Keep the scope as narrow as possible, to make it easier to implement. 45 | 46 | Remember that this is a volunteer-driven project, and that contributions are welcome. 47 | 48 | 49 | Contributing Code 50 | ***************** 51 | 52 | Contributions should be made in response to a particular GitHub Issue. We find it easier to review code if we've already discussed what it should do, and assessed if it fits with the wider codebase. 53 | 54 | Beginner friendly issues are marked with the ``beginner friendly`` tag. We'll endeavour to write clear instructions on what we want to do, why we want to do it, and roughly how to do it. Feel free to ask us any questions that may arise. 55 | 56 | A good pull request: 57 | 58 | * Is clear. 59 | * Works across all supported version of Python. 60 | * Complies with the existing codebase style (`flake8 `_, `pylint `_). 61 | * Includes `docstrings `_ and comments for unintuitive sections of code. 62 | * Includes documentation for new features. 63 | * Includes tests cases that demonstrates the previous flaw that now passes with the included patch, or demonstrates the newly added feature. Tests should have 100% code coverage. 64 | * Is appropriately licensed (Apache 2.0). 65 | 66 | 67 | 68 | 69 | Get Started 70 | ----------- 71 | 72 | 1. Fork the ``sshless`` repository on GitHub. 73 | 2. Clone your fork locally:: 74 | 75 | $ git clone git@github.org:your_name_here/sshless.git 76 | 77 | 3. Install your local copy into a `virtual environment `_. Assuming you have virtualenv installed, this is how you set up your fork for local development: 78 | 79 | .. code-block:: shell 80 | 81 | $ cd sshless/ 82 | $ # Enable your virtual environment 83 | $ virtualenv env 84 | $ source env/bin/activate 85 | $ # Install python requirements 86 | $ pip install -r requirements.txt 87 | 88 | 4. Create a branch for local development: 89 | 90 | .. code-block:: shell 91 | 92 | $ git checkout -b - 93 | 94 | 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox: 95 | 96 | .. code-block:: shell 97 | 98 | $ make lint 99 | $ make test-all 100 | $ make coverage # coverage should be 100% 101 | 102 | 6. Make sure the changes comply with the pull request guidelines in the section on `Contributing Code`_. 103 | 104 | 7. Commit your changes: 105 | 106 | .. code-block:: shell 107 | 108 | $ git add . 109 | $ git commit 110 | 111 | Commit messages should follow `these guidelines `_. 112 | 113 | Push your branch to GitHub:: 114 | 115 | $ git push origin 116 | 117 | 8. Submit a pull request through the GitHub website. 118 | 119 | 120 | Credits 121 | ------- 122 | 123 | This document took inspiration from the CONTRIBUTING files of the `Atom `_ and `Boto3 `_ projects. 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache Software License 2.0 2 | 3 | Copyright 2017 Cloudreach Europe Limited or its affiliates. All Rights Reserved. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include VERSION 2 | include LICENSE 3 | include README.rst 4 | include requirements.txt 5 | 6 | include sshless 7 | recursive-exclude * __pycache__ 8 | recursive-exclude * *.py[co] 9 | 10 | recursive-include conf.py Makefile 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | 4 | clean: clean-build clean-pyc 5 | 6 | version: 7 | python setup.py --version 8 | 9 | clean-build: 10 | rm -rf build/ 11 | rm -rf dist/ 12 | rm -rf .eggs/ 13 | rm -rf .cache/ 14 | rm -rf .tox/ 15 | rm -rf .pytest_cache/ 16 | rm -rf '*.egg-info/' 17 | find . -name '*.egg-info' -exec rm -fr {} + 18 | find . -name '*.egg' -exec rm -f {} + 19 | 20 | clean-pyc: 21 | find . -name '*.pyc' -exec rm -f {} + 22 | find . -name '*.pyo' -exec rm -f {} + 23 | find . -name '*~' -exec rm -f {} + 24 | find . -name '__pycache__' -exec rm -fr {} + 25 | 26 | lint: 27 | flake8 --ignore=E722,F405,E501,F403 sshless tests 28 | 29 | dist: clean 30 | python setup.py sdist 31 | 32 | pip: dist 33 | twine upload dist/* 34 | 35 | tag_github_release: 36 | git tag v`python setup.py --version` 37 | git push origin v`python setup.py --version` 38 | 39 | 40 | local: clean-build 41 | python setup.py install 42 | rm -fr build/ 43 | rm -fr dist/ 44 | rm -fr .eggs/ 45 | rm -rf *.egg-info/ 46 | 47 | test: 48 | pytest 49 | 50 | test-all: 51 | tox 52 | mv Changelog CHANGELOG.rst 53 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | SSHLess with AWS SSM 3 | ==================== 4 | 5 | .. image:: https://travis-ci.org/cloudreach/sshless.svg?branch=master 6 | :target: https://travis-ci.org/cloudreach/sshless 7 | 8 | .. image:: https://badge.fury.io/py/sshless.svg 9 | :target: https://badge.fury.io/py/sshless 10 | 11 | 12 | Overview 13 | -------- 14 | 15 | At re:invent 2017, many features were introduced such as `SSM PrivateLink `_, `PCI compliance `_. 16 | I decided to investigate on SSM and `SendCommand `_ to understand its capabilities in a real world. 17 | SSHLess is a python implementation of SSM SendCommand to simulate the usage of a normal CLI 18 | 19 | 20 | Config 21 | ------ 22 | 23 | this script is designed to run across multiple accounts and across multiple regions you can switch between regions/accounts using some OS vars 24 | 25 | To execute an assume role action 26 | :: 27 | 28 | $ export AWS_SSM_ROLE=arn:aws:iam::111111111:role/admin 29 | 30 | 31 | Cache Filters 32 | ------------- 33 | 34 | sshless use a local file to save the Target filters in order to simplify and avoid to have long command line history 35 | 36 | Example:: 37 | 38 | $ sshless cmd --name web-1 "uname -a" 39 | ..... output omitted .... 40 | $ cat ~/.sshless/filters # local file with your filter 41 | { 42 | "Targets": [{ 43 | "Key": "tag:Name", 44 | "Values": ["web-1"] 45 | }] 46 | } 47 | $ sshless cmd "uname -a" # valid command to the same target 48 | ..... output omitted .... 49 | 50 | 51 | Command 52 | ------- 53 | 54 | Instance ID Filter:: 55 | 56 | $ export SSHLESS_ID_FILTER=i-0da73e7c56e628889,i-0b83e0b9f8f900500 57 | $ sshless cmd "uname -a" 58 | 59 | $ sshless cmd -i i-0da73e7c56e628889,i-0b83e0b9f8f900500 "uname -a" 60 | 61 | Tag Name Filter:: 62 | 63 | $ export SSHLESS_NAME_FILTER=web-1 64 | $ sshless cmd "uname -a" 65 | $ sshless cmd --name web-1 "uname -a" 66 | 67 | Advanced Tag filter:: 68 | 69 | $ export SSHLESS_FILTER=tag:Role=web 70 | $ sshless cmd "uname -a" 71 | $ sshless cmd --filters tag:Role=web "uname -a" 72 | 73 | SSM Parameter store integration:: 74 | 75 | $ sshless cmd --name web-1 "echo {{ssm:example.parameter}}" 76 | 77 | List of all SSM instances Online:: 78 | 79 | $ sshless list 80 | 81 | 82 | Execute command and save output to S3:: 83 | 84 | $ sshless cmd --name web-1 "uname -a" --s3-output=[your-s3-bucket-ssm-output] 85 | $ sshless cmd --name web-1 "uname -a" --s3-output=[your-s3-bucket-ssm-output] --preserve-s3-output 86 | 87 | 88 | ============ 89 | SSHLess DEMO 90 | ============ 91 | 92 | Full Demo Lab is available `HERE `_ 93 | 94 | Maintenance 95 | ----------- 96 | Cloudreach shall, where possible, use reasonable efforts to maintain this repository. Typically the repository is maintained by the developer in his/her free or personal development time. 97 | 98 | License 99 | ------- 100 | 101 | sshless is licensed under the `Apache2 `_. 102 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | botocore 2 | boto3>=1.5.0 3 | click>=6.6 4 | packaging>=16.8 5 | termcolor 6 | tox 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = sshless 3 | author = cloudreach 4 | author-email = oss@cloudreach.com 5 | description-file = 6 | README.rst 7 | summary = sshless using AWS SSM 8 | project_urls = 9 | Bug Tracker = https://github.com/cloudreach/sshless/issues 10 | Documentation = https://github.com/cloudreach/sshless/blob/master/README.rst 11 | Source Code = https://github.com/cloudreach/sshless 12 | classifier = 13 | Development Status :: 4 - Beta 14 | Intended Audience :: Developers 15 | Natural Language :: English 16 | Environment :: Console 17 | Programming Language :: Python :: 2.6 18 | Programming Language :: Python :: 2.7 19 | Programming Language :: Python :: 3.6 20 | 21 | [pbr] 22 | skip_authors = True 23 | 24 | [entry_points] 25 | console_scripts = 26 | sshless = sshless.cli:cli 27 | 28 | 29 | [extras] 30 | dev = 31 | pytest 32 | tox 33 | pytest-vcr 34 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- encoding: utf-8 -*- 3 | 4 | from setuptools import setup 5 | 6 | setup( 7 | setup_requires=['pbr'], 8 | pbr=True, 9 | ) 10 | -------------------------------------------------------------------------------- /sshless/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudreach/sshless/04a40a9ad99baab1acb9f5568687a698e4baf659/sshless/__init__.py -------------------------------------------------------------------------------- /sshless/__main__.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | 3 | from . cli import cli 4 | 5 | if __name__ == '__main__': 6 | cli() 7 | -------------------------------------------------------------------------------- /sshless/cli.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python 2 | 3 | import sys 4 | import logging 5 | import time 6 | import os 7 | from functools import wraps 8 | import click 9 | from . core import SSHLess 10 | from . util import * 11 | from termcolor import colored 12 | 13 | 14 | # Setup simple logging for INFO 15 | logging.getLogger("botocore").setLevel(logging.CRITICAL) 16 | logger = logging.getLogger("sshless") 17 | handler = logging.StreamHandler(sys.stdout) 18 | FORMAT = "[%(asctime)s][%(levelname)s] %(message)s" 19 | handler.setFormatter(logging.Formatter(FORMAT)) 20 | logger.addHandler(handler) 21 | logger.setLevel(logging.WARN) 22 | 23 | 24 | def catch_exceptions(func): 25 | @wraps(func) 26 | def decorated(*args, **kwargs): 27 | """ 28 | Invokes ``func``, catches expected errors, prints the error message and 29 | exits sceptre with a non-zero exit code. 30 | """ 31 | try: 32 | return func(*args, **kwargs) 33 | except: 34 | # logger.error(sys.exc_info()[1]) 35 | click.echo("[{}][{}] {}".format(time.strftime("%Y-%m-%d %H:%M:%S"), 36 | colored("ERROR", "red"), 37 | sys.exc_info()[1] 38 | )) 39 | sys.exit(1) 40 | 41 | return decorated 42 | 43 | 44 | @click.group() 45 | @click.version_option(prog_name="sshless") 46 | @click.pass_context 47 | @click.option( 48 | "--iam", default=os.environ.get("AWS_SSM_ROLE", ""), help="IAM to assume") 49 | @click.option( 50 | "--region", 51 | default=os.environ.get("AWS_DEFAULT_REGION", "eu-west-1"), 52 | help="AWS region") 53 | @click.option('-v', '--verbose', is_flag=True, multiple=True) 54 | def cli(ctx, iam, region, verbose): 55 | ctx.obj = { 56 | "options": {}, 57 | "region": region, 58 | "iam": iam, 59 | "verbosity": len(verbose) 60 | } 61 | 62 | if ctx.obj["verbosity"] == 1: 63 | logger.setLevel(logging.INFO) 64 | elif ctx.obj["verbosity"] > 1: 65 | logger.setLevel(logging.DEBUG) 66 | logger.debug("log level is set to DEBUG") 67 | logger.debug(ctx.obj) 68 | pass 69 | 70 | 71 | @cli.command() 72 | @click.option('-f', '--filters', default="PingStatus=Online", help='advanced Filter default: PingStatus=Online') 73 | @click.pass_context 74 | @catch_exceptions 75 | def list(ctx, filters): 76 | """SSM Managed instances Online""" 77 | 78 | sshless = SSHLess(ctx.obj) 79 | fl = [] 80 | for ff in filters.split(","): 81 | key, val = ff.split("=") 82 | fl.append({"Key": key, "Values": [val]}) 83 | 84 | response = sshless.ssm.describe_instance_information( 85 | Filters=fl 86 | ) 87 | logger.info("Total instances: {}".format( 88 | len(response["InstanceInformationList"]))) 89 | click.echo(format_json(response["InstanceInformationList"])) 90 | 91 | 92 | @cli.command() 93 | @click.option('-f', '--filters', default="DocumentType=Command", help='Document Filter default: DocumentType=Command') 94 | @click.option('-a', '--all-details', is_flag=True, help='All details') 95 | @click.option('-o', '--owner', is_flag=True, help='Show owner') 96 | @click.option('-P', '--platform', is_flag=True, help='Show platform types') 97 | @click.option('-d', '--doc-version', is_flag=True, help='Show document version') 98 | @click.option('-t', '--doc-type', is_flag=True, help='Show document type') 99 | @click.option('-s', '--schema', is_flag=True, help='Show schema version') 100 | @click.pass_context 101 | @catch_exceptions 102 | def docs(ctx, filters, all_details, owner, platform, doc_version, doc_type, schema): 103 | """List SSM documents""" 104 | sshless = SSHLess(ctx.obj) 105 | fl = [] 106 | for ff in filters.split(","): 107 | key, val = ff.split("=") 108 | fl.append({"key": key, "value": val}) 109 | docs = sshless.list_documents(fl) 110 | param_map = { 111 | 'owner': 'Owner', 112 | 'platform': 'PlatformTypes', 113 | 'doc_version': 'DocumentVersion', 114 | 'doc_type': 'DocumentType', 115 | 'schema': 'SchemaVersion' 116 | } 117 | output = [] 118 | if all_details: 119 | output = docs 120 | else: 121 | for d in docs: 122 | doc_info = [{"Name": d["Name"]}] 123 | for k, v in param_map.items(): 124 | if ctx.params[k]: 125 | doc_info.append({k: d[v]}) 126 | output.append(doc_info) 127 | click.echo(format_json(output)) 128 | 129 | 130 | @cli.command() 131 | @click.argument('ssm-document') 132 | @click.option('-V', '--document-version', default=None, help='Document Version') 133 | @click.pass_context 134 | @catch_exceptions 135 | def get(ctx, ssm_document, document_version): 136 | """Get SSM document""" 137 | sshless = SSHLess(ctx.obj) 138 | 139 | params = {'Name': ssm_document} 140 | if document_version: 141 | params['DocumentVersion'] = version 142 | doc = sshless.ssm.get_document(**params) 143 | 144 | doc_info = doc['Name'] 145 | if 'DocumentVersion' in doc: 146 | doc_info += ' v{}'.format(doc['DocumentVersion']) 147 | if 'DocumentType' in doc: 148 | doc_info += ' {}'.format(doc['DocumentType']) 149 | click.echo("Name: {}".format(doc_info)) 150 | click.echo(doc['Content']) 151 | 152 | 153 | @cli.command() 154 | @click.argument('command') 155 | @click.option('-s', '--show-stats', is_flag=True, default=False) 156 | @click.option('-n', '--name', default=os.environ.get("SSHLESS_NAME_FILTER", None), help='Filter based on tag:Name') 157 | @click.option('-f', '--filters', default=os.environ.get("SSHLESS_FILTER", None), help='advanced Filter') 158 | @click.option('-i', '--instances', default=os.environ.get("SSHLESS_ID_FILTER", None), help='instances ID') 159 | @click.option('--maxconcurrency', default=None, help='Max concurrency allowed (Optional)') 160 | @click.option('--maxerrors', default=1, help='Max errors allowed (default: 1)') 161 | @click.option('--comment', default='sshless cli', help='Command invocation comment') 162 | @click.option('--interval', default=1, help='Check interval (default: 1.0s)') 163 | @click.option('--working-directory', default=None, help='workingDirectory') 164 | @click.option('--timeout', default=600, help='TimeoutSeconds - If this time is reached and the command has not already started executing, it will not execute.') 165 | @click.option('--s3-output', default=os.environ.get("SSHLESS_S3_OUTPUT", None), help='S3 output (Optional)') 166 | @click.option('--s3-key', default=os.environ.get("SSHLESS_S3_KEY", ""), help='S3 Key (Optional)') 167 | @click.option('--s3-region', default=os.environ.get("SSHLESS_S3_REGION", None), help='S3 region (Optional)') 168 | @click.option('--preserve-s3-output', is_flag=True, default=False, help='Preserve S3 output (Optional)') 169 | @click.pass_context 170 | @catch_exceptions 171 | def cmd(ctx, command, show_stats, name, filters, instances, maxconcurrency, maxerrors, comment, interval, working_directory, timeout, s3_output, s3_key, s3_region, preserve_s3_output): 172 | """SSM AWS-RunShellScript commands""" 173 | 174 | sshless = SSHLess(ctx.obj) 175 | if name and filters: 176 | click.echo("[{}] name and filters are mutually exclusive".format( 177 | colored("Error", "red"))) 178 | sys.exit(1) 179 | 180 | fl = [] 181 | params = { 182 | "DocumentName": "AWS-RunShellScript", 183 | "Parameters": {"commands": [command]}, 184 | "Comment": comment, 185 | "MaxErrors": str(maxerrors), 186 | "TimeoutSeconds": int(timeout) 187 | } 188 | 189 | if instances: 190 | if name or filters: 191 | click.echo("[{}] instances filters override tag or advanced filter".format( 192 | colored("Warn", "yellow"))) 193 | params["InstanceIds"] = instances.split(",") 194 | target = "InstanceIds: {}".format(instances) 195 | save_filter({"InstanceIds": params["InstanceIds"]}) 196 | 197 | elif name: 198 | if filters: 199 | click.echo("[{}] name filter override advanced filter".format( 200 | colored("Warn", "yellow"))) 201 | 202 | fl.append({'Key': 'tag:Name', 'Values': [name]}) 203 | params["Targets"] = fl 204 | save_filter({"Targets": params["Targets"]}) 205 | target = "Tag Name Filter: tag:Name={}".format(name) 206 | elif filters: 207 | for ff in filters.split(","): 208 | key, val = ff.split("=") 209 | fl.append({"Key": key, "Values": [val]}) 210 | params["Targets"] = fl 211 | target = "Tag Filter: {}".format(filters) 212 | save_filter({"Targets": params["Targets"]}) 213 | 214 | if all(v is None for v in [instances, name, filters]): 215 | cache_filter = read_filter() 216 | target = "Read from cache: {}".format(cache_filter) 217 | if cache_filter: 218 | logger.info("read filter from Cache: {}".format(cache_filter)) 219 | params = dict(params.items() + cache_filter.items()) 220 | else: 221 | raise ValueError("No valid Target - please check the online help") 222 | 223 | logger.debug("target: {}".format(target)) 224 | 225 | if maxconcurrency: 226 | params["MaxConcurrency"] = str(maxconcurrency) 227 | 228 | if working_directory: 229 | params["Parameters"]["workingDirectory"] = [str(working_directory)] 230 | 231 | if s3_output: 232 | params["OutputS3BucketName"] = s3_output 233 | if s3_key: 234 | params["OutputS3KeyPrefix"] = s3_key 235 | if s3_region: 236 | params["OutputS3Region"] = s3_region 237 | 238 | cmd_result = sshless.send_command(params) 239 | cmd_id = cmd_result['Command']['CommandId'] 240 | 241 | attempt = 0 242 | while True: 243 | time.sleep(0.2) 244 | attempt += 1 245 | if attempt % 10 == 0: 246 | logger.info( 247 | "attempt [{}] command {} is running please wait".format(attempt, cmd_id)) 248 | 249 | out = sshless.list_commands(CommandId=cmd_id)[0] 250 | 251 | if out['Status'] not in ['Pending', 'InProgress']: 252 | if out['TargetCount'] == out['CompletedCount']: 253 | 254 | if out["TargetCount"] == 0: 255 | click.echo(colored("TargetCount: 0", "red")) 256 | sys.exit(1) 257 | 258 | logger.debug(sshless.command_url(cmd_id)) 259 | logger.debug(format_json(out)) 260 | 261 | if show_stats: 262 | click.echo(get_report(out, target)) 263 | 264 | res = sshless.list_command_invocations(cmd_id) 265 | if len(res) != 0: 266 | if s3_output: 267 | # lookup for the stdout file inside s3 bucket 268 | status, instanceid, key, body = sshless.get_s3_output( 269 | cmd_id, s3_output, s3_key, s3_region) 270 | click.echo("[{}] {}".format( 271 | get_status(status), instanceid)) 272 | click.echo(body) 273 | if not preserve_s3_output: 274 | # delete stdout file inside s3 bucket 275 | sshless.delete_s3_output(key, s3_output, s3_region) 276 | 277 | else: 278 | for i in res: 279 | click.echo("[{}] {} {}".format(get_status( 280 | i['Status']), i['InstanceId'], i['InstanceName'])) 281 | for cp in i['CommandPlugins']: 282 | click.echo(cp['Output']) 283 | break 284 | 285 | time.sleep(interval) 286 | -------------------------------------------------------------------------------- /sshless/core.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import absolute_import 3 | import logging 4 | import boto3 5 | from . util import format_json 6 | 7 | 8 | logger = logging.getLogger("sshless") 9 | 10 | 11 | class SSHLess(object): 12 | """Summary 13 | 14 | Attributes: 15 | cfg (dict): Config with all related parameters 16 | ssm (boto3.client): SSM boto3.client 17 | target (list): SSM target 18 | """ 19 | 20 | def __init__(self, cfg): 21 | """Init class 22 | 23 | Args: 24 | args (object): CFG 25 | """ 26 | self.cfg = cfg 27 | self.credentials = {} 28 | self.ssm = self.get_client("ssm") 29 | 30 | def get_client(self, service): 31 | """boto3.client helper 32 | can return a simple boto3.client or execute an sts assume_role action 33 | 34 | Args: 35 | service (string): AWS service 36 | 37 | Returns: 38 | boto3.client: 39 | """ 40 | if self.cfg["iam"] == "": 41 | return boto3.client(service, region_name=self.cfg["region"]) 42 | 43 | if self.credentials == {}: 44 | logger.info("assume Role: {}".format(self.cfg["iam"])) 45 | sts_client = boto3.client("sts") 46 | self.credentials = sts_client.assume_role( 47 | RoleArn=self.cfg["iam"], 48 | RoleSessionName="sshless")["Credentials"] 49 | 50 | return boto3.client( 51 | service, 52 | region_name=self.cfg["region"], 53 | aws_access_key_id=self.credentials["AccessKeyId"], 54 | aws_secret_access_key=self.credentials["SecretAccessKey"], 55 | aws_session_token=self.credentials["SessionToken"]) 56 | 57 | def send_command(self, params): 58 | """send_command 59 | ssm send_command 60 | http://boto3.readthedocs.io/en/latest/reference/services/ssm.html#SSM.Client.send_command 61 | 62 | Args: 63 | params (object): SSM send_command object 64 | """ 65 | logger.info("Send command") 66 | logger.debug(format_json(params)) 67 | return self.ssm.send_command(**params) 68 | 69 | def list_commands(self, CommandId=None): 70 | """list_commands 71 | ssm list_commands 72 | http://boto3.readthedocs.io/en/latest/reference/services/ssm.html#SSM.Client.list_commands 73 | 74 | Args: 75 | CommandId (string): SSM CommandId 76 | """ 77 | 78 | params = { 79 | "CommandId": CommandId 80 | } 81 | 82 | command = self.ssm.list_commands(**params)['Commands'] 83 | logger.debug(command) 84 | return command 85 | 86 | def list_command_invocations(self, CommandId=None): 87 | """list_command_invocations 88 | ssm list_command_invocations 89 | http://boto3.readthedocs.io/en/latest/reference/services/ssm.html#SSM.Client.list_command_invocations 90 | 91 | Args: 92 | CommandId (string): SSM CommandId 93 | """ 94 | params = { 95 | 'Details': True, 96 | "CommandId": CommandId 97 | } 98 | invocation = self.ssm.list_command_invocations( 99 | **params)['CommandInvocations'] 100 | logger.debug(invocation) 101 | return invocation 102 | 103 | def list_documents(self, filters): 104 | """ Return a list of SSM Docutments """ 105 | ssm_max_results = 30 106 | data = self.ssm.list_documents( 107 | MaxResults=ssm_max_results, 108 | DocumentFilterList=filters) 109 | docs = data['DocumentIdentifiers'] 110 | while True: 111 | if 'NextToken' not in data: 112 | break 113 | data = self.ssm.list_documents( 114 | NextToken=data['NextToken'], 115 | MaxResults=ssm_max_results, 116 | DocumentFilterList=filters, 117 | ) 118 | docs += data['DocumentIdentifiers'] 119 | return docs 120 | 121 | def command_url(self, CommandId): 122 | if self.cfg["region"] is None: 123 | self.cfg["region"] = 'us-east-1' 124 | return 'https://console.aws.amazon.com/ec2/v2/home?region=' + \ 125 | self.cfg["region"] + '#Commands:CommandId=' + \ 126 | str(CommandId) + ';sort=CommandId' 127 | 128 | def delete_s3_output(self, key, s3_output, s3_region=None): 129 | logger.debug("deleting s3 : {}".format(key)) 130 | if s3_region: 131 | s3 = self.get_client("s3", region_name=s3_region) 132 | else: 133 | s3 = self.get_client("s3") 134 | 135 | s3.delete_object( 136 | Bucket=s3_output, 137 | Key=key 138 | ) 139 | 140 | def get_s3_output(self, cmd_id, s3_output, s3_key="", s3_region=None): 141 | if s3_region: 142 | s3 = self.get_client("s3", region_name=s3_region) 143 | else: 144 | s3 = self.get_client("s3") 145 | 146 | if s3_key: 147 | if s3_key.endswith("/"): 148 | s3_key = s3_key 149 | else: 150 | s3_key = "{}/".format(s3_key) 151 | 152 | # Create a paginator to pull 1000 objects at a time 153 | paginator = s3.get_paginator('list_objects') 154 | operation_parameters = {'Bucket': s3_output, 155 | 'Prefix': '{}{}/'.format(s3_key, cmd_id)} 156 | pageresponse = paginator.paginate(**operation_parameters) 157 | logger.info("List s3 output") 158 | logger.debug(operation_parameters) 159 | # PageResponse Holds 1000 objects 160 | for pageobject in pageresponse: 161 | if len(pageobject["Contents"]) > 1: 162 | logger.warn("more than one file found") 163 | logger.info(operation_parameters) 164 | logger.debug(pageobject["Contents"]) 165 | 166 | for obj in pageobject["Contents"]: 167 | 168 | if obj["Key"].endswith("stdout"): 169 | status = "Success" 170 | elif obj["Key"].endswith("stderr"): 171 | status = "Error" 172 | else: 173 | logger.warn("Unknown s3 obejct: {}".format(obj["Key"])) 174 | continue 175 | 176 | output = obj["Key"].split("/")[1] 177 | # GET s3 output 178 | out = s3.get_object(Bucket=s3_output, Key=obj["Key"])['Body'] 179 | 180 | return status, output, obj["Key"], out.read().decode('utf-8') 181 | -------------------------------------------------------------------------------- /sshless/util.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import json 4 | import os 5 | from termcolor import colored 6 | 7 | 8 | def get_status(status): 9 | if status == "Success": 10 | return colored(status, "green") 11 | else: 12 | return colored(status, "red") 13 | 14 | 15 | def json_serial(obj): 16 | """JSON serializer for objects not serializable by default json code 17 | >>> from datetime import date, datetime 18 | >>> json_serial(date(2018, 12, 12)) 19 | '2018-12-12' 20 | >>> json_serial(datetime(2018, 12, 12, 12, 12)) 21 | '2018-12-12T12:12:00' 22 | >>> json_serial("") 23 | Traceback (most recent call last): 24 | ... 25 | TypeError: Type not serializable 26 | """ 27 | try: 28 | return obj.isoformat() 29 | except AttributeError: 30 | raise TypeError("Type %s not serializable" % type(obj)) 31 | 32 | 33 | def format_json(s): 34 | return json.dumps(s, indent=2, default=json_serial) 35 | 36 | 37 | def get_report(i, target): 38 | return """[{}] 39 | CommandId: {} 40 | Requested: {} 41 | Command: {} 42 | {} 43 | Stats: Targets: {} Completed: {} Errors: {} 44 | """.format(get_status(i['Status']), 45 | i['CommandId'], 46 | i['RequestedDateTime'].replace(microsecond=0), 47 | i['Parameters']["commands"][0], 48 | target, 49 | i['TargetCount'], 50 | i['CompletedCount'], 51 | i['ErrorCount'] 52 | ) 53 | 54 | 55 | def save_filter(target): 56 | base_path = "{}/.sshless".format(os.path.expanduser('~')) 57 | if not os.path.exists(base_path): 58 | os.makedirs(base_path) 59 | 60 | with open("{}/filter".format(base_path), 'w') as cache: 61 | cache.write(json.dumps(target, indent=2)) 62 | 63 | 64 | def read_filter(): 65 | filter_file = "{}/.sshless/filter".format(os.path.expanduser('~')) 66 | if not os.path.exists(filter_file): 67 | return {} 68 | else: 69 | return json.load(open(filter_file)) 70 | -------------------------------------------------------------------------------- /test/DOC.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Official Doc 3 | ============ 4 | 5 | `Overview of Managing Access Permissions `_ 6 | 7 | `Restricting Run Command Access Based on Instance Tags `_ 8 | 9 | 10 | 11 | ========= 12 | Use Cases 13 | ========= 14 | 15 | `Manage Instances at Scale without SSH `_ 16 | 17 | `Parameter Store and IAM Roles for Tasks `_ 18 | 19 | `Parameters Store by Hierarchy, Tags `_ 20 | -------------------------------------------------------------------------------- /test/README.rst: -------------------------------------------------------------------------------- 1 | ==== 2 | DEMO 3 | ==== 4 | 5 | Overview 6 | -------- 7 | 8 | The demo is required to use Terraform to bootstrap the full scenario composed by: 9 | 10 | - 2 EC2 instances with the tag Role=web 11 | - 2 EC2 instances with the tag Role=app 12 | - 2 EC2 instances with the tag Role=legacy to simulate two OnPrem Virtual Machine 13 | - 1 SSM parameter Store entry 14 | - 3 IAM Roles 15 | - 1 Security Group 16 | - 1 VPC, 2 Public Subnet 17 | 18 | 19 | Diagram 20 | ******* 21 | 22 | .. image:: resources/diagram.png 23 | :align: center 24 | 25 | 26 | 27 | Let's start 28 | ----------- 29 | 30 | to bootstrap the scenario run the following commands 31 | :: 32 | 33 | $ cd test/terraform/ 34 | $ ./run.sh apply 35 | ..... 36 | 37 | 38 | 39 | to execute command on instances with ``tag:Role=web`` 40 | :: 41 | 42 | $ ./run.sh web 43 | + sshless cmd -f tag:Role=web 'wget -q -O - http://169.254.169.254/latest/dynamic/instance-identity/document' 44 | [Success] i-0681b64d16c5039b9 ip-10-0-28-176.eu-central-1.compute.internal 45 | { 46 | "devpayProductCodes" : null, 47 | "marketplaceProductCodes" : null, 48 | "availabilityZone" : "eu-central-1b", 49 | "version" : "2017-09-30", 50 | "region" : "eu-central-1", 51 | "instanceId" : "i-0681b64d16c5039b9", 52 | "billingProducts" : null, 53 | "instanceType" : "t2.micro", 54 | "privateIp" : "10.0.28.176", 55 | "pendingTime" : "2018-02-19T15:23:26Z", 56 | "accountId" : "111111111111", 57 | "architecture" : "x86_64", 58 | "kernelId" : null, 59 | "ramdiskId" : null, 60 | "imageId" : "ami-5652ce39" 61 | } 62 | [Success] i-0b17d5f281971ae1c ip-10-0-29-140.eu-central-1.compute.internal 63 | { 64 | "availabilityZone" : "eu-central-1b", 65 | "devpayProductCodes" : null, 66 | "marketplaceProductCodes" : null, 67 | "version" : "2017-09-30", 68 | "instanceId" : "i-0b17d5f281971ae1c", 69 | "billingProducts" : null, 70 | "instanceType" : "t2.micro", 71 | "imageId" : "ami-5652ce39", 72 | "privateIp" : "10.0.29.140", 73 | "pendingTime" : "2018-02-19T15:23:27Z", 74 | "accountId" : "111111111111", 75 | "architecture" : "x86_64", 76 | "kernelId" : null, 77 | "ramdiskId" : null, 78 | "region" : "eu-central-1" 79 | } 80 | 81 | 82 | to execute command virtual machine OnPrem 83 | :: 84 | 85 | $ ./run.sh legacy 86 | Onprem: Query using ID (Tags not available) ID: mi-07f7cd55f45a6b585,mi-09c4a25f6c0d1eac7 87 | + sshless cmd -i mi-07f7cd55f45a6b585,mi-09c4a25f6c0d1eac7 hostname 88 | [Success] mi-09c4a25f6c0d1eac7 89 | legacy-1 90 | 91 | [Success] mi-07f7cd55f45a6b585 92 | legacy-2 93 | 94 | 95 | to execute command virtual machine Azure 96 | :: 97 | 98 | $ ./run.sh azure 99 | Azure: Query using ID (Tags not available) ID: mi-0566401fcacec9264 100 | + sshless cmd -i mi-0566401fcacec9264 hostname 101 | [Success] mi-0566401fcacec9264 102 | azure-1 103 | 104 | 105 | 106 | to test the integration with Parameter Store 107 | :: 108 | 109 | $ ./run.sh parameter 110 | EC2 reading Parameter Store 111 | + sshless cmd -f tag:Purpose=sshless 'echo {{ssm:example.parameter}}' 112 | [Success] i-0b17d5f281971ae1c ip-10-0-29-140.eu-central-1.compute.internal 113 | I am an SSM parameter 114 | 115 | [Success] i-0207f41cec6b1678f ip-10-0-31-142.eu-central-1.compute.internal 116 | I am an SSM parameter 117 | 118 | [Success] i-0681b64d16c5039b9 ip-10-0-28-176.eu-central-1.compute.internal 119 | I am an SSM parameter 120 | 121 | [Success] i-03a92bd70a15713ec ip-10-0-30-236.eu-central-1.compute.internal 122 | I am an SSM parameter 123 | 124 | + set +x 125 | OnPrem reading Parameter Store 126 | Query using ID (Tags not available on prem) ID: mi-07f7cd55f45a6b585,mi-09c4a25f6c0d1eac7 127 | + sshless cmd -i mi-07f7cd55f45a6b585,mi-09c4a25f6c0d1eac7 'echo {{ssm:example.parameter}}' 128 | [Success] mi-09c4a25f6c0d1eac7 129 | I am an SSM parameter 130 | 131 | [Success] mi-07f7cd55f45a6b585 132 | I am an SSM parameter 133 | 134 | 135 | to delete everything 136 | :: 137 | 138 | $ cd test/terraform/ 139 | $ ./run.sh destroy 140 | ..... 141 | -------------------------------------------------------------------------------- /test/cassettes/test_cmd_with_instances_ids[sshless_cmd0].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{"DocumentName": "AWS-RunShellScript", "Parameters": {"commands": ["\"ls\""]}, 4 | "Comment": "sshless cli", "MaxErrors": "1", "TimeoutSeconds": 600, "InstanceIds": 5 | ["i-0b83e0b9f8f900500"]}' 6 | headers: 7 | Authorization: 8 | - !!binary | 9 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 10 | L2V1LXdlc3QtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlwZTto 11 | b3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9NGQ2MjE0N2Q5NWJmYjA2Yjk3 12 | Zjc5NmE0ZDhhOGI0ZGVjNTgzNzExMDc4Mjc0NjFmODExNTQ0NWU0NTBiMjBhOA== 13 | Content-Length: ['185'] 14 | Content-Type: 15 | - !!binary | 16 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 17 | User-Agent: 18 | - !!binary | 19 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuNCBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 20 | X-Amz-Date: 21 | - !!binary | 22 | MjAxODAyMTlUMjIzNjUyWg== 23 | X-Amz-Target: 24 | - !!binary | 25 | QW1hem9uU1NNLlNlbmRDb21tYW5k 26 | method: POST 27 | uri: https://ssm.eu-west-1.amazonaws.com/ 28 | response: 29 | body: {string: '{"Command":{"CommandId":"cff40d22-0b32-4525-bf4e-5d4bd867a6bc","Comment":"sshless 30 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519084012398E9,"InstanceIds":["i-0b83e0b9f8f900500"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["\"ls\""]},"RequestedDateTime":1.519079812398E9,"ServiceRole":"","Status":"Pending","StatusDetails":"Pending","TargetCount":1,"Targets":[]}}'} 31 | headers: 32 | Content-Length: ['609'] 33 | Content-Type: [application/x-amz-json-1.1] 34 | Date: ['Mon, 19 Feb 2018 22:36:51 GMT'] 35 | x-amzn-RequestId: [6181d7cd-15c5-11e8-95ee-69d72d92455f] 36 | status: {code: 200, message: OK} 37 | - request: 38 | body: '{"CommandId": "cff40d22-0b32-4525-bf4e-5d4bd867a6bc"}' 39 | headers: 40 | Authorization: 41 | - !!binary | 42 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 43 | L2V1LXdlc3QtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlwZTto 44 | b3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9NTEyZjY3YjY4ZWI1NzRiZmUx 45 | NGJmMTRkZjJkMjMxNzk3YzY4ZTdhMWIxMDJhNWE1MDY5ZjVkYWExNjU4MzZlZg== 46 | Content-Length: ['53'] 47 | Content-Type: 48 | - !!binary | 49 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 50 | User-Agent: 51 | - !!binary | 52 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuNCBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 53 | X-Amz-Date: 54 | - !!binary | 55 | MjAxODAyMTlUMjIzNjUyWg== 56 | X-Amz-Target: 57 | - !!binary | 58 | QW1hem9uU1NNLkxpc3RDb21tYW5kcw== 59 | method: POST 60 | uri: https://ssm.eu-west-1.amazonaws.com/ 61 | response: 62 | body: {string: '{"Commands":[{"CommandId":"cff40d22-0b32-4525-bf4e-5d4bd867a6bc","Comment":"sshless 63 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519084012398E9,"InstanceIds":["i-0b83e0b9f8f900500"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["\"ls\""]},"RequestedDateTime":1.519079812398E9,"ServiceRole":"","Status":"InProgress","StatusDetails":"InProgress","TargetCount":1,"Targets":[]}]}'} 64 | headers: 65 | Content-Length: ['618'] 66 | Content-Type: [application/x-amz-json-1.1] 67 | Date: ['Mon, 19 Feb 2018 22:36:51 GMT'] 68 | x-amzn-RequestId: [61b14c64-15c5-11e8-95ee-69d72d92455f] 69 | status: {code: 200, message: OK} 70 | - request: 71 | body: '{"CommandId": "cff40d22-0b32-4525-bf4e-5d4bd867a6bc"}' 72 | headers: 73 | Authorization: 74 | - !!binary | 75 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 76 | L2V1LXdlc3QtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlwZTto 77 | b3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9YjU1NzQxNTNiYzc3MWFmZTQx 78 | ZmYyMjY5OGNkNTEwMDc5YWE4MDJmZmY4M2ZjNTUxOTljNjQ0YzY4Njk3NmVlOA== 79 | Content-Length: ['53'] 80 | Content-Type: 81 | - !!binary | 82 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 83 | User-Agent: 84 | - !!binary | 85 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuNCBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 86 | X-Amz-Date: 87 | - !!binary | 88 | MjAxODAyMTlUMjIzNjUzWg== 89 | X-Amz-Target: 90 | - !!binary | 91 | QW1hem9uU1NNLkxpc3RDb21tYW5kcw== 92 | method: POST 93 | uri: https://ssm.eu-west-1.amazonaws.com/ 94 | response: 95 | body: {string: '{"Commands":[{"CommandId":"cff40d22-0b32-4525-bf4e-5d4bd867a6bc","Comment":"sshless 96 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519084012398E9,"InstanceIds":["i-0b83e0b9f8f900500"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["\"ls\""]},"RequestedDateTime":1.519079812398E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]}]}'} 97 | headers: 98 | Content-Length: ['612'] 99 | Content-Type: [application/x-amz-json-1.1] 100 | Date: ['Mon, 19 Feb 2018 22:36:52 GMT'] 101 | x-amzn-RequestId: [6273638b-15c5-11e8-95ee-69d72d92455f] 102 | status: {code: 200, message: OK} 103 | - request: 104 | body: '{"Details": true, "CommandId": "cff40d22-0b32-4525-bf4e-5d4bd867a6bc"}' 105 | headers: 106 | Authorization: 107 | - !!binary | 108 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 109 | L2V1LXdlc3QtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlwZTto 110 | b3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9MWU0MGI1NWE5NmY0NTFiZDNk 111 | NWM5ZjY4ODk4ZTBkZTA2ZGJhZGE0ZmVmYjdhYjgyODhkYzEzMTNjMDNmOTY2Ng== 112 | Content-Length: ['70'] 113 | Content-Type: 114 | - !!binary | 115 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 116 | User-Agent: 117 | - !!binary | 118 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuNCBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 119 | X-Amz-Date: 120 | - !!binary | 121 | MjAxODAyMTlUMjIzNjU0Wg== 122 | X-Amz-Target: 123 | - !!binary | 124 | QW1hem9uU1NNLkxpc3RDb21tYW5kSW52b2NhdGlvbnM= 125 | method: POST 126 | uri: https://ssm.eu-west-1.amazonaws.com/ 127 | response: 128 | body: {string: '{"CommandInvocations":[{"CommandId":"cff40d22-0b32-4525-bf4e-5d4bd867a6bc","CommandPlugins":[{"Name":"aws:runShellScript","Output":"bin\nboot\ncgroup\ndev\netc\nhome\nlib\nlib64\nlocal\nlost+found\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nselinux\nsrv\nsys\ntmp\nusr\nvar\n","OutputS3BucketName":"","OutputS3KeyPrefix":"","OutputS3Region":"eu-west-1","ResponseCode":0,"ResponseFinishDateTime":1.51907981274E9,"ResponseStartDateTime":1.519079812736E9,"StandardErrorUrl":"","StandardOutputUrl":"","Status":"Success","StatusDetails":"Success"}],"Comment":"sshless 129 | cli","DocumentName":"AWS-RunShellScript","DocumentVersion":"","InstanceId":"i-0b83e0b9f8f900500","InstanceName":"","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"RequestedDateTime":1.519079812478E9,"ServiceRole":"","StandardErrorUrl":"","StandardOutputUrl":"","Status":"Success","StatusDetails":"Success"}]}'} 130 | headers: 131 | Content-Length: ['911'] 132 | Content-Type: [application/x-amz-json-1.1] 133 | Date: ['Mon, 19 Feb 2018 22:36:53 GMT'] 134 | x-amzn-RequestId: [627f2368-15c5-11e8-95ee-69d72d92455f] 135 | status: {code: 200, message: OK} 136 | version: 1 137 | -------------------------------------------------------------------------------- /test/cassettes/test_list_commands[7ce628a4-e96c-4a52-88be-027750b6bf58].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{"CommandId": "7ce628a4-e96c-4a52-88be-027750b6bf58"}' 4 | headers: 5 | Authorization: 6 | - !!binary | 7 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 8 | L2V1LWNlbnRyYWwtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlw 9 | ZTtob3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9NTlhMjc3MWM1ZDkzMWQ5 10 | N2JiYWRlODliOTExMjE2ZDg2NGJmNmZhY2M0MGQ1YTZiMDdlYTI5MDhjOGM1M2NhYg== 11 | Content-Length: ['53'] 12 | Content-Type: 13 | - !!binary | 14 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 15 | User-Agent: 16 | - !!binary | 17 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuMiBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 18 | X-Amz-Date: 19 | - !!binary | 20 | MjAxODAyMTlUMTkxNDQzWg== 21 | X-Amz-Target: 22 | - !!binary | 23 | QW1hem9uU1NNLkxpc3RDb21tYW5kcw== 24 | method: POST 25 | uri: https://ssm.eu-central-1.amazonaws.com/ 26 | response: 27 | body: {string: '{"Commands":[{"CommandId":"7ce628a4-e96c-4a52-88be-027750b6bf58","Comment":"sshless 28 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51784561934E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 29 | -a"]},"RequestedDateTime":1.51784141934E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[{"Key":"tag:SSM","Values":["on"]}]}]}'} 30 | headers: 31 | Content-Length: ['624'] 32 | Content-Type: [application/x-amz-json-1.1] 33 | Date: ['Mon, 19 Feb 2018 19:14:43 GMT'] 34 | x-amzn-RequestId: [24876737-15a9-11e8-93b7-8f701c766373] 35 | status: {code: 200, message: OK} 36 | version: 1 37 | -------------------------------------------------------------------------------- /test/cassettes/test_list_commands[empty].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{}' 4 | headers: 5 | Authorization: 6 | - !!binary | 7 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 8 | L2V1LWNlbnRyYWwtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlw 9 | ZTtob3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9NDEwYzExZDRmNjIzNzcx 10 | YTk1YmQ2ODdhMzY1Zjc4MDRkZmVlOGUyNzc1NjE4YTc1ZGE2ZGM5NjQxMjNiMDMyNA== 11 | Content-Length: ['2'] 12 | Content-Type: 13 | - !!binary | 14 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 15 | User-Agent: 16 | - !!binary | 17 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuMiBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 18 | X-Amz-Date: 19 | - !!binary | 20 | MjAxODAyMTlUMTkyNTQ3Wg== 21 | X-Amz-Target: 22 | - !!binary | 23 | QW1hem9uU1NNLkxpc3RDb21tYW5kcw== 24 | method: POST 25 | uri: https://ssm.eu-central-1.amazonaws.com/ 26 | response: 27 | body: {string: '{"Commands":[{"CommandId":"c76bdd22-b503-4033-baeb-1f1e5ac0a28c","Comment":"sshless 28 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519058267367E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["wget 29 | -q -O - http://169.254.169.254/latest/dynamic/instance-identity/document"]},"RequestedDateTime":1.519054067367E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"ba884375-4ee0-4919-8855-a2f0419bf822","Comment":"sshless 30 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519058179319E9,"InstanceIds":["mi-07f7cd55f45a6b585","mi-09c4a25f6c0d1eac7"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 31 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.519053979319E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"65e48f90-c5fb-47ad-bfdd-c741fbc4e487","Comment":"sshless 32 | cli","CompletedCount":4,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519058176541E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 33 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.519053976541E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":4,"Targets":[{"Key":"tag:Purpose","Values":["sshless"]}]},{"CommandId":"ce4e8927-ed92-429e-a0de-ab24f2b8ca56","Comment":"sshless 34 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518446563076E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518442363076E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"87fe8341-ca39-4771-8367-4644a009178f","Comment":"sshless 35 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518442854078E9,"InstanceIds":["mi-0cb85d5e5b6ae28ab","mi-0e4ba7482c95656cd"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 36 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.518438654078E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"a87aef63-3f0e-4f7b-b991-d4bb9fb3f0ad","Comment":"sshless 37 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518442838013E9,"InstanceIds":["mi-0cb85d5e5b6ae28ab","mi-0e4ba7482c95656cd"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518438638013E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"0d30a6d4-2190-4176-825d-d1e4c27f75af","Comment":"sshless 38 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51844272792E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.51843852792E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"60f329e7-cf08-4224-aa96-07a55417ca3e","Comment":"sshless 39 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518435210192E9,"InstanceIds":["mi-0a576f28d468df81b"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 40 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.518431010192E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"996fa62c-bda3-420b-bfd9-296e43c2cd53","Comment":"sshless 41 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518434974794E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["ps 42 | aux"]},"RequestedDateTime":1.518430774794E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"47966694-3ffc-48e1-890b-18089942d09a","Comment":"sshless 43 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518434961862E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["ps 44 | aux"]},"RequestedDateTime":1.518430761862E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"1c78a7b3-6ae8-4389-9045-67d8e7abe926","Comment":"sshless 45 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518432803353E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 46 | -a"]},"RequestedDateTime":1.518428603353E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"7ead3296-6823-4d35-bd3a-2e4a9ff11de1","Comment":"sshless 47 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518432777879E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 48 | -a"]},"RequestedDateTime":1.518428577879E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"f585800c-a501-474c-8cd0-ebb50ddcc4ca","Comment":"sshless 49 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518432626781E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 50 | -a"]},"RequestedDateTime":1.518428426781E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"9fc4e4d1-8b69-4302-a53f-6ab4a2146b32","Comment":"sshless 51 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518089577415E9,"InstanceIds":["mi-011c034055d4e7332","mi-0714c5975cdb0efaa"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 52 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.518085377415E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"9411260b-6f30-455b-9c57-97448320936b","Comment":"sshless 53 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51808944229E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.51808524229E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["app"]}]},{"CommandId":"6c724895-bc58-44dc-ac31-62baa5768c62","Comment":"sshless 54 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518089342528E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518085142528E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"4c2c6e60-b045-4b39-803b-9f954888bab3","Comment":"sshless 55 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518085841264E9,"InstanceIds":["mi-012701a4cc61ad60f","mi-0c4d20abb5e260d36"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518081641264E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"12b54dd6-375b-4388-b650-e12cbd9ff7c6","Comment":"sshless 56 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083815865E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518079615865E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["legacy"]}]},{"CommandId":"9e2f15fb-8771-43d5-a29f-3bbbc22f40ef","Comment":"sshless 57 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083770519E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518079570519E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["app"]}]},{"CommandId":"60e4b4ec-9eb0-4f4e-8d5b-c37fc6f7f724","Comment":"sshless 58 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083465751E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518079265751E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"cf9596d3-e341-428f-bb01-437ce2b02299","Comment":"sshless 59 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083460519E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518079260519E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"7ee8e556-ede6-455c-b022-a60d8c9ca25d","Comment":"sshless 60 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083455526E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518079255526E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"27134386-9c32-4528-8a01-4cc110ada797","Comment":"sshless 61 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083168469E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078968469E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"94004cc0-06c4-49f7-a620-d16e92a52b62","Comment":"sshless 62 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083162977E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078962977E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"226d2a2d-cb60-45d3-ab4f-d113a9f6796a","Comment":"sshless 63 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083159169E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078959169E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"dcb47831-a927-4bc1-a504-d7557759d9d6","Comment":"sshless 64 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083156608E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078956608E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"283c6177-a20b-4433-bbcc-46c35323b398","Comment":"sshless 65 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518044002233E9,"InstanceIds":["mi-0c0dcb1cac6caaa63"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname 66 | && id"]},"RequestedDateTime":1.518039802233E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"69c176f8-b834-4d77-abb9-473b2e4ecaa2","Comment":"sshless 67 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518043582574E9,"InstanceIds":["mi-00c431b4a93cb2043"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname 68 | && id"]},"RequestedDateTime":1.518039382574E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"30f4c1cd-53d9-4f17-96cd-6dc4d903ddcc","Comment":"sshless 69 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518043552186E9,"InstanceIds":["mi-00c431b4a93cb2043"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518039352186E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"246707c8-f5c2-4a47-bd99-a1cee83858b2","Comment":"sshless 70 | cli","CompletedCount":4,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517849857714E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.517845657714E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":4,"Targets":[{"Key":"tag:Purpose","Values":["sshless"]}]},{"CommandId":"f3394735-16d1-4f18-8f81-0563d99237e1","Comment":"sshless 71 | cli","CompletedCount":4,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517849816932E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 72 | -a"]},"RequestedDateTime":1.517845616932E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":4,"Targets":[{"Key":"tag:Purpose","Values":["sshless"]}]},{"CommandId":"34fd7c72-9487-4cb9-8687-eed233ec6109","Comment":"sshless 73 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517847375014E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 74 | -a"]},"RequestedDateTime":1.517843175014E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"d1aa9545-d4c4-40fc-b09b-a025cfba8a91","Comment":"sshless 75 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517847309793E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 76 | -a"]},"RequestedDateTime":1.517843109793E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"62346399-6460-4df2-945f-7643f2efb537","Comment":"sshless 77 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517847299749E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 78 | -a"]},"RequestedDateTime":1.517843099749E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"2f757d7f-4195-463b-a953-408129190d3c","Comment":"sshless 79 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517846295031E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 80 | -a"]},"RequestedDateTime":1.51784209503E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"b476c343-99c2-49a6-9aee-502f64d1b045","Comment":"sshless 81 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517846169783E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 82 | -a"]},"RequestedDateTime":1.517841969783E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"f450c40c-e8bc-47af-b327-e1ae441cf2d7","Comment":"sshless 83 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517845835605E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 84 | -a"]},"RequestedDateTime":1.517841635605E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"b33c9bb1-882a-4788-8f3f-07069a615c39","Comment":"sshless 85 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517845828388E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 86 | -a"]},"RequestedDateTime":1.517841628388E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"75a387cd-3da5-43cf-b7e8-8368ea0ce2ab","Comment":"sshless 87 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517845667321E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 88 | -a"]},"RequestedDateTime":1.517841467321E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"7ce628a4-e96c-4a52-88be-027750b6bf58","Comment":"sshless 89 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51784561934E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 90 | -a"]},"RequestedDateTime":1.51784141934E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[{"Key":"tag:SSM","Values":["on"]}]}],"NextToken":"AAEAAR55utB+dlUDArkeheWus6PeYRb+5aSH0oK2qJJC0N2WAAAAAFqLJMDlfSVlasC2eIdbrU7IJVKxE42u9VhVOHZVgNrtxTX6AaJMcP5j1nFLqdB6X/gxc/9mgmDkubtsgKRdhNW6qEkrzBqPUEC4HujB1jsLuNlO8jbT3b+qP6ZFPWvTknyrxCnzC+K3ythkspKerySwGFqC8qv3w3oGV63nZrOq4cxRQyp/jglOE3JnpKUx+6CnvQbGL/pgV2J1WX/uiDganwiq7SIUaacWXtFH4nxz8bu50AKQxTpUo8S5c7BC4fkW8qfDiYvzXT1pKvMgmrYWW5UEMokz6Hc/nk5KCaAv0ItwDo2/2dqD6gzFOgNCStKf5VqK09Ie5SqshxxK1fMe5+J9s+d0t0fhs72OYsYmFH5gh/aqkWYy693cRfDohawUxalhmrBJTg2VV7dcraMJBhFPuHWDmAYxrancV/RLOqo+Fw=="}'} 91 | headers: 92 | Content-Length: ['25314'] 93 | Content-Type: [application/x-amz-json-1.1] 94 | Date: ['Mon, 19 Feb 2018 19:25:51 GMT'] 95 | x-amzn-RequestId: [b01a7ec1-15aa-11e8-90c8-2335b9937ac8] 96 | status: {code: 200, message: OK} 97 | version: 1 98 | -------------------------------------------------------------------------------- /test/cassettes/test_list_commands_ok[7ce628a4-e96c-4a52-88be-027750b6bf58].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{"CommandId": "7ce628a4-e96c-4a52-88be-027750b6bf58"}' 4 | headers: 5 | Authorization: 6 | - !!binary | 7 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 8 | L2V1LWNlbnRyYWwtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlw 9 | ZTtob3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9MzI1OTAxM2NmMGZjMzJk 10 | ZDU2ZDMwNjg2YzNhMmE1NDY5M2M1NjQyYTQ0NDc4NTBiZjdmYTg4ZjI3YTAxMDg2ZQ== 11 | Content-Length: ['53'] 12 | Content-Type: 13 | - !!binary | 14 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 15 | User-Agent: 16 | - !!binary | 17 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuMiBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 18 | X-Amz-Date: 19 | - !!binary | 20 | MjAxODAyMTlUMTkzNzU5Wg== 21 | X-Amz-Target: 22 | - !!binary | 23 | QW1hem9uU1NNLkxpc3RDb21tYW5kcw== 24 | method: POST 25 | uri: https://ssm.eu-central-1.amazonaws.com/ 26 | response: 27 | body: {string: '{"Commands":[{"CommandId":"7ce628a4-e96c-4a52-88be-027750b6bf58","Comment":"sshless 28 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51784561934E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 29 | -a"]},"RequestedDateTime":1.51784141934E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[{"Key":"tag:SSM","Values":["on"]}]}]}'} 30 | headers: 31 | Content-Length: ['624'] 32 | Content-Type: [application/x-amz-json-1.1] 33 | Date: ['Mon, 19 Feb 2018 19:37:59 GMT'] 34 | x-amzn-RequestId: [6410f068-15ac-11e8-b899-c329fc5f2f36] 35 | status: {code: 200, message: OK} 36 | version: 1 37 | -------------------------------------------------------------------------------- /test/cassettes/test_list_commands_ok[empty].yaml: -------------------------------------------------------------------------------- 1 | interactions: 2 | - request: 3 | body: '{}' 4 | headers: 5 | Authorization: 6 | - !!binary | 7 | QVdTNC1ITUFDLVNIQTI1NiBDcmVkZW50aWFsPUFLSUFJTzZZTFdHREhRUVlGM0hBLzIwMTgwMjE5 8 | L2V1LWNlbnRyYWwtMS9zc20vYXdzNF9yZXF1ZXN0LCBTaWduZWRIZWFkZXJzPWNvbnRlbnQtdHlw 9 | ZTtob3N0O3gtYW16LWRhdGU7eC1hbXotdGFyZ2V0LCBTaWduYXR1cmU9NGFkZTJlMDA1ZGM0MTAz 10 | OWQxOTZlMWExM2I1YmM4OTdlMTYxYmNiY2VhZjhjYjExNmRiY2RkY2Q4NzFkODg3Yw== 11 | Content-Length: ['2'] 12 | Content-Type: 13 | - !!binary | 14 | YXBwbGljYXRpb24veC1hbXotanNvbi0xLjE= 15 | User-Agent: 16 | - !!binary | 17 | Qm90bzMvMS41LjMxIFB5dGhvbi8zLjYuMiBEYXJ3aW4vMTYuNy4wIEJvdG9jb3JlLzEuOC40NQ== 18 | X-Amz-Date: 19 | - !!binary | 20 | MjAxODAyMTlUMTkzNzU4Wg== 21 | X-Amz-Target: 22 | - !!binary | 23 | QW1hem9uU1NNLkxpc3RDb21tYW5kcw== 24 | method: POST 25 | uri: https://ssm.eu-central-1.amazonaws.com/ 26 | response: 27 | body: {string: '{"Commands":[{"CommandId":"c76bdd22-b503-4033-baeb-1f1e5ac0a28c","Comment":"sshless 28 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519058267367E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["wget 29 | -q -O - http://169.254.169.254/latest/dynamic/instance-identity/document"]},"RequestedDateTime":1.519054067367E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"ba884375-4ee0-4919-8855-a2f0419bf822","Comment":"sshless 30 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519058179319E9,"InstanceIds":["mi-07f7cd55f45a6b585","mi-09c4a25f6c0d1eac7"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 31 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.519053979319E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"65e48f90-c5fb-47ad-bfdd-c741fbc4e487","Comment":"sshless 32 | cli","CompletedCount":4,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.519058176541E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 33 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.519053976541E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":4,"Targets":[{"Key":"tag:Purpose","Values":["sshless"]}]},{"CommandId":"ce4e8927-ed92-429e-a0de-ab24f2b8ca56","Comment":"sshless 34 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518446563076E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518442363076E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"87fe8341-ca39-4771-8367-4644a009178f","Comment":"sshless 35 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518442854078E9,"InstanceIds":["mi-0cb85d5e5b6ae28ab","mi-0e4ba7482c95656cd"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 36 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.518438654078E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"a87aef63-3f0e-4f7b-b991-d4bb9fb3f0ad","Comment":"sshless 37 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518442838013E9,"InstanceIds":["mi-0cb85d5e5b6ae28ab","mi-0e4ba7482c95656cd"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518438638013E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"0d30a6d4-2190-4176-825d-d1e4c27f75af","Comment":"sshless 38 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51844272792E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.51843852792E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"60f329e7-cf08-4224-aa96-07a55417ca3e","Comment":"sshless 39 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518435210192E9,"InstanceIds":["mi-0a576f28d468df81b"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 40 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.518431010192E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"996fa62c-bda3-420b-bfd9-296e43c2cd53","Comment":"sshless 41 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518434974794E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["ps 42 | aux"]},"RequestedDateTime":1.518430774794E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"47966694-3ffc-48e1-890b-18089942d09a","Comment":"sshless 43 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518434961862E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["ps 44 | aux"]},"RequestedDateTime":1.518430761862E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"1c78a7b3-6ae8-4389-9045-67d8e7abe926","Comment":"sshless 45 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518432803353E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 46 | -a"]},"RequestedDateTime":1.518428603353E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"7ead3296-6823-4d35-bd3a-2e4a9ff11de1","Comment":"sshless 47 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518432777879E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 48 | -a"]},"RequestedDateTime":1.518428577879E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"f585800c-a501-474c-8cd0-ebb50ddcc4ca","Comment":"sshless 49 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518432626781E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 50 | -a"]},"RequestedDateTime":1.518428426781E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"9fc4e4d1-8b69-4302-a53f-6ab4a2146b32","Comment":"sshless 51 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518089577415E9,"InstanceIds":["mi-011c034055d4e7332","mi-0714c5975cdb0efaa"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["echo 52 | {{ssm:example.parameter}}"]},"RequestedDateTime":1.518085377415E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"9411260b-6f30-455b-9c57-97448320936b","Comment":"sshless 53 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51808944229E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.51808524229E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["app"]}]},{"CommandId":"6c724895-bc58-44dc-ac31-62baa5768c62","Comment":"sshless 54 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518089342528E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518085142528E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"4c2c6e60-b045-4b39-803b-9f954888bab3","Comment":"sshless 55 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518085841264E9,"InstanceIds":["mi-012701a4cc61ad60f","mi-0c4d20abb5e260d36"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518081641264E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[]},{"CommandId":"12b54dd6-375b-4388-b650-e12cbd9ff7c6","Comment":"sshless 56 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083815865E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518079615865E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["legacy"]}]},{"CommandId":"9e2f15fb-8771-43d5-a29f-3bbbc22f40ef","Comment":"sshless 57 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083770519E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518079570519E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:Role","Values":["app"]}]},{"CommandId":"60e4b4ec-9eb0-4f4e-8d5b-c37fc6f7f724","Comment":"sshless 58 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083465751E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518079265751E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"cf9596d3-e341-428f-bb01-437ce2b02299","Comment":"sshless 59 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083460519E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518079260519E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"7ee8e556-ede6-455c-b022-a60d8c9ca25d","Comment":"sshless 60 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083455526E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518079255526E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"27134386-9c32-4528-8a01-4cc110ada797","Comment":"sshless 61 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083168469E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078968469E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"94004cc0-06c4-49f7-a620-d16e92a52b62","Comment":"sshless 62 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083162977E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078962977E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"226d2a2d-cb60-45d3-ab4f-d113a9f6796a","Comment":"sshless 63 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083159169E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078959169E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"dcb47831-a927-4bc1-a504-d7557759d9d6","Comment":"sshless 64 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518083156608E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["id"]},"RequestedDateTime":1.518078956608E9,"ServiceRole":"","Status":"Success","StatusDetails":"NoInstancesInTag","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["web"]}]},{"CommandId":"283c6177-a20b-4433-bbcc-46c35323b398","Comment":"sshless 65 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518044002233E9,"InstanceIds":["mi-0c0dcb1cac6caaa63"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname 66 | && id"]},"RequestedDateTime":1.518039802233E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"69c176f8-b834-4d77-abb9-473b2e4ecaa2","Comment":"sshless 67 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518043582574E9,"InstanceIds":["mi-00c431b4a93cb2043"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname 68 | && id"]},"RequestedDateTime":1.518039382574E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"30f4c1cd-53d9-4f17-96cd-6dc4d903ddcc","Comment":"sshless 69 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.518043552186E9,"InstanceIds":["mi-00c431b4a93cb2043"],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.518039352186E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[]},{"CommandId":"246707c8-f5c2-4a47-bd99-a1cee83858b2","Comment":"sshless 70 | cli","CompletedCount":4,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517849857714E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["hostname"]},"RequestedDateTime":1.517845657714E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":4,"Targets":[{"Key":"tag:Purpose","Values":["sshless"]}]},{"CommandId":"f3394735-16d1-4f18-8f81-0563d99237e1","Comment":"sshless 71 | cli","CompletedCount":4,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517849816932E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 72 | -a"]},"RequestedDateTime":1.517845616932E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":4,"Targets":[{"Key":"tag:Purpose","Values":["sshless"]}]},{"CommandId":"34fd7c72-9487-4cb9-8687-eed233ec6109","Comment":"sshless 73 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517847375014E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 74 | -a"]},"RequestedDateTime":1.517843175014E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"d1aa9545-d4c4-40fc-b09b-a025cfba8a91","Comment":"sshless 75 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517847309793E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 76 | -a"]},"RequestedDateTime":1.517843109793E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"62346399-6460-4df2-945f-7643f2efb537","Comment":"sshless 77 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517847299749E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 78 | -a"]},"RequestedDateTime":1.517843099749E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:Role","Values":["WEB"]}]},{"CommandId":"2f757d7f-4195-463b-a953-408129190d3c","Comment":"sshless 79 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517846295031E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 80 | -a"]},"RequestedDateTime":1.51784209503E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"b476c343-99c2-49a6-9aee-502f64d1b045","Comment":"sshless 81 | cli","CompletedCount":0,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517846169783E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 82 | -a"]},"RequestedDateTime":1.517841969783E9,"ServiceRole":"","Status":"Failed","StatusDetails":"Failed","TargetCount":0,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"f450c40c-e8bc-47af-b327-e1ae441cf2d7","Comment":"sshless 83 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517845835605E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 84 | -a"]},"RequestedDateTime":1.517841635605E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"b33c9bb1-882a-4788-8f3f-07069a615c39","Comment":"sshless 85 | cli","CompletedCount":2,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517845828388E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 86 | -a"]},"RequestedDateTime":1.517841628388E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":2,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"75a387cd-3da5-43cf-b7e8-8368ea0ce2ab","Comment":"sshless 87 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.517845667321E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 88 | -a"]},"RequestedDateTime":1.517841467321E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[{"Key":"tag:SSM","Values":["on"]}]},{"CommandId":"7ce628a4-e96c-4a52-88be-027750b6bf58","Comment":"sshless 89 | cli","CompletedCount":1,"DocumentName":"AWS-RunShellScript","DocumentVersion":"","ErrorCount":0,"ExpiresAfter":1.51784561934E9,"InstanceIds":[],"Interactive":false,"MaxConcurrency":"50","MaxErrors":"1","NotificationConfig":{"NotificationArn":"","NotificationEvents":[],"NotificationType":""},"OutputS3BucketName":"","OutputS3KeyPrefix":"","Parameters":{"commands":["uname 90 | -a"]},"RequestedDateTime":1.51784141934E9,"ServiceRole":"","Status":"Success","StatusDetails":"Success","TargetCount":1,"Targets":[{"Key":"tag:SSM","Values":["on"]}]}],"NextToken":"AAEAAU5D0sPUe+eQ2nOxy416a6gN0BoR9ccVpEIKWdQUPvQeAAAAAFqLJ5eDpLuXyierONuBrs7Wc71EvdwsyQXXUUpksQhKGFj72f+rxuBgRx640BE6OcFdpEU7OyhbUTnDY8IVUzeGElMOdmedUN9RrFFKIZrIzjbqgePQ9973Ng7gUoZxZ5jlWJFaLOE4DTRmtZxHAd8IACx032y0zXZK6Jo27ts/c3LSblN+AbdCddlEXF61KSMa8Pv3ie96pVLUm0RqQGqlLmhHPttxl24PQuo6/DMR1wkU3NRcNAF23DIypJ8lMGO/48rsemtoyED4ipmMGXn6EGQl84HpIoMYk6QsvTQwS0YuIX6pBU4whKuaJz7Szc2DIWdGxsLT4kqecHE5uq4Vae0/kj0vkE1savJ+E2+WW5B5YNOs8q+Qp2BLaM8CLGFxugDmNVebibr/yxYyYdKpTO2eBj4APr+6mDIWBvf/tZ00dg=="}'} 91 | headers: 92 | Content-Length: ['25314'] 93 | Content-Type: [application/x-amz-json-1.1] 94 | Date: ['Mon, 19 Feb 2018 19:37:58 GMT'] 95 | x-amzn-RequestId: [63e54c88-15ac-11e8-90c8-2335b9937ac8] 96 | status: {code: 200, message: OK} 97 | version: 1 98 | -------------------------------------------------------------------------------- /test/conftest.py: -------------------------------------------------------------------------------- 1 | from click.testing import CliRunner 2 | import os 3 | import pytest 4 | from sshless.core import SSHLess 5 | from sshless.cli import cli 6 | 7 | 8 | @pytest.fixture(scope='session') 9 | def sshless_app(): 10 | return SSHLess({'iam': '', 'region': 'eu-central-1'}) 11 | 12 | 13 | @pytest.fixture() 14 | def homedir(tmpdir): 15 | save_home = os.environ.get('HOME', '') 16 | os.environ['HOME'] = str(tmpdir) 17 | yield tmpdir 18 | os.environ['HOME'] = save_home 19 | 20 | 21 | @pytest.fixture(scope='session') 22 | def sshless_cmd(request): 23 | args, env = request.param 24 | runner = CliRunner() 25 | yield runner.invoke(cli=cli, args=args) 26 | -------------------------------------------------------------------------------- /test/resources/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudreach/sshless/04a40a9ad99baab1acb9f5568687a698e4baf659/test/resources/diagram.png -------------------------------------------------------------------------------- /test/terraform/azure.tf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | data "template_file" "user_data_azure-1" { 6 | template = "${file("user_data.tpl")}" 7 | 8 | vars { 9 | hostname = "azure-1" 10 | region = "${var.region}" 11 | activation_code = "${aws_ssm_activation.ssm_activation-a1.activation_code}" 12 | activation_id = "${aws_ssm_activation.ssm_activation-a1.id}" 13 | } 14 | } 15 | 16 | resource "aws_instance" "azure-1" { 17 | count = 1 18 | ami = "${data.aws_ami.amazon_linux.id}" 19 | instance_type = "t2.micro" 20 | subnet_id = "${element(aws_subnet.public.*.id, 0)}" 21 | user_data = "${data.template_file.user_data_azure-1.rendered}" 22 | vpc_security_group_ids = ["${aws_security_group.sg_base.id}"] 23 | tags = "${merge( 24 | map("Name", "azure-1"), 25 | map("Owner", "demo"), 26 | map("Role", "azure"), 27 | map("Purpose", "sshless-azure") 28 | )}" 29 | } 30 | -------------------------------------------------------------------------------- /test/terraform/ec2.tf: -------------------------------------------------------------------------------- 1 | variable "user_data" { 2 | description = "ec2 user_data" 3 | default = < 0 23 | # assert '7ce628a4-e96c-4a52-88be-027750b6bf58' in [cmd['CommandId'] for cmd in cmds] 24 | -------------------------------------------------------------------------------- /test/test_util.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from sshless.util import read_filter 3 | 4 | 5 | @pytest.mark.usefixtures('homedir') 6 | def test_read_filter_file_not_exist(): 7 | result = read_filter() 8 | assert result == {} 9 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # tox (https://tox.readthedocs.io/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py27, py36 8 | 9 | [testenv] 10 | commands = pytest --doctest-modules --doctest-ignore-import-errors --vcr-record-mode=none 11 | deps = 12 | pytest-vcr 13 | --------------------------------------------------------------------------------