Azure Compute Deprecated

Deprecated. Create and Manage Azure Virtual Machines

IT Services · Deprecated Content (Deprecated)

Details

IDAzure Compute
ProviderMicrosoft
CategoryIT Services
From Version5.0.0
Docker Imagedemisto/python:2.7.18.20958

Configuration parameters

  • host — Host URL (e.g. https://management.azure.com) (required)
  • tenant_id — Tenant ID (received from the admin consent - see Detailed Instructions (?) section) (required)
  • token — Token (received from the admin consent - see detailed instructions (?)) (required)
  • proxy — Use system proxy settings
  • unsecure — Trust any certificate (not secure)

Commands (7)

  • azure-list-resource-groups Deprecated

    Lists all resource groups that belong to your Azure subscription.

  • azure-vm-create-instance Deprecated

    Creates a virtual machine instance with the specified OS image.

  • azure-vm-delete-instance Deprecated

    Deletes a specified virtual machine.

  • azure-vm-get-instance-details Deprecated

    Gets the properties of a given virtual machine.

  • azure-vm-list-instances Deprecated

    Lists the virtual machine instances in the given resource group.

  • azure-vm-poweroff-instance Deprecated

    Powers off a given virtual machine.

  • azure-vm-start-instance Deprecated

    Powers on a given virtual machine.

import demistomock as demisto
from CommonServerPython import *
from CommonServerUserPython import *
'''IMPORTS'''

import requests
from datetime import datetime

'''GLOBAL VARS'''

PARAMS = demisto.params()
USE_SSL = not demisto.params().get('unsecure')
TENANT_ID = PARAMS.get('tenant_id')
TOKEN = PARAMS.get('token')
HOST = PARAMS.get('host', 'https://management.azure.com')
SERVER = HOST[:-1] if HOST.endswith('/') else HOST
API_VERSION = '2018-06-01'
HEADERS = {}
SUBSCRIPTION_ID = None
BASE_URL = None

# Image options to be used in the create_vm_command
IMAGES = {
    'ubuntu server 14.04 lts': {
        'publisher': 'Canonical',
        'offer': 'UbuntuServer',
        'sku': '14.04-LTS',
        'version': 'latest'
    },
    'ubuntu server 16.04 lts': {
        'publisher': 'Canonical',
        'offer': 'UbuntuServer',
        'sku': '16.04-LTS',
        'version': 'latest'
    },
    'ubuntu server 18.04 lts': {
        'publisher': 'Canonical',
        'offer': 'UbuntuServer',
        'sku': '18.04-LTS',
        'version': 'latest'
    },
    'red hat enterprise linux 7.6': {
        'publisher': 'RedHat',
        'offer': 'RHEL',
        'sku': '7-RAW',
        'version': 'latest'
    },
    'centos-based 7.5': {
        'publisher': 'OpenLogic',
        'offer': 'CentOS',
        'sku': '7.5',
        'version': 'latest'
    },
    'windows server 2012 r2 datacenter': {
        'publisher': 'MicrosoftWindowsServer',
        'offer': 'WindowsServer',
        'sku': '2012-R2-Datacenter',
        'version': 'latest'
    },
    'windows server 2016 datacenter': {
        'publisher': 'MicrosoftWindowsServer',
        'offer': 'WindowsServer',
        'sku': '2016-Datacenter',
        'version': 'latest'
    },
    'windows 10 pro version 1803': {
        'publisher': 'MicrosoftWindowsDesktop',
        'offer': 'Windows-10',
        'sku': 'rs4-pro',
        'version': 'latest'
    },
    'windows 10 pro version 1809': {
        'publisher': 'MicrosoftWindowsDesktop',
        'offer': 'Windows-10',
        'sku': 'rs5-pro',
        'version': 'latest'
    }
}

# Error messages for different provisioning states
CREATING_OR_UPDATING_ERR = 'Please wait for the VM to finish being' \
                           ' {} before executing this command. To retrieve the ' \
                           'last known state of the VM, execute the ' \
                           '`azure-vm-get-instance-details` command. '
DELETING_ERR = 'You cannot execute this command because the VM is being deleted.'
FAILED_ERR = 'Unable to power-off or power-on \'{}\' virtual machine ' \
             'because the following provisioning failure occurred during ' \
             'the vm\'s creation.\ncode: "{}"\nmessage: "{}"\nVisit the ' \
             'Azure Web Portal to take care of this issue.'

# Error messages determined by the provisioning state of the VM
PROVISIONING_STATE_TO_ERRORS = {
    'creating': CREATING_OR_UPDATING_ERR.format('created'),
    'updating': CREATING_OR_UPDATING_ERR.format('updated'),
    'deleting': DELETING_ERR,
    'failed': FAILED_ERR
}

'''SETUP'''

# Disable insecure warnings
requests.packages.urllib3.disable_warnings()
# Remove proxy if not set to true in params
if not PARAMS.get('proxy'):
    del os.environ['HTTP_PROXY']
    del os.environ['HTTPS_PROXY']
    del os.environ['http_proxy']
    del os.environ['https_proxy']


'''HELPER FUNCTIONS'''


def screen_errors(error_message, *args, **kwargs):
    """
    Make sure that the values passed as args and the keys in kwargs do not appear in error messages

    parameter: (string) error_message
        The error message that needs to be screened for the values in args and the keys
        in kwargs

    parameter: (list) *args
        Arguments that need to be screened from error outputs and that will be replaced
        by x's enclosed by a '<' symbol on the left, and a '>' symbol on the right

    parameter: (dict) **kwargs
        Key-value pairs for each of which the user wishes to screen the key identifier string
        from the error_message and replace it with its assigned value string. Useful for
        when the user wishes to replace sensitive data with a value of their choosing
        instead of the default x's enclosed by '<', and '>' symbols on the left and right respectively

    returns:
        The error message free of sensitive information as determined by the values of
        args and the keys of kwargs
    """
    if isinstance(error_message, Exception):
        # Format Exception object as String
        error_as_dict = vars(error_message)
        updated_error_message = ''
        for key, val in error_as_dict.iteritems():
            if updated_error_message != '':
                updated_error_message += '\n' + str(key) + ': ' + str(val)
            else:
                updated_error_message += str(key) + ': ' + str(val)
    elif not isinstance(error_message, str):
        # If not an Exception or a String, try to cast to a string
        updated_error_message = str(error_message)
    else:
        updated_error_message = error_message

    for argument in args:
        if argument != '' and argument in updated_error_message:
            length = len(argument)
            placeholder = '<' + 'x' * length + '>'
            updated_error_message = updated_error_message.replace(argument, placeholder)

    for key, value in kwargs.items():
        if key != '' and key in updated_error_message:
            updated_error_message = updated_error_message.replace(key, value)

    return updated_error_message


def validate_provisioning_state(args):
    """
    Ensure that the provisioning state of a VM is 'Succeeded'

    For all provisioning states other than 'Succeeded', this method will raise an
    exception with an informative error message.

    parameter: (dict) args
        The command arguments passed to either the `azure-vm-start-instance` or
        `azure-vm-poweroff-instance` commands

    returns:
        None
    """
    response = get_vm(args)
    # Retrieve relevant properties for checking provisioning state and returning
    # informative error messages if necessary
    vm_name = response.get('name')
    properties = response.get('properties')
    provisioning_state = properties.get('provisioningState')
    statuses = properties.get('instanceView', {}).get('statuses')

    # Check if the current ProvisioningState of the VM allows for executing this command
    if provisioning_state.lower() == 'failed':
        for status in statuses:
            status_code = status.get('code')
            if 'provisioningstate/failed' in status_code.lower():
                message = status.get('message')
                err_msg = PROVISIONING_STATE_TO_ERRORS.get('failed')
                raise Exception(err_msg.format(vm_name, status_code, message))  # type: ignore
        # In the case that the microsoft API changes and the status code is no longer
        # relevant, preventing the above exception with its detailed error message from
        # being raised, then raise the below exception with a more general error message
        err_msg = 'Cannot execute this command because the ProvisioningState of the VM is \'Failed\'.'
        raise Exception(err_msg)
    elif provisioning_state.lower() in PROVISIONING_STATE_TO_ERRORS.keys():
        err_msg = PROVISIONING_STATE_TO_ERRORS.get(provisioning_state.lower())
        raise Exception(err_msg)


def epoch_seconds(d=None):
    """
    Return the number of seconds for given date. If no date, return current.

    parameter: (date) d
        The date to convert to seconds

    returns:
        The date in seconds
    """
    if not d:
        d = datetime.utcnow()
    return int((d - datetime.utcfromtimestamp(0)).total_seconds())


def set_subscription_id():
    """
    Setting subscription ID to the context and returning it
    """
    headers = {
        'Authorization': TOKEN,
        'Accept': 'application/json'
    }
    token_retrieval_url = 'https://demistobot.demisto.com/azurecompute-token'  # disable-secrets-detection
    parameters = {'tenant': TENANT_ID, 'product': 'AzureCompute'}
    r = requests.get(token_retrieval_url, headers=headers, params=parameters, verify=USE_SSL)
    try:
        response = r.json()
        if r.status_code != requests.codes.ok:
            return_error('Error: {}\nDescription:{}'.format(response.get('title'), response.get('detail')))
        sub_id = response.get('subscription_id')
        demisto.setIntegrationContext({
            'token': response.get('token'),
            'stored': epoch_seconds(),
            'subscription_id': sub_id
        })
        return sub_id
    except ValueError as e:
        if e.message == 'No JSON object could be decoded':
            return_error(response.content)
        else:
            raise e


def update_access_token():
    """
    Check if we have a valid token and if not get one and update global HEADERS
    """
    ctx = demisto.getIntegrationContext()
    if ctx.get('token') and ctx.get('stored'):
        if epoch_seconds() - ctx.get('stored') < 60 * 60 - 30:
            HEADERS['Authorization'] = 'Bearer ' + ctx.get('token')
            return
    headers = {
        'Authorization': TOKEN,
        'Accept': 'application/json'
    }
    token_retrieval_url = 'https://demistobot.demisto.com/azurecompute-token'  # disable-secrets-detection
    parameters = {'tenant': TENANT_ID, 'product': 'AzureCompute'}
    r = requests.get(token_retrieval_url, headers=headers, params=parameters, verify=USE_SSL)
    if r.status_code not in {200, 201}:
        return_error('Error in authentication with the application. Try checking the credentials you entered.')
    try:
        response = r.json()
    except ValueError:
        err_msg = 'There was a problem in retrieving an updated access token.'
        err_msg += ' The response from the Demistobot server did not contain the expected content.'
        return_error(err_msg)
    demisto.setIntegrationContext({
        'token': response.get('token'),
        'stored': epoch_seconds(),
        'subscription_id': response.get('subscription_id')
    })
    HEADERS['Authorization'] = 'Bearer ' + response.get('token')


def assign_image_attributes(image):
    """
    Retrieve image properties determined by the chosen image

    returns:
        Image Properties Tuple (sku, publisher, offer, version)
    """
    image = image.lower()
    image_properties = IMAGES.get(image)
    if not image_properties:
        err_msg = 'Invalid value entered for the \'os_image\' argument. '
        err_msg += 'Only values from the provided options are accepted.'
        raise Exception(err_msg)
    sku = image_properties.get('sku')
    publisher = image_properties.get('publisher')
    offer = image_properties.get('offer')
    version = image_properties.get('version')
    return sku, publisher, offer, version


def create_vm_parameters(args):
    """
    Construct the VM object

    Use the actual parameters passed to the 'azure-vm-create-instance' command
    to build a vm object that will be sent in the body of the command's associated
    API call.

    parameter: (dict) args
        Dictionary that contains the actual parameters that were passed to the
        'azure-vm-create-instance' command

    returns:
        Virtual Machine Object
    """
    # Retrieve relevant command arguments
    location = args.get('virtual_machine_location')
    vm_size = args.get('vm_size')
    image = args.get('os_image')
    sku = args.get('sku')
    publisher = args.get('publisher')
    version = args.get('version')
    offer = args.get('offer')
    vm_name = args.get('virtual_machine_name')
    resource_group = args.get('resource_group')
    admin_username = args.get('admin_username')
    admin_password = args.get('admin_password')
    nic_name = args.get('nic_name')
    full_nic_id = '/subscriptions/' + SUBSCRIPTION_ID + '/resourceGroups/'  # type: ignore
    full_nic_id += resource_group + '/providers/Microsoft.Network/networkInterfaces/' + nic_name

    if not image and not (sku and publisher and version and offer):
        err_msg = 'You must enter a value for the \'os_image\' argument '
        err_msg += 'or the group of arguments, \'sku\', \'publisher\', \'version\', and \'offer\'.'
        raise Exception(err_msg)

    if image:
        sku, publisher, offer, version = assign_image_attributes(image)

    # Construct VM object
    vm = {
        'location': location,
        'properties': {
            'hardwareProfile': {
                'vmSize': vm_size
            },
            'storageProfile': {
                'imageReference': {
                    'sku': sku,
                    'publisher': publisher,
                    'version': version,
                    'offer': offer
                },
                'osDisk': {
                    'caching': 'ReadWrite',
                    'managedDisk': {
                        'storageAccountType': 'Standard_LRS'
                    },
                    'name': vm_name,
                    'createOption': 'FromImage'
                }
            },
            'osProfile': {
                'adminUsername': admin_username,
                'computerName': vm_name,
                'adminPassword': admin_password
            },
            'networkProfile': {
                'networkInterfaces': [
                    {
                        'id': full_nic_id,
                        'properties': {
                            'primary': 'true'
                        }
                    }
                ]
            }
        },
        'name': vm_name
    }

    return vm


def http_request(method, url_suffix=None, data=None, headers=HEADERS,
                 params=None, codes=None, full_url=None, j_son=None):
    """
    A wrapper for requests lib to send our requests and handle requests and responses better

    parameter: (string) method
        A string denoting the http request method to use.
        Can be 'GET', 'POST, 'PUT', 'DELETE', etc.

    parameter: (string) url_suffix
        The API endpoint that determines which data we are trying to access/create/update
        in our call to the API

    parameter: (dict) data
        The key/value pairs to be form-encoded

    parameter: (dict) headers
        The headers to use with the request

    parameter: (dict) params
        The parameters to use with this request

    parameter: (set) codes
        The set of status codes against which the status code of the response should be checked

    parameter: (string) full_url
        The full url to make a request to. Only necessary in the case that you need to make
        an API request to an endpoint which differs in its base url from the majority of
        the API calls in the integration

    parameter: (dict) j_son
        A JSON serializable Python object to send in the body of the request

    returns:
        JSON Response Object
    """
    update_access_token()
    try:
        url = full_url if full_url else None
        if not url:
            url = BASE_URL + url_suffix if url_suffix else BASE_URL
        r = requests.request(
            method,
            url,
            headers=headers,
            data=data,
            params=params,
            verify=USE_SSL,
            json=j_son
        )
        green_codes = codes if codes else {200, 201, 202, 204}
        if r.status_code not in green_codes:
            err_msg = 'Error in API call to Azure Compute Integration [{}] - {}'.format(r.status_code, r.reason)
            err = r.json().get('error')
            if err:
                err_msg1 = '\nError code: {}\nError message: {}'.format(err.get('code'), err.get('message'))
                err_msg += err_msg1
            raise Exception(err_msg)
        response = json.loads(r.content)
    except ValueError:
        response = r.content

    return response


'''MAIN FUNCTIONS / API CALLS'''

# <---------- Test Module ----------> #


def test_module():
    # Implicitly will test TENANT_ID, TOKEN and SUBSCRIPTION_ID
    list_resource_groups()
    demisto.results('ok')

# <-------- Resource Groups --------> #


def list_resource_groups():
    parameters = {'api-version': '2018-05-01'}
    response = http_request('GET', params=parameters, codes={200})
    return response


def list_resource_groups_command():
    """
    List all Resource Groups belonging to your Azure subscription

    returns:
        Resource-Group Objects
    """
    response = list_resource_groups()
    # Retrieve relevant properties to return to context
    value = response.get('value')
    resource_groups = []
    for resource_group in value:
        resource_group_context = {
            'Name': resource_group.get('name'),
            'ID': resource_group.get('id'),
            'Location': resource_group.get('location'),
            'ProvisioningState': resource_group.get('properties', {}).get('provisioningState')
        }
        resource_groups.append(resource_group_context)

    title = 'List of Resource Groups'
    human_readable = tableToMarkdown(title, resource_groups, removeNull=True)
    entry_context = {'Azure.ResourceGroup(val.Name && val.Name === obj.Name)': resource_groups}
    demisto.results({
        'Type': entryTypes['note'],
        'Contents': response,
        'ContentsFormat': formats['text'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': human_readable,
        'EntryContext': entry_context
    })


# <-------- Virtual Machines --------> #

def list_vms(resource_group):
    # Construct endpoint URI suffix
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines'
    parameters = {'api-version': API_VERSION}
    # Call API
    response = http_request('GET', url_endpoint, params=parameters, codes={200})
    return response


def list_vms_command():
    """
    List the VM instances in the specified Resource Group

    demisto parameter: (string) resource_group
        Resource Group of the VMs

    returns:
        Virtual Machine Objects
    """
    resource_group = demisto.args().get('resource_group')
    response = list_vms(resource_group)

    vm_objects_list = response.get('value')

    vms = []
    for vm_object in vm_objects_list:
        vm_name = vm_object.get('name').lower()
        location = vm_object.get('location')
        properties = vm_object.get('properties')
        provisioning_state = properties.get('provisioningState')
        os_disk = properties.get('storageProfile', {}).get('osDisk')
        datadisk = os_disk.get('diskSizeGB', 'NA')
        vm_id = properties.get('vmId')
        os_type = os_disk.get('osType')
        vm = {
            'Name': vm_name,
            'ID': vm_id,
            'Size': datadisk,
            'OS': os_type,
            'Location': location,
            'ProvisioningState': provisioning_state,
            'ResourceGroup': resource_group
        }
        vms.append(vm)

    title = 'Microsoft Azure - List of Virtual Machines in Resource Group "{}"'.format(resource_group)
    table_headers = ['Name', 'ID', 'Size', 'OS', 'Location', 'ProvisioningState', 'ResourceGroup']
    human_readable = tableToMarkdown(title, vms, headers=table_headers, removeNull=True)
    entry_context = {'Azure.Compute(val.Name && val.Name === obj.Name)': vms}
    demisto.results({
        'Type': entryTypes['note'],
        'Contents': response,
        'ContentsFormat': formats['text'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': human_readable,
        'EntryContext': entry_context
    })


def get_vm(args):
    # Retrieve relevant command arguments
    resource_group = args.get('resource_group')
    vm_name = args.get('virtual_machine_name')

    # Construct endpoint URI suffix
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines/' + vm_name
    parameters = {'$expand': 'instanceView', 'api-version': API_VERSION}

    # Call API
    response = http_request('GET', url_endpoint, params=parameters, codes={200})

    return response


def get_vm_command():
    """
    Get the properties of a specified Virtual Machine

    demisto parameter: (string) resource_group
        Resource Group to which the virtual machine belongs

    demisto parameter: (string) virtual_machine_name
        Name of the virtual machine you wish to view the details of

    returns:
        Virtual Machine Object
    """
    args = demisto.args()
    response = get_vm(args)

    # Retrieve relevant properties to return to context
    vm_name = response.get('name').lower()
    properties = response.get('properties')
    os_disk = properties.get('storageProfile', {}).get('osDisk')
    datadisk = os_disk.get('diskSizeGB', 'NA')
    vm_id = properties.get('vmId')
    os_type = os_disk.get('osType')
    provisioning_state = properties.get('provisioningState')
    location = response.get('location')
    statuses = properties.get('instanceView', {}).get('statuses')
    power_state = None
    for status in statuses:
        status_code = status.get('code')
        status_code_prefix = status_code[:status_code.find('/')]
        if status_code_prefix == 'PowerState':
            power_state = status.get('displayStatus')

    vm = {
        'Name': vm_name,
        'ID': vm_id,
        'Size': datadisk,
        'OS': os_type,
        'ProvisioningState': provisioning_state,
        'Location': location,
        'PowerState': power_state,
        'ResourceGroup': args.get('resource_group')
    }

    title = 'Properties of VM "{}"'.format(vm_name)
    table_headers = ['Name', 'ID', 'Size', 'OS', 'ProvisioningState', 'Location', 'PowerState']
    human_readable = tableToMarkdown(title, vm, headers=table_headers, removeNull=True)
    entry_context = {'Azure.Compute(val.Name && val.Name === obj.Name)': vm}
    demisto.results({
        'Type': entryTypes['note'],
        'Contents': response,
        'ContentsFormat': formats['json'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': human_readable,
        'EntryContext': entry_context
    })


def create_vm(args):
    # Retrieve relevant command arguments
    resource_group = args.get('resource_group')
    vm_name = args.get('virtual_machine_name')

    # Construct endpoint URI suffix
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines/' + vm_name
    parameters = {'api-version': API_VERSION}

    # Construct VM object utilizing parameters passed as command arguments
    payload = create_vm_parameters(args)

    # Call API
    response = http_request('PUT', url_endpoint, params=parameters, j_son=payload)

    return response


def create_vm_command():
    """
    Create a virtual machine instance with the specified OS image

    demisto parameter: (string) resource_group
        Resource group to which the new VM will belong

    demisto parameter: (string) virtual_machine_name
        Name to assign to the new virtual machine

    demisto parameter: (string) virtual_machine_location
        Region in which the vm will be hosted

    demisto parameter: (string) nic_name
        The name of the Network Interface to link the VM with. This must be created from the Azure Portal

    demisto parameter: (string) vm_size
        The name of a VirtualMachineSize which determines the size of the deployed vm

    demisto parameter: (string) os_image
        Choose the base operating system image of the vm

    demisto parameter: (string) sku
        SKU of the image to be used

    demisto parameter: (string) publisher
        Name of the publisher of the image

    demisto parameter: (string) version
        Version of the image to use

    demisto parameter: (string) offer
        Specifies the offer of the platform image or marketplace image used
        to create the virtual machine

    demisto parameter: (string) admin_username
        Admin Username to be used when creating the VM

    demisto parameter: (string) admin_password
        Admin Password to be used when creating the VM

    returns:
        Virtual Machine Object
    """
    args = demisto.args()
    response = create_vm(args)

    # Retrieve relevant properties to return to context
    vm_name = response.get('name').lower()
    properties = response.get('properties')
    os_disk = properties.get('storageProfile', {}).get('osDisk')
    datadisk = os_disk.get('diskSizeGB', 'NA')
    vm_id = properties.get('vmId')
    os_type = os_disk.get('osType')
    provisioning_state = properties.get('provisioningState')
    location = response.get('location')

    vm = {
        'Name': vm_name,
        'ID': vm_id,
        'Size': datadisk,
        'OS': os_type,
        'ProvisioningState': provisioning_state,
        'Location': location,
        'ResourceGroup': args.get('resource_group')
    }

    title = 'Created Virtual Machine "{}"'.format(vm_name)
    human_readable = tableToMarkdown(title, vm, removeNull=True)
    entry_context = {'Azure.Compute(val.Name && val.Name === obj.Name)': vm}
    demisto.results({
        'Type': entryTypes['note'],
        'Contents': response,
        'ContentsFormat': formats['json'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': human_readable,
        'EntryContext': entry_context
    })


def delete_vm(args):
    # Retrieve relevant command arguments
    resource_group = args.get('resource_group')
    vm_name = args.get('virtual_machine_name')

    # Construct endpoint URI suffix (for de-allocation of compute resources)
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines/' + vm_name + '/deallocate'
    parameters = {'api-version': API_VERSION}

    # Call API to deallocate compute resources
    http_request('POST', url_endpoint, params=parameters, codes={200, 202})

    # Construct endpoint URI suffix (for deletion)
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines/' + vm_name
    parameters = {'api-version': API_VERSION}

    # Call API to delete
    response = http_request('DELETE', url_endpoint, params=parameters, codes={200, 202, 204})

    return response


def delete_vm_command():
    """
    Delete a specified Virtual Machine

    demisto parameter: (string) resource_group
        Resource Group to which the virtual machine belongs

    demisto parameter: (string) virtual_machine_name
        Name of the virtual machine to delete

    returns:
        Success message to the war room
    """
    args = demisto.args()
    delete_vm(args)
    success_msg = '"{}" VM Deletion Successfully Initiated'.format(args.get('virtual_machine_name'))
    demisto.results(success_msg)


def start_vm(args):
    # Retrieve relevant command arguments
    resource_group = args.get('resource_group')
    vm_name = args.get('virtual_machine_name')

    # Construct endpoint URI suffix
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines/' + vm_name + '/start'
    parameters = {'api-version': API_VERSION}

    # Call API
    response = http_request('POST', url_endpoint, params=parameters, codes={202})

    return response


def start_vm_command():
    """
    Power-on a specified Virtual Machine

    demisto parameter: (string) resource_group
        Resource Group to which the virtual machine belongs

    demisto parameter: (string) virtual_machine_name
        Name of the virtual machine to power-on

    returns:
        Virtual Machine Object
    """
    args = demisto.args()
    vm_name = args.get('virtual_machine_name').lower()

    # Raise an exception if the VM isn't in the proper provisioning state
    validate_provisioning_state(args)

    start_vm(args)

    vm = {
        'Name': vm_name,
        'ResourceGroup': args.get('resource_group'),
        'PowerState': 'VM starting'
    }

    title = 'Power-on of Virtual Machine "{}" Successfully Initiated'.format(vm_name)
    human_readable = tableToMarkdown(title, vm, removeNull=True)
    entry_context = {'Azure.Compute(val.Name && val.Name === obj.Name)': vm}
    demisto.results({
        'Type': entryTypes['note'],
        'Contents': vm,
        'ContentsFormat': formats['json'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': human_readable,
        'EntryContext': entry_context
    })


def poweroff_vm(args):
    # Retrieve relevant command arguments
    resource_group = args.get('resource_group')
    vm_name = args.get('virtual_machine_name')

    # Construct endpoint URI suffix
    url_endpoint = resource_group + '/providers/Microsoft.Compute/virtualMachines/' + vm_name + '/powerOff'
    parameters = {'api-version': API_VERSION}

    # Call API
    response = http_request('POST', url_endpoint, params=parameters, codes={202})

    return response


def poweroff_vm_command():
    """
    Power-off a specified Virtual Machine

    demisto parameter: (string) resource_group
        Resource Group to which the virtual machine belongs

    demisto parameter: (string) virtual_machine_name
        Name of the virtual machine to power-off

    returns:
        Virtual Machine Object
    """
    args = demisto.args()
    vm_name = args.get('virtual_machine_name').lower()

    # Raise an exception if the VM isn't in the proper provisioning state
    validate_provisioning_state(args)

    poweroff_vm(args)

    vm = {
        'Name': vm_name,
        'ResourceGroup': args.get('resource_group'),
        'PowerState': 'VM stopping'
    }

    title = 'Power-off of Virtual Machine "{}" Successfully Initiated'.format(vm_name)
    human_readable = tableToMarkdown(title, vm, removeNull=True)
    entry_context = {'Azure.Compute(val.Name && val.Name === obj.Name)': vm}
    demisto.results({
        'Type': entryTypes['note'],
        'Contents': vm,
        'ContentsFormat': formats['json'],
        'ReadableContentsFormat': formats['markdown'],
        'HumanReadable': human_readable,
        'EntryContext': entry_context
    })


'''COMMAND SWITCHBOARD'''

commands = {
    'azure-vm-list-instances': list_vms_command,
    'azure-vm-get-instance-details': get_vm_command,
    'azure-vm-start-instance': start_vm_command,
    'azure-vm-poweroff-instance': poweroff_vm_command,
    'azure-vm-create-instance': create_vm_command,
    'azure-vm-delete-instance': delete_vm_command,
    'azure-list-resource-groups': list_resource_groups_command
}

'''EXECUTION'''

try:
    # Initial setup
    SUBSCRIPTION_ID = set_subscription_id()
    BASE_URL = SERVER + '/subscriptions/' + SUBSCRIPTION_ID + '/resourceGroups/'

    if demisto.command() == 'test-module':
        test_module()
    elif demisto.command() in commands.keys():
        commands[demisto.command()]()

except Exception as e:
    screened_error_message = screen_errors(e.message, TENANT_ID)
    return_error(screened_error_message)