PKJzL$;$;azuresshconfig.py#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'Yoichi Kawasaki' import sys import os import argparse import simplejson as json try: from msrestazure.azure_active_directory import ServicePrincipalCredentials from azure.mgmt.resource import ResourceManagementClient from azure.mgmt.compute import ComputeManagementClient from azure.mgmt.network import NetworkManagementClient from azure.mgmt.network.models.public_ip_address_dns_settings import PublicIPAddressDnsSettings except ImportError: pass ### Global Defines _AZURE_SSH_CONFIG_VERSION = '0.3.0' _AZURE_SSH_CONFIG_HOME_SITE = 'https://github.com/yokawasa/azure-ssh-config' _DEFAULT_AZURE_SSH_CONFIG_JSON_FILE = '{}/.azure/azuresshconfig.json'.format(os.environ['HOME']) _DEFAULT_SSH_CONFIG_FILE = '{}/.ssh/config'.format(os.environ['HOME']) _DEFAULT_SSH_CONFIG_BLOCK_START_MAKR = "### AZURE-SSH-CONFIG BEGIN ###" _DEFAULT_SSH_CONFIG_BLOCK_END_MARK = "### AZURE-SSH-CONFIG END ###" class SSHConfigBlock: def __init__(self): self._entries = [] # definitions below are collected from man ssh_config(5) self._ssh_config_param_defines = ['Host', 'AddressFamily', 'BatchMode', 'BindAddress', 'ChallengeResponseAuthentication', 'CheckHostIP', 'Cipher', 'Ciphers', 'ClearAllForwardings', 'Compression', 'CompressionLevel', 'ConnectionAttempts', 'ConnectTimeout', 'ControlMaster', 'ControlPath', 'ControlPersist', 'DynamicForward', 'EnableSSHKeysign', 'EscapeChar', 'ExitOnForwardFailure', 'ForwardAgent', 'ForwardX11', 'ForwardX11Timeout', 'ForwardX11Trusted', 'GatewayPorts', 'GlobalKnownHostsFile', 'GSSAPIAuthentication', 'GSSAPIDelegateCredentials', 'HashKnownHosts', 'HostbasedAuthentication', 'HostKeyAlgorithms', 'HostKeyAlias', 'HostName', 'IdentitiesOnly', 'IdentityFile', 'IgnoreUnknown', 'IPQoS', 'KbdInteractiveAuthentication', 'KbdInteractiveDevices', 'KexAlgorithms', 'LocalCommand', 'LocalForward', 'LogLevel', 'MACs', 'NoHostAuthenticationForLocalhost', 'NumberOfPasswordPrompts', 'PasswordAuthentication', 'PermitLocalCommand', 'PKCS11Provider', 'Port', 'PreferredAuthentications', 'Protocol', 'ProxyCommand', 'PubkeyAuthentication', 'RekeyLimit', 'RemoteForward', 'RequestTTY', 'RhostsRSAAuthentication', 'RSAAuthentication', 'SendEnv', 'ServerAliveCountMax', 'ServerAliveInterval', 'StrictHostKeyChecking', 'TCPKeepAlive', 'Tunnel', 'TunnelDevice', 'UsePrivilegedPort', 'User', 'UserKnownHostsFile', 'VerifyHostKeyDNS', 'VersionAddendum', 'VisualHostKey', 'XAuthLocation' ] def add_entry(self,entry_name, access_address, params ): entry = { 'Name' : entry_name, 'HostName': access_address } for d in self._ssh_config_param_defines: if exists_in_dict(d, params): entry[d] = params[d] self._entries.append(entry) def to_string(self): buffer = '' for entry in self._entries: buffer += "Host {}\n".format(entry['Name']) buffer += " HostName {}\n".format(entry['HostName']) for d in self._ssh_config_param_defines: if exists_in_dict(d, entry) and d != 'HostName': buffer += " {0} {1}\n".format(d, entry[d]) buffer += "\n" return buffer class SSHConfig: def __init__(self, sshconfig=_DEFAULT_SSH_CONFIG_FILE, block_start_mark=_DEFAULT_SSH_CONFIG_BLOCK_START_MAKR, block_end_mark=_DEFAULT_SSH_CONFIG_BLOCK_END_MARK ): self.sshconfig = sshconfig self._block_start_mark = "{}\n".format(block_start_mark) self._block_end_mark = "{}\n".format(block_end_mark) self._block = '' self._pre_block = '' self._post_block = '' self.__prepare() self.__parse() def __prepare(self): if not os.path.exists(self.sshconfig): # Create an Empty SSH Config file open(self.sshconfig, 'a').close() def __parse(self): try: f = open(self.sshconfig, "r") contents = f.read() start_block_mark_startpos = contents.find(self._block_start_mark) if (start_block_mark_startpos > -1): block_startpos = start_block_mark_startpos + len(self._block_start_mark) block_endpos = contents.find(self._block_end_mark, block_startpos) if (block_endpos > -1): end_block_mark_endpos = block_endpos + len(self._block_end_mark) self._block = contents[block_startpos:block_endpos] self._pre_block = contents[:start_block_mark_startpos] self._post_block = contents[end_block_mark_endpos:] else: print_err("You have start block mark but not end mark!") raise Exception("Unexpected error: invlid block mark: {0}".format(self.sshconfig)) else: self._pre_block = contents except IOError: print_err('Cannot Open %s' % self.sshconfig ) raise else: f.close() def block_exists(self): return True if self._block else False def get_block (self): return self._block def append_block (self,block): self._block = block try: f = open(self.sshconfig,"w") f.write( "{0}{1}{2}\n{3}".format( self._pre_block, self._block_start_mark, block, self._block_end_mark)) except IOError: print_err('Cannot Open %s' % self.sshconfig ) raise else: f.close def update_block (self, block): self._block = block try: f = open(self.sshconfig,"w") f.write( "{0}{1}{2}\n{3}{4}".format( self._pre_block, self._block_start_mark, block, self._block_end_mark, self._post_block)) except IOError: print_err('Cannot Open {}'.format(self.sshconfig) ) raise else: f.close class ClientProfileConfig: def __init__(self,config_file): self.subscription_id = '' self.client_id = '' self.client_scret = '' self.tenant_id = '' self.__open_read_config(config_file) def __open_read_config(self,config_file): try: cf = open(config_file, 'r') o = json.load(cf) self.subscription_id= str(o['subscription_id']) self.client_id = str(o['client_id']) self.client_scret = str(o['client_scret']) self.tenant_id = str(o['tenant_id']) except IOError: print_err('Cannot Open {}'.format(config_file) ) else: cf.close() def dump(self): print("Azure SSH config client profile dump:\n\tsubscription_id={0}\n" "\tclient_id={1}\n\tclient_scret={2}\n\ttenant_id={3}".format( self.subscription_id, self.client_id, self.client_scret, self.tenant_id)) @staticmethod def generate_template(any_file): try: f = open(any_file,"w") f.write( "{\n" " \"subscription_id\": \"\",\n" " \"client_id\": \"\",\n" " \"client_scret\": \"\",\n" " \"tenant_id\": \"\"\n" "}" ) except IOError: print_err('Cannot Open {}'.format(any_file) ) else: f.close def print_err(s): sys.stderr.write("[ERROR] {}\n".format(s)) def exists_in_dict (key, target_dict): return True if ( (key in target_dict) and target_dict[key] ) else False def get_resorucegroup_from_vmid (vmid): ids = vmid.split('/') # virutal machine id format: # /subscriptions//resourceGroups//providers//virtualMachines/ if len(ids) != 9: pass return ids[4] def get_network_interface_info (network_client, network_interface_id): ni_info = {} ids = network_interface_id.split('/') # network interface id format: # /subscriptions//resourceGroups//providers//networkInterfaces/ if len(ids) != 9: pass ni = network_client.network_interfaces.get( ids[4], ids[8] ) ipconfigs= ni.ip_configurations ipconfig = ipconfigs[0] # privte ip ni_info['private_ip'] = ipconfig.private_ip_address # get public address if not ipconfig.public_ip_address: return ni_info public_address_id = ipconfig.public_ip_address.id # public address id format: # /subscriptions//resourceGroups//providers//publicIPAddresses/ pas= public_address_id.split('/') if len(pas) != 9: return ni_info pia = network_client.public_ip_addresses.get(pas[4], pas[8]) if pia.dns_settings: dns_settings = pia.dns_settings if dns_settings.domain_name_label and dns_settings.fqdn: ni_info['fqdn'] = dns_settings.fqdn if pia.ip_address: ni_info['public_ip'] = pia.ip_address return ni_info def main(): parser = argparse.ArgumentParser(description='This program generates SSH config from Azure ARM VM inventry in subscription') parser.add_argument( '--version', action='version', version=_AZURE_SSH_CONFIG_VERSION) parser.add_argument( '--init', action='store_true', help='Create template client profile at $HOME/.azure/azuresshconfig.json only if there is no existing one') parser.add_argument( '--profile', help='Specify azure client profile file to use ($HOME/.azure/azuresshconfig.json by default)') parser.add_argument( '--output', help='Specify ssh config file path ($HOME/.ssh/config by default). Or specify "stdout" if you want to print its output to STDOUT') parser.add_argument( '--user', help='SSH username to use for all hosts') parser.add_argument( '--identityfile', help='SSH identity file to use for all hosts') parser.add_argument( '--private', action='store_true', help='Use private IP addresses (Public IP is used by default)') parser.add_argument( '--resourcegroups', help='A comma-separated list of resource group to be considered for ssh-config generation (all resource groups by default)') parser.add_argument( '--params', help='Any ssh-config params you want to add with query-string format: key1=value1&key2=value2&...') args = parser.parse_args() if args.init: # check if $HOME/.azure directory exists and create if not exists azure_config_home = '{}/.azure'.format(os.environ['HOME']) if not os.path.exists(azure_config_home): os.makedirs(azure_config_home) # Initialize azure client profile file if not os.path.exists(_DEFAULT_AZURE_SSH_CONFIG_JSON_FILE): ClientProfileConfig.generate_template(_DEFAULT_AZURE_SSH_CONFIG_JSON_FILE) print ("Created template client profile!: {}".format( _DEFAULT_AZURE_SSH_CONFIG_JSON_FILE)) else: print_err("No action has done since client profile file already exist!: {}".format( _DEFAULT_AZURE_SSH_CONFIG_JSON_FILE)) quit() ### Args Validation client_profile_file = args.profile if args.profile else _DEFAULT_AZURE_SSH_CONFIG_JSON_FILE if not os.path.exists(client_profile_file): print_err("Client profile doesn't exist: {0}\n" "For the client profile detail, refer to {1}".format( client_profile_file, _AZURE_SSH_CONFIG_HOME_SITE)) quit() ssh_config_output = args.output if args.output else _DEFAULT_SSH_CONFIG_FILE ssh_default_user = args.user if args.user else '' ssh_default_identityfile = args.identityfile if args.identityfile else '' option_private_ip = args.private filter_resource_groups = [] if args.resourcegroups: lower_rg = args.resourcegroups.lower() rslist= lower_rg.split(',') if len(rslist) > 0: filter_resource_groups = rslist additional_params = [] if args.params: pairlist = args.params.split('&') for s in pairlist: p = s.split('=') if (len(p)==2): additional_params.append(p) ### Load Config cconf = ClientProfileConfig(client_profile_file) ### Get Target virtual machines info List credentials = ServicePrincipalCredentials( cconf.client_id,cconf.client_scret, tenant=cconf.tenant_id) compute_client = ComputeManagementClient( credentials, cconf.subscription_id) network_client = NetworkManagementClient( credentials, cconf.subscription_id) target_vm_list = [] for vm in compute_client.virtual_machines.list_all(): target_vm = {} target_vm['name'] = vm.name vm_rgroup = get_resorucegroup_from_vmid(vm.id) # Filtering by resource group if needed if len(filter_resource_groups) > 0: r = vm_rgroup.lower() if not (r in filter_resource_groups): continue # skip network_interfaces = vm.network_profile.network_interfaces for ni in network_interfaces: ni_info = get_network_interface_info(network_client, ni.id) if option_private_ip: if exists_in_dict('private_ip',ni_info): target_vm['access_ip'] = ni_info['private_ip'] else: if exists_in_dict('public_ip',ni_info): target_vm['access_ip'] = ni_info['public_ip'] if exists_in_dict('access_ip',target_vm): break # Add only vm that has access_ip if exists_in_dict('access_ip',target_vm): target_vm_list.append(target_vm) ### Generate and append config block to ssh-config file scblock = SSHConfigBlock() for v in target_vm_list: params = {} if ssh_default_user: params['User'] = ssh_default_user if ssh_default_identityfile: params['IdentityFile'] = ssh_default_identityfile if len(additional_params) > 0: for pset in additional_params: params[pset[0]] = pset[1] scblock.add_entry(v['name'], v['access_ip'],params) ssh_config_block = scblock.to_string() if ssh_config_output.lower() == 'stdout': print("{}".format(ssh_config_block)) else: ssh_config = SSHConfig(ssh_config_output) if ssh_config.block_exists(): ssh_config.update_block(ssh_config_block) else: ssh_config.append_block(ssh_config_block) print("Done! Updated: {}".format(ssh_config.sshconfig)) if __name__ == "__main__": main() # # vim:ts=4 et # PKXJ%''.azuresshconfig-0.3.0.dist-info/DESCRIPTION.rstAzure SSH Config (azuresshconfig) ================================= Generate SSH config file from Azure ARM VM inventry in subscription Introduction ------------ azuresshconfig is a simple script that collects Azure ARM Virtual Machine(VM) inventry in subscription and generate a SSH config entries to be appended to $HOME/.ssh/config (the file is newly created if no exist). This is like an Azure version of `ec2ssh `__ or `aws-ssh-config `__ that strongly inspired this initiative. This would be very helpful when you manage lots of VMs that have dynamic IP assignment settings and need frequent VM up-and-down operations for them which causes the change of IPs assigned to VMs. In such a case, azuresshconfig will definitly make your SSH life easy. Installation ------------ :: pip install azuresshconfig Configuration ------------- Generate client profile template file by executing the following command. :: azuresshconfig --init Configure the client profile file, in which you add your service principal account info to access your resources in Azure via Azure APIs. :: vi $HOME/.azure/azuresshconfig.json { "subscription_id": "", "client_id": "", "client_scret": "", "tenant_id": "" } For those who don't know how to create service principal, there is a great instruction: `Use Azure CLI to create a service principal to access resources `__. If you have Azure CLI 2.0 command installed on your evironment, you can create your service principal and configure its access to your azure resources with a single command 'az ad sp create-for-rbac'. Suppose your app id uri is 'http://unofficialism.info' and role you want to give for the app is 'Reader', you can create your service principal like this: :: az ad sp create-for-rbac -n "http://unofficialism.info" --role reader You will get an output like this, and with them you can fill out the client profile file: :: { "appId": "c36x4b4f-bef6-422e-bd3b-65057e7ab065", # -> client_id in client profile file "displayName": "azure-cli-2017-03-30-05-16-59", "name": "http://unofficialism.info", "password": "32126d32-7453-4053-3353-c420d4ffef2e", # -> client_scret in client profile file "tenant": "72f988bf-86f1-41af-91cb-2d7cd011db47" # -> tenant_id in client profile file } For the detail of service principal role, please refer to `Built-in roles for Azure Role-Based Access Control `__. Usage ----- Assuming all required packages are installed and rightly configured, you're ready to run azuresshconfig :: azuresshconfig --help usage: azuresshconfig.py [-h] [--version] [--init] [--profile PROFILE] [--output OUTPUT] [--user USER] [--identityfile IDENTITYFILE] [--private] [--resourcegroups RESOURCEGROUPS] [--params PARAMS] This program generates SSH config from Azure ARM VM inventry in subscription optional arguments: -h, --help show this help message and exit --version show program's version number and exit --init Create template client profile at $HOME/.azure/azuresshconfig.json only if there is no existing one --profile PROFILE Specify azure client profile file to use ($HOME/.azure/azuresshconfig.json by default) --output OUTPUT Specify ssh config file path ($HOME/.ssh/config by default). Or specify "stdout" if you want to print its output to STDOUT --user USER SSH username to use for all hosts --identityfile IDENTITYFILE SSH identity file to use for all hosts --private Use private IP addresses (Public IP is used by default) --resourcegroups RESOURCEGROUPS A comma-separated list of resource group to be considered for ssh-config generation (all resource groups by default) --params PARAMS Any ssh-config params you want to add with query- string format: key1=value1&key2=value2&... 1. Running with no optional args ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig Each host entry in output ssh-config file is simple like this: :: cat ~/.ssh/config ### AZURE-SSH-CONFIG BEGIN ### Host myvm1 HostName 40.74.124.30 Host myvm2 HostName 40.74.116.134 .... ### AZURE-SSH-CONFIG END ### 2. Running with user, output, and identity file options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig --user yoichika --output /mypath/config --identityfile ~/.ssh/id_rsa User and identify file are added to each host entry in output ssh-config file: :: cat /mypath/config ### AZURE-SSH-CONFIG BEGIN ### Host myvm1 HostName 40.74.124.30 IdentityFile ~/.ssh/id_rsa User yoichika Host myvm2 HostName 40.74.116.134 IdentityFile ~/.ssh/id_rsa User yoichika .... ### AZURE-SSH-CONFIG END ### 3. Running with user, identity file, and resource group options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig --user yoichika \ --identityfile ~/.ssh/id_rsa \ --resourcegroups mygroup1,mygroup2 Only host entry that belong to specified resource group are added in ssh-config 4. Running with user, identity file, and additional ssh-config params ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig.py --user yoichika \ --identityfile ~/.ssh/id_rsa \ --params "Port=2222&Protocol=2&UserKnownHostsFile=~/.ssh/known_hosts&ForwardAgent=yes" Additional ssh-config params specified by --params are added to an output ssh-config file like this: :: cat ~/.ssh/config ### AZURE-SSH-CONFIG BEGIN ### Host myvm1 HostName 40.74.124.30 IdentityFile ~/.ssh/id_rsa User yoichika Port 2222 Protocol 2 UserKnownHostsFile ~/.ssh/known_hosts ForwardAgent yes Host myvm2 HostName 40.74.116.134 IdentityFile /home/yoichika/.ssh/id_rsa User yoichika Port 2222 Protocol 2 UserKnownHostsFile ~/.ssh/known_hosts ForwardAgent yes .... ### AZURE-SSH-CONFIG END ### Docker (Dockerfile) ------------------- Now docker image for azuresshconfig is available (yoichikawasaki/azuresshconfig). The image is based on Alpine Linux image, and contains Python2.7, pip, azuresshconfig Python packages and its prerequisite libraries. Download size of this image is only 155 MB :: $ docker images azuresshconfig REPOSITORY TAG IMAGE ID CREATED SIZE azuresshconfig latest 7488bef4343f 7 minutes ago 155 MB Usage Example ~~~~~~~~~~~~~ .. code:: bash $ docker run -v $HOME:/root --rm -it yoichikawasaki/azuresshconfig \ --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config or you can build from Dockerfile and run your local images like this: .. code:: bash $ docker build -t azuresshconfig . $ docker run -v $HOME:/root --rm -it azuresshconfig \ --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config Shell Completion ---------------- Bash ~~~~ Bash completion will work by loading bash/\ `azuresshconfig\_completion.bash `__. In order to load azuresshconfig\_completion.bash, you can do like this :: # copy this under either of following directories cp azuresshconfig_completion.bash (/etc/bash_completion.d | /usr/local/etc/bash_completion.d | ~/bash_completion.d) # or append 'source /path/to/azuresshconfig_completion.bash' to .bashrc like this echo 'source /path/to/azuresshconfig_completion.bash' >> .bashrc Once azuresshconfig\_completion.bash is loaded, Bash completion will work this: :: $ azuresshconfig -[tab] -h --identityfile --params --profile --user --help --init --private --resourcegroups $ azuresshconfig --i[tab] --identityfile --init $ azuresshconfig --p[tab] --params --private --profile $ azuresshconfig --user [tab] $ azuresshconfig --user $ azuresshconfig --user --identityfile [tab] $ azuresshconfig --user --identityfile Todo ---- - Support zsh Completion (Hopefully support it soon) Issues ------ - `Kown Issues and resolutions `__ - `Current Issues, bugs, and requests `__ Change log ---------- - `Changelog `__ Links ----- - https://pypi.python.org/pypi/azuresshconfig/ - http://unofficialism.info/posts/azuresshconfig/ Contributing ------------ Bug reports and pull requests are welcome on GitHub at https://github.com/yokawasa/azure-ssh-config. Copyright --------- .. raw:: html .. raw:: html :: .. raw:: html .. raw:: html :: .. raw:: html .. raw:: html
CopyrightCopyright (c) 2016- Yoichi Kawasaki
LicenseMIT
PKXJ9"66/azuresshconfig-0.3.0.dist-info/entry_points.txt[console_scripts] azuresshconfig=azuresshconfig:main PKXJd),..,azuresshconfig-0.3.0.dist-info/metadata.json{"classifiers": ["Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Utilities"], "extensions": {"python.commands": {"wrap_console": {"azuresshconfig": "azuresshconfig:main"}}, "python.details": {"contacts": [{"email": "yoichi.kawasaki@outlook.com", "name": "Yoichi Kawasaki", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/yokawasa/azure-ssh-config"}}, "python.exports": {"console_scripts": {"azuresshconfig": "azuresshconfig:main"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["azure", "ssh", "config"], "license": "MIT", "metadata_version": "2.0", "name": "azuresshconfig", "platform": "any", "run_requires": [{"requires": ["argparse", "azure-mgmt-compute (>=0.30.0rc6)", "azure-mgmt-network (>=0.30.0rc6)", "azure-mgmt-resource (>=0.30.0rc6)", "msrestazure", "simplejson"]}], "summary": "Generate SSH config file from Azure ARM VM inventry in subscription", "version": "0.3.0"}PKXJ {,azuresshconfig-0.3.0.dist-info/top_level.txtazuresshconfig PKXJndnn$azuresshconfig-0.3.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PKXJ_R]T++'azuresshconfig-0.3.0.dist-info/METADATAMetadata-Version: 2.0 Name: azuresshconfig Version: 0.3.0 Summary: Generate SSH config file from Azure ARM VM inventry in subscription Home-page: https://github.com/yokawasa/azure-ssh-config Author: Yoichi Kawasaki Author-email: yoichi.kawasaki@outlook.com License: MIT Keywords: azure ssh config Platform: any Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Utilities Requires-Dist: argparse Requires-Dist: azure-mgmt-compute (>=0.30.0rc6) Requires-Dist: azure-mgmt-network (>=0.30.0rc6) Requires-Dist: azure-mgmt-resource (>=0.30.0rc6) Requires-Dist: msrestazure Requires-Dist: simplejson Azure SSH Config (azuresshconfig) ================================= Generate SSH config file from Azure ARM VM inventry in subscription Introduction ------------ azuresshconfig is a simple script that collects Azure ARM Virtual Machine(VM) inventry in subscription and generate a SSH config entries to be appended to $HOME/.ssh/config (the file is newly created if no exist). This is like an Azure version of `ec2ssh `__ or `aws-ssh-config `__ that strongly inspired this initiative. This would be very helpful when you manage lots of VMs that have dynamic IP assignment settings and need frequent VM up-and-down operations for them which causes the change of IPs assigned to VMs. In such a case, azuresshconfig will definitly make your SSH life easy. Installation ------------ :: pip install azuresshconfig Configuration ------------- Generate client profile template file by executing the following command. :: azuresshconfig --init Configure the client profile file, in which you add your service principal account info to access your resources in Azure via Azure APIs. :: vi $HOME/.azure/azuresshconfig.json { "subscription_id": "", "client_id": "", "client_scret": "", "tenant_id": "" } For those who don't know how to create service principal, there is a great instruction: `Use Azure CLI to create a service principal to access resources `__. If you have Azure CLI 2.0 command installed on your evironment, you can create your service principal and configure its access to your azure resources with a single command 'az ad sp create-for-rbac'. Suppose your app id uri is 'http://unofficialism.info' and role you want to give for the app is 'Reader', you can create your service principal like this: :: az ad sp create-for-rbac -n "http://unofficialism.info" --role reader You will get an output like this, and with them you can fill out the client profile file: :: { "appId": "c36x4b4f-bef6-422e-bd3b-65057e7ab065", # -> client_id in client profile file "displayName": "azure-cli-2017-03-30-05-16-59", "name": "http://unofficialism.info", "password": "32126d32-7453-4053-3353-c420d4ffef2e", # -> client_scret in client profile file "tenant": "72f988bf-86f1-41af-91cb-2d7cd011db47" # -> tenant_id in client profile file } For the detail of service principal role, please refer to `Built-in roles for Azure Role-Based Access Control `__. Usage ----- Assuming all required packages are installed and rightly configured, you're ready to run azuresshconfig :: azuresshconfig --help usage: azuresshconfig.py [-h] [--version] [--init] [--profile PROFILE] [--output OUTPUT] [--user USER] [--identityfile IDENTITYFILE] [--private] [--resourcegroups RESOURCEGROUPS] [--params PARAMS] This program generates SSH config from Azure ARM VM inventry in subscription optional arguments: -h, --help show this help message and exit --version show program's version number and exit --init Create template client profile at $HOME/.azure/azuresshconfig.json only if there is no existing one --profile PROFILE Specify azure client profile file to use ($HOME/.azure/azuresshconfig.json by default) --output OUTPUT Specify ssh config file path ($HOME/.ssh/config by default). Or specify "stdout" if you want to print its output to STDOUT --user USER SSH username to use for all hosts --identityfile IDENTITYFILE SSH identity file to use for all hosts --private Use private IP addresses (Public IP is used by default) --resourcegroups RESOURCEGROUPS A comma-separated list of resource group to be considered for ssh-config generation (all resource groups by default) --params PARAMS Any ssh-config params you want to add with query- string format: key1=value1&key2=value2&... 1. Running with no optional args ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig Each host entry in output ssh-config file is simple like this: :: cat ~/.ssh/config ### AZURE-SSH-CONFIG BEGIN ### Host myvm1 HostName 40.74.124.30 Host myvm2 HostName 40.74.116.134 .... ### AZURE-SSH-CONFIG END ### 2. Running with user, output, and identity file options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig --user yoichika --output /mypath/config --identityfile ~/.ssh/id_rsa User and identify file are added to each host entry in output ssh-config file: :: cat /mypath/config ### AZURE-SSH-CONFIG BEGIN ### Host myvm1 HostName 40.74.124.30 IdentityFile ~/.ssh/id_rsa User yoichika Host myvm2 HostName 40.74.116.134 IdentityFile ~/.ssh/id_rsa User yoichika .... ### AZURE-SSH-CONFIG END ### 3. Running with user, identity file, and resource group options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig --user yoichika \ --identityfile ~/.ssh/id_rsa \ --resourcegroups mygroup1,mygroup2 Only host entry that belong to specified resource group are added in ssh-config 4. Running with user, identity file, and additional ssh-config params ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: azuresshconfig.py --user yoichika \ --identityfile ~/.ssh/id_rsa \ --params "Port=2222&Protocol=2&UserKnownHostsFile=~/.ssh/known_hosts&ForwardAgent=yes" Additional ssh-config params specified by --params are added to an output ssh-config file like this: :: cat ~/.ssh/config ### AZURE-SSH-CONFIG BEGIN ### Host myvm1 HostName 40.74.124.30 IdentityFile ~/.ssh/id_rsa User yoichika Port 2222 Protocol 2 UserKnownHostsFile ~/.ssh/known_hosts ForwardAgent yes Host myvm2 HostName 40.74.116.134 IdentityFile /home/yoichika/.ssh/id_rsa User yoichika Port 2222 Protocol 2 UserKnownHostsFile ~/.ssh/known_hosts ForwardAgent yes .... ### AZURE-SSH-CONFIG END ### Docker (Dockerfile) ------------------- Now docker image for azuresshconfig is available (yoichikawasaki/azuresshconfig). The image is based on Alpine Linux image, and contains Python2.7, pip, azuresshconfig Python packages and its prerequisite libraries. Download size of this image is only 155 MB :: $ docker images azuresshconfig REPOSITORY TAG IMAGE ID CREATED SIZE azuresshconfig latest 7488bef4343f 7 minutes ago 155 MB Usage Example ~~~~~~~~~~~~~ .. code:: bash $ docker run -v $HOME:/root --rm -it yoichikawasaki/azuresshconfig \ --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config or you can build from Dockerfile and run your local images like this: .. code:: bash $ docker build -t azuresshconfig . $ docker run -v $HOME:/root --rm -it azuresshconfig \ --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config Shell Completion ---------------- Bash ~~~~ Bash completion will work by loading bash/\ `azuresshconfig\_completion.bash `__. In order to load azuresshconfig\_completion.bash, you can do like this :: # copy this under either of following directories cp azuresshconfig_completion.bash (/etc/bash_completion.d | /usr/local/etc/bash_completion.d | ~/bash_completion.d) # or append 'source /path/to/azuresshconfig_completion.bash' to .bashrc like this echo 'source /path/to/azuresshconfig_completion.bash' >> .bashrc Once azuresshconfig\_completion.bash is loaded, Bash completion will work this: :: $ azuresshconfig -[tab] -h --identityfile --params --profile --user --help --init --private --resourcegroups $ azuresshconfig --i[tab] --identityfile --init $ azuresshconfig --p[tab] --params --private --profile $ azuresshconfig --user [tab] $ azuresshconfig --user $ azuresshconfig --user --identityfile [tab] $ azuresshconfig --user --identityfile Todo ---- - Support zsh Completion (Hopefully support it soon) Issues ------ - `Kown Issues and resolutions `__ - `Current Issues, bugs, and requests `__ Change log ---------- - `Changelog `__ Links ----- - https://pypi.python.org/pypi/azuresshconfig/ - http://unofficialism.info/posts/azuresshconfig/ Contributing ------------ Bug reports and pull requests are welcome on GitHub at https://github.com/yokawasa/azure-ssh-config. Copyright --------- .. raw:: html .. raw:: html :: .. raw:: html .. raw:: html :: .. raw:: html .. raw:: html
CopyrightCopyright (c) 2016- Yoichi Kawasaki
LicenseMIT
PKXJjw6"%azuresshconfig-0.3.0.dist-info/RECORDazuresshconfig.py,sha256=n1mTY3-Z88a5lmvDAV-iPf4bgEcRIlM7fy0npuVuqnM,15140 azuresshconfig-0.3.0.dist-info/DESCRIPTION.rst,sha256=sCC6CSrIsiZFG307UFTtNEDwwjERo1c9SAbwm84LHOk,10119 azuresshconfig-0.3.0.dist-info/METADATA,sha256=E_V6r6BHUVtq4KhYMjP79mkKC5NkfUk3L1i55Wb2ttQ,11167 azuresshconfig-0.3.0.dist-info/RECORD,, azuresshconfig-0.3.0.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 azuresshconfig-0.3.0.dist-info/entry_points.txt,sha256=2M5L1ChqAnb4HZJFv_sMm58Cm4xWMpCQmbgV2qdwCYQ,54 azuresshconfig-0.3.0.dist-info/metadata.json,sha256=cwncjI88waEfvl0NPtC82BD6CGJ0fYKxomlZkVOBoNk,1326 azuresshconfig-0.3.0.dist-info/top_level.txt,sha256=WfFQDDsyuV3L6OF7wml2ggkuA6lYHJILyGRsqcqoeUw,15 PKJzL$;$;azuresshconfig.pyPKXJ%''.S;azuresshconfig-0.3.0.dist-info/DESCRIPTION.rstPKXJ9"66/&cazuresshconfig-0.3.0.dist-info/entry_points.txtPKXJd),..,cazuresshconfig-0.3.0.dist-info/metadata.jsonPKXJ {,!iazuresshconfig-0.3.0.dist-info/top_level.txtPKXJndnn$ziazuresshconfig-0.3.0.dist-info/WHEELPKXJ_R]T++'*jazuresshconfig-0.3.0.dist-info/METADATAPKXJjw6"%azuresshconfig-0.3.0.dist-info/RECORDPK