├── .gitignore ├── setup.py ├── .github └── dependabot.yml ├── LICENSE ├── README.md └── awsec2info.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | awsplugins.egg-info 3 | build 4 | awsec2info.egg-info 5 | __pycache__ 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="awsec2info", 5 | version="0.1", 6 | py_modules=["awsec2info"], 7 | install_requires=["awscli"], 8 | ) 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Jürgen 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-cli-ec2-plugin 2 | Basic AWS CLI extension for fetching data related to EC2 instances using tag keys. 3 | 4 | ## Installation 5 | From GitHub 6 | ```shell 7 | python -m pip install git+https://github.com/pyToshka/aws-cli-ec2-plugin.git 8 | 9 | ``` 10 | Local installation 11 | 12 | ```shell 13 | git clone https://github.com/pyToshka/aws-cli-ec2-plugin.git 14 | cd aws-cli-ec2-plugin 15 | pip install . 16 | ``` 17 | 18 | ## Configuration 19 | 20 | For enabling the plugin: 21 | ```shell 22 | 23 | aws configure set plugins.ec2info awsec2info 24 | 25 | ``` 26 | ## Options 27 | ```text 28 | 29 | Options: 30 | --tag TEXT Tag name [required] 31 | --output [json|plain|table] Output format for received information 32 | --attribute [name|type|state|private_ip|public_ip] Get single attribute 33 | --region TEXT AWS region name. Default us-east-1 34 | ``` 35 | ## Simple output 36 | 37 | ### Text 38 | 39 | ```shell 40 | aws ec2info --tag Name --region us-west-2 41 | ``` 42 | output 43 | ```text 44 | ------ 45 | name: test 46 | type: t3.large 47 | state: stopped 48 | private_ip: 127.0.0.1 49 | public_ip: None 50 | ------ 51 | 52 | ``` 53 | ### Json 54 | 55 | ```shell 56 | aws ec2info --tag Name --output json --region us-west-2 57 | ``` 58 | output 59 | ```text 60 | { 61 | "name": "test", 62 | "private_ip": "127.0.0.1", 63 | "public_ip": null, 64 | "state": "stopped", 65 | "type": "t3.large" 66 | } 67 | ] 68 | 69 | ``` 70 | 71 | ### Table 72 | 73 | ```shell 74 | aws ec2info --tag Name --output table --region us-west-2 75 | ``` 76 | output 77 | ```text 78 | name | type | state | private_ip | public_ip 79 | --------- | --------- | ------- | ------------- | -------------- 80 | test | t3.large | stopped | 127.0.0. | 81 | 82 | ``` 83 | 84 | Enjoy 85 | -------------------------------------------------------------------------------- /awsec2info.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from awscli.customizations.commands import BasicCommand 3 | import ast 4 | import pprint 5 | import boto3 6 | 7 | import json 8 | 9 | LOG = logging.getLogger(__name__) 10 | 11 | 12 | def pprint_json(json_array, sort=True, indents=4): 13 | if isinstance(json_array, str): 14 | print(json.dumps(json.loads(json_array), sort_keys=sort, indent=indents)) 15 | else: 16 | print(json.dumps(json_array, sort_keys=sort, indent=indents)) 17 | return None 18 | 19 | 20 | def pprint_table(my_dict, col_list=None): 21 | """ 22 | :param my_dict: 23 | :param col_list: 24 | """ 25 | if not col_list: 26 | col_list = list(my_dict[0].keys() if my_dict else []) 27 | my_list = [col_list] # 1st row = header 28 | for item in my_dict: 29 | my_list.append( 30 | [str(item[col] if item[col] is not None else "") for col in col_list] 31 | ) 32 | col_size = [max(map(len, col)) for col in zip(*my_list)] 33 | format_str = " | ".join(["{{:<{}}}".format(i) for i in col_size]) 34 | my_list.insert(1, ["-" * i for i in col_size]) # Seperating line 35 | for item in my_list: 36 | print(format_str.format(*item)) 37 | 38 | 39 | def get_aws_info(tag, region="us-east-1", output="plain", attribute=None): 40 | """Getting information about EC2 instance based on tag name""" 41 | if region is None: 42 | region = "us-east-1" 43 | print( 44 | f"Retrieving information for ec2 instance based on input parameters tag - {tag}, region {region} and attribute {attribute} output format is {output}" 45 | ) 46 | ec2 = boto3.resource("ec2", region_name=region) 47 | instances = {} 48 | instances_list = [] 49 | name = "" 50 | for instance in ec2.instances.all(): 51 | if instance.tags is None: 52 | continue 53 | for tags in instance.tags: 54 | if "Name" in tags["Key"]: 55 | name = tags["Value"] 56 | if tags["Key"] == tag: 57 | try: 58 | public_ip = instance.public_ip_address 59 | except AttributeError: 60 | public_ip = "None" 61 | try: 62 | private_ip = instance.private_ip_address 63 | except AttributeError: 64 | private_ip = "None" 65 | instances[instance.id] = { 66 | "name": name, 67 | "type": instance.instance_type, 68 | "state": instance.state["Name"], 69 | "private_ip": private_ip, 70 | "public_ip": public_ip, 71 | } 72 | if attribute: 73 | for instance_id, instance in instances.items(): 74 | print(ast.literal_eval(pprint.pformat(instance[attribute]))) 75 | if output == "json" and not attribute: 76 | for instance_id, instance in instances.items(): 77 | instances_list.append(instance) 78 | pprint_json(instances_list) 79 | 80 | elif output == "table" and not attribute: 81 | for instance_id, instance in instances.items(): 82 | instances_list.append(instance) 83 | pprint_table(instances_list) 84 | else: 85 | if attribute: 86 | pass 87 | 88 | else: 89 | attributes = ["name", "type", "state", "private_ip", "public_ip"] 90 | for instance_id, instance in instances.items(): 91 | for key in attributes: 92 | print("{0}: {1}".format(key, instance[key])) 93 | print("------") 94 | 95 | 96 | class Ec2InfoPluginError(Exception): 97 | pass 98 | 99 | 100 | def awscli_initialize(cli): 101 | cli.register("building-command-table.main", inject_commands) 102 | 103 | 104 | def inject_commands(command_table, session, **kwargs): 105 | command_table["ec2info"] = Ec2InfoPlugin(session) 106 | 107 | 108 | class Ec2InfoPlugin(BasicCommand): 109 | NAME = "ec2info" 110 | DESCRIPTION = "Retrieving information about EC2 instances based on tag key" 111 | SYNOPSIS = "aws ec2info [--tag TAG --output [json|plain|table] --attribute [name|type|state|private_ip|public_ip]]" 112 | ARG_TABLE = [ 113 | {"name": "tag", "required": True, "help_text": "Tag name"}, 114 | { 115 | "name": "output", 116 | "required": False, 117 | "default": "plain", 118 | "help_text": "Output format for received information. Accept json|plain|table", 119 | }, 120 | { 121 | "name": "attribute", 122 | "required": False, 123 | "help_text": "Get single attribute. Accept [name|type|state|private_ip|public_ip]", 124 | }, 125 | ] 126 | UPDATE = False 127 | 128 | def _run_main(self, args, parsed_globals): 129 | """Run the command and report success.""" 130 | logging.basicConfig(level=logging.INFO) 131 | for handler in logging.root.handlers: 132 | handler.addFilter(logging.Filter(__name__)) 133 | self._call(args, parsed_globals) 134 | 135 | return 0 136 | 137 | def _call(self, options, parsed_globals): 138 | """Run the command.""" 139 | get_aws_info( 140 | tag=options.tag, 141 | region=parsed_globals.region, 142 | output=parsed_globals.output, 143 | attribute=options.attribute, 144 | ) 145 | --------------------------------------------------------------------------------