Microservices

Windows Commands and PowerShell Scripts as API Microservices

Command

Windows commands? Yes, please!

This post goes through the steps of exposing Windows commands and PowerShell scripts as remote Zato API services that can be invoked by REST clients.

You may also like: Invoking REST APIs From Java Microservices

This lets one access a fleet of Windows systems from a single place and makes it possible for Zato services to participate in Windows management processes.

Note that Zato servers always run on Linux and no installation of any kind of software under Windows is necessary for Zato to connect to remote systems.

Prerequisites

Start by installing a library that implements the remote Windows connectivity.

$ cd /opt/zato/current
$ ./bin/pip install pywinrm

Next, stop and start again any servers running, e.g.

$ zato stop /path/to/server
$ zato start /path/to/server

Python Code

Deploy the following service to your Zato cluster — note its name, windows.remote.management.

# -*- coding: utf-8 -*- from __future__ import absolute_import, division, print_function, unicode_literals # stdlib
from traceback import format_exc # pywinrm
import winrm # Zato
from zato.server.service import Service class MyService(Service): name = 'windows.remote.management' class SimpleIO: input_required = 'type', 'data' output_required = 'exec_code' output_optional = 'status_code', 'stdout', 'stderr', 'details' response_elem = None skip_empty_keys = True def handle(self): # Local aliases input_type = self.request.input.type input_data = self.request.input.data # Validate input - we support either regular commands # or PowerShell scripts on input. if input_type not in ('cmd', 'ps'): self.response.payload.exec_code = 'error' self.response.payload.details = 'Invalid type' # Input was valid, we can try to execute the command now else: try: # Remote server details host = '10.151.139.17' # Credentials username = 'myuser' password = 'NZaIhMezvK00Y' # Establish a connection to the remote host session = winrm.Session(host, (username, password)) # Dynamically select a function to run, # either for commands or PowerShell func = session.run_cmd if input_type == 'cmd' else session.run_ps # Run the function with input data given result = func(input_data) # Status code is always available self.response.payload.status_code = result.status_code self.response.payload.stdout = result.std_out self.response.payload.stderr = result.std_err except Exception: self.response.payload.exec_code = 'error' self.response.payload.details = format_exc() # Everything went fine else: self.response.payload.exec_code = 'ok'

REST Channel

Create a new REST channel in web-admin and mount service windows.remote.management on it. Make sure to set the data format to JSON.

Usage

Let us invoke the service from the command line, using curl. For clarity, the output of commands below is limited to a few lines.

First, we will run a regular command to get a directory listing of drive C.

$ curl http://zz:[email protected]:11223/windows -d '{"type":"cmd", "data":"dir c:"}'

{"status_code": 0, "exec_code": "ok", "stdout": " Volume in drive C has no label.\r\n Volume Serial Number is 1F76-3AB6\r\n\r\n Directory of C:\\Users\\Administrator\r\n\r\n07/22/2019 11:53 PM <DIR>"}

What if we provide an invalid drive name?

$ curl http://zz:[email protected]:11223/windows -d '{"type":"cmd", "data":"dir z:"}'

{"status_code": 1, "exec_code": "ok", "stderr": "The system cannot find the path specified.\r\n"}

Now, invoke a PowerShell script, which in this case is a single-line one to check the connection from the remote Windows system to example.com, but it could be much more complex, there are no limitations:

$ curl http://zz:[email protected]:11223/windows -d \ '{"type":"ps", "data":"Test-Connection example.com"}'

This time, both stdout and stderr are returned but because the overall status_code is 0, we know that the invocation was successful.

{"status_code": 0, "exec_code": "ok", "stdout": "WIN-A3I92B... example.com 93.184.216.34", "stderr": "#< CLIXML\r\n<Objs Version=\"1.1.0.1\" ...",

In Conclusion

The service is just a starting point and there are a couple of ways to extend it:

  • Details of remote servers, including credentials, should be kept separately.
  • Permissions, including ACLs, can be added to allow or disallow access to particular commands to selected REST users only.

Yet, even in this simple form, it already shows how easy it is to connect to Windows servers and turn remote commands into REST APIs microservices.

On top of it, REST is but one of many formats that Zato supports — one could just as well design workflows around AMQP, ZeroMQ, IBM MQ, FTP or other protocols in addition to REST with no changes to Python code required.

Further Reading

API vs. Microservices: A Microservice Is More Than Just an API

API Security for Microservices

API Interaction Types in a Microservice Architecture: Queries, Commands, and Events

Comment here

This site uses Akismet to reduce spam. Learn how your comment data is processed.