21

I want to use Azure CLI to get the list of all the VMs in my resource group. But I want to implement the same using a python script.

For example, I will use the following command in Azure CLI to list the VMs in my resource group:

" az vm list -g MyResourceGroup "

But, I want the python script to do the same, where I just have to incorporate the CLI command in the python program.

Software Fan
  • 609
  • 3
  • 8
  • 17
  • 3
    Just curious: Why attempt to call the CLI commands, when you already have the management REST API available to you, wrapped in a python SDK? The CLI and the various SDKs (including python) are built upon the same REST API. – David Makogon Jul 26 '18 at 19:23
  • its a lot easier to use cli, like a LOT. we were using some sdk calls and some cli calls in our python solution. mostly because of that – 4c74356b41 Jul 26 '18 at 19:26
  • well, just do a standard shell command, thats it – 4c74356b41 Jul 26 '18 at 19:26
  • If you want to do this, you want the [`subprocess`](https://docs.python.org/3/library/subprocess.html) module. Read the introduction, go through the examples in the "Replacing Older Functions", then use the rest as a reference to help you as needed to flesh out the details. – abarnert Jul 26 '18 at 19:28
  • Well, I am able to get the information all I need using python SDK and using those functions. But I wanted to use the CLI for quick checks. So, that I dont have t go into the program and edit it for small changes. But I have multiple CLI commands that I need to run. So, I though using python for just running these CLI commands would be great. – Software Fan Jul 26 '18 at 19:30
  • 1
    @4c74356b41 - there is a function defined in the python sdk ( `list_resources()` ) for enumerating items in a resource group. Unless the OP is doing something esoteric, this is all built-in and has no dependency on running shell processes. – David Makogon Jul 26 '18 at 19:30
  • Also, if you're trying to specifically call things via PowerShell, and your machine might have `cmd` instead of `PowerShell` as the `COMSPEC` shell, that isn't directly covered in any of the examples; you want to use `shell=False` and explicitly call `PowerShell as your command, passing your real command as the arguments. Basically the same way you'd do it from the `cmd` prompt. – abarnert Jul 26 '18 at 19:31
  • well, i'm not suggesting to use azure cli for this particular call, but there are many instances where using cli is a lot easier than using SDK, especially when you are time constrained and familiar with cli, but not with SDK. I worked about a year with Python SDK, i'm well aware of that – 4c74356b41 Jul 26 '18 at 19:38
  • So, how do I use Python to run the Azure CLI? – Software Fan Jul 26 '18 at 19:55

7 Answers7

25

I have been implementing this over the last couple days. The method that @cbehrenberg provides is mostly what I used, but I found that you can do it without using a temporary file. Instead catch the output directly from the azure client. Thought it might be useful.

from azure.cli.core import get_default_cli

def az_cli (args_str):
    args = args_str.split()
    cli = get_default_cli()
    cli.invoke(args)
    if cli.result.result:
        return cli.result.result
    elif cli.result.error:
        raise cli.result.error
    return True

Then invoked the same way:

from azhelper import az_cli

response = az_cli("vm list")
print("vm's: %s" % (response))
Ash
  • 657
  • 9
  • 16
joek575
  • 561
  • 5
  • 9
  • 3
    If you want to suppress the output to stdout then invoke the cli like this: `cli.invoke(args, out_file = open(os.devnull, 'w'))` – ChrisWue Oct 30 '19 at 20:06
  • When I run `az ad sp list --display-name example-name --query "[].appId"`, I can get the result from the az cli but when I call this from a python script like `run_az_cli('ad sp list --display-name example-name --query "[].appId"')`, I don't get any results. This is happening when I include the --query "[].appId" to the string. Somehow, the cli couldn't get the result because I am assuming the library does not support something like --query "[].appId"? Have you ever try anything like this? – Alper Silistre Apr 28 '20 at 14:57
  • 1
    @alpersilistre Try removing the double quotes around your query - I ran into this issue today. The reason why the double quotes work in a shell but not with the az_cli function from this answer is that the shell interprets and then removes the quotes before handing the arguments to the az cli. The az_cli python function, on the other hand, just splits by space, leaving any quotes in place. – Michi Werner Sep 17 '20 at 21:39
  • I am getting this error when I run this : `CommandNotFoundError: 'az' is misspelled or not recognized by the system.` – Sajal Jan 18 '21 at 09:52
  • 2
    @Sajal - I was getting the same error message because we are including 'az' at the beginning of the arguments. Since the CLI automatically calls the 'az' command, we don't need to include it in the arg list. – adam Apr 09 '21 at 08:46
  • Isn't there a potential problem with argument splitting that if you give a string with spaces in it as argument to some command it will split them to separate arguments. Cannot thing of right away where I would need that, but just a thought. So might need some more advanced parsing in that case or just having another method to take the argument string list directly. Anyways exactly what I was looking for to replace powershell which I seem to run into conflict with all the time even after years of semi regular script writing. – user232548 Feb 17 '22 at 09:50
11

How to run Azure CLI commands using python?

According to this file. we could invoke the Azure CLI with following way:

from azure.cli.core import get_default_cli
get_default_cli().invoke(['vm', 'list', '-g', 'groupname'])

Note : If you get No module named 'azure.cli.command_modules' error, please install azure-cli

enter image description here

Tom Sun - MSFT
  • 24,161
  • 3
  • 30
  • 47
  • I get the error you mentioned when packaging the app with cx_freeze. It works in the .py format just fine. Any thoughts why packaging is breaking it? – Vitalijs Arkulinskis Jan 11 '21 at 01:14
8

Using the subprocess solution is problematic as subprocess doesn't check the PATH where Azure CLI is found e.g. on Windows. To use "cmd -c" would be a Windows-specific solution and needs a fork if/else for additional Linux support.

@tom-sun's answer is almost correct, as you can reuse the Azure CLI python modules, as the CLI is also written in Python. Problem is, that the return of the .invoke() instruction is always returning the error code. To get the full body response, you must pass a file like object on the argument list of the underlying Knack Code to get the response. By default, this redirects to StdOut, that is why you can see it e.g. in your terminal but you always get a zero for success.

I wrote a little helper function that accepts the Azure CLI instructions in one string (I don't like many arguments as list, it doesn't read nicely - but that is just a personal preference, don't blame me). It uses a temporary file as output target and then it is read back in memory - this is required afaik from the underlying Knack CLI code; by default StdOut is the standard pipe.

It requires you to have azure-cli installed for Python: pip install azure-cli

File azhelper.py:

from azure.cli.core import get_default_cli
import tempfile

def az_cli (args_str):
    temp = tempfile.TemporaryFile()
    args = args_str.split()
    code = get_default_cli().invoke(args, None, temp)
    temp.seek(0)
    data = temp.read().strip()
    temp.close()
    return [code, data]

You can then invoke like this:

from azhelper import *

code, response = az_cli("vm list")
print("vm's: %s" % (response))

Of course you need to be logged in, see @4c74356b41 answer.

If somebody finds a better way to deal with the response instead with a temporary file, this would be much appreciated! I tried with an in memory StringIO object but this somehow doesn't comply with the underlying Knack CLI code.

cbehrenberg
  • 81
  • 1
  • 2
  • 1
    Use supprocess.Popen() and the az binary instead. pipe json directly. My major reason for not using pypi azure-cli is that it is 335MB of modules that needs to be installed while the az binary (apt install azure-cli) just takes a few seconds. – MortenB Apr 01 '19 at 11:57
  • 2
    I have to use tempfile.TemporaryFile(mode = "r+") and code = get_default_cli().invoke(args, out_file=temp) But all the other bits work great!! Thank you. – drWatson Oct 11 '19 at 10:27
4

My understanding is that the current recommendation is to use the management libraries for this.

The SDK's management (or "management plane") libraries, the names of which all begin with azure-mgmt-, help you create, provision and otherwise manage Azure resources from Python scripts. All Azure services have corresponding management libraries.
With the management libraries, you can write configuration and deployment scripts to perform the same tasks that you can through the Azure portal or the Azure CLI.

More info here: Provision and manage Azure resources with management libraries

genegc
  • 1,630
  • 18
  • 16
3

since you still didnt delete this I assume you still looking for a way.

from subprocess import call   
call(["az", "vm", "list", "-g", "rgName"])

you will also need to silently auth first with something like:

az login --service-principal -u http://sample-cli-login -p Test1234 --tenant 54826b22-38d6-4fb2-bad9-b7b93a3e9c5a
4c74356b41
  • 69,186
  • 6
  • 100
  • 141
3

I think you can use the subprocess and call the az cli to get the output instead using get_default_cli. Reference Git Repo

import subprocess
import json

process = subprocess.Popen(['az','network', 'ddos-protection', 'list'], stdout=subprocess.PIPE)
out, err = process.communicate()
d = json.loads(out)
print(d)
Andy Wong
  • 3,676
  • 1
  • 21
  • 18
3

Use subprocess.run instead of using subprocess.Popen

It will by default cause python script to wait until the program run from subprocess (in this case azure cli command) is completed.

For example, let's create an Azure AD application.

import subprocess
import json

# create command you want to run on az cli as a string
create_app_command = "az ad sp create-for-rbac --skip-assignment --years 20 --name dummy_app"

# use 'shell = True' as Azure CLI installed on system is accessible from native shell
# using 'subprocess.PIPE' will return stderr and stdout to create_app object
create_app = subprocess.run(create_app_command, shell = True, stdout=subprocess.PIPE, stderr = subprocess.PIPE)

# reading output and error
create_app_stdout =  create_app.stdout.decode("utf-8")
create_app_stderr = create_app.stderr.decode("utf-8")

# now you can log error to a file 'logging.error(create_app_stderr)

# you can use stdout for further logic in code
# You get need to get appID and password for authentication somewhere later in code
ad_app_details = json.loads(create_app_stdout)
ad_app_appId = ad_app_details['appId']
ad_app_password = ad_app_details['password']

For more information on using subprocess module see this link

Wobli
  • 192
  • 14
Arun Pant
  • 116
  • 1
  • 4