Automation Request Approval

In Calling Automation Using the RESTful API we looked at how external systems can make use of ManageIQ Automate workflows by calling the RESTful API. In the examples we specified :auto_approve ⇒ true in the REST call so that our requests were immediately processed, however we can only auto-approve our own requests if we authenticate as an admin user.

Embedding admin credentials in our external (calling) scripts is generally considered unwise, but if we still want our automation requests to be auto-approved, what can we do?

Fortunately by this stage in the book we have learned enough to be able to implement our own approval workflow for automation requests. The example in this chapter uses an access control group profile to control which groups can submit auto-approved automation requests.

Implementing a Custom Approval Workflow

Our automation request approval workflows will follow a very similar pattern to those for provision request approval, and we’ll re-use and adapt several of the components. We’ll implement two workflows; one triggered from a request_created event, and one from a request_pending event (see Event-triggered automation request approval workflows).

Screenshot
Figure 1. Event-triggered automation request approval workflows


Before we implement anything we need to create some new Automate datastore components to hold our workflow objects.

Namespace

We’ll create a new namespace called Automation in our own domain.

Group Profile

We’ll create a simple variant of the virtual machine provisioning group profile (we can copy this from the ManageIQ domain and edit it). Our profile class will contain two instances (profiles), Bit63Group_vm_user and .missing (see Automation approval group profiles).

Screenshot
Figure 2. Automation approval group profiles


The profile merely contains the name of the auto-approval state machine instance that will be used to determine whether or not the request is auto-approved. The profile is queried using the message get_auto_approval_state_machine_instance, and returns the Value field via a collect as /state_machine_instance.

We’ll allow members of group Bit63Group_vm_user to have their requests auto-approved, and everyone else (including admins who haven’t specified :auto_approve ⇒ true) will require explicit approval.

The profile for the group Bit63Group_vm_user is shown in Profile schema for group Bit63Group_vm_user.

Screenshot
Figure 3. Profile schema for group Bit63Group_vm_user


The .missing profile for all other groups is shown in Profile schema for .missing.

Screenshot
Figure 4. Profile schema for .missing


State Machine

We’ll create a StateMachines namespace, and a simple variant of the VM ProvisionRequestApproval class. We’ll copy the ProvisionRequestApproval class from the ManageIQ domain into ours under the new StateMachines namespace, and call it AutomationRequestApproval. We’ll copy the associated instances and methods as well (see AutomationRequestApproval instances and methods).

Screenshot
Figure 5. AutomationRequestApproval instances and methods


Instances

The RequireApproval instance has an approval_type value of require_approval (see Fields of the RequireApproval instance).

Screenshot
Figure 6. Fields of the RequireApproval instance


The Auto instance is similar, but has an approval_type value of auto.

Methods

The validate_request method is as follows:

request = $evm.root['miq_request']
resource = request.resource
raise "Automation Request not found" if request.nil? || resource.nil?

$evm.log("info", "Checking for auto_approval")
approval_type = $evm.object['approval_type'].downcase
if approval_type == 'auto'
  $evm.root["miq_request"].approve("admin", "Auto-Approved")
  $evm.root['ae_result'] = 'ok'
else
  msg =  "Request was not auto-approved"
  resource.set_message(msg)
  $evm.root['ae_result'] = 'error'
  $evm.object['reason'] = msg
end

The pending_request method is as follows:

#
# This method is executed when the automation request is NOT auto-approved
#
# Get objects
msg = $evm.object['reason']
$evm.log('info', "#{msg}")

# Raise automation event: request_pending
$evm.root["miq_request"].pending

The method definition is also given an input parameter with Input Name reason and Data Type string

The approve_request method is as follows:

#
# This method is executed when the automation request is auto-approved
#
# Auto-Approve request
$evm.log("info", "AUTO-APPROVING automation request")
$evm.root["miq_request"].approve("admin", "Auto-Approved")

Email Classes

We create an Email class, with an AutomationRequest_Pending instance and method (see Email classes and methods).

Screenshot
Figure 7. Email classes and methods


The method code is copied and adapted as appropriate from the VM ProvisionRequest_Pending method. We specify as the to_email_address a user that will act as approver for the automation requests.

The full code for the methods is here

Policies

We need to generate policy instances for two AutomationRequest events, AutomationRequest_created and AutomationRequest_approved. We copy the standard /System/Policy class to our domain, and add two instances (see New policy instances).

Screenshot
Figure 8. New policy instances


AutomationRequest_created

Our policy instance for AutomationRequest_created has three entries; an assertion and two relationships. We need to recognise whether an automation request was made with the :auto_approve ⇒ true parameter. If it was, we need to skip our own approval workflow.

We know (from some investigative debugging using ObjectWalker) that when a request is made that specifies :auto_approve ⇒ true, we have an $evm.root['automation_request'].approval_state attribute with a value of approved. When a request is made that specifies :auto_approve ⇒ false this value is pending_approval. We can therefore create our assertion to look for $evm.root['automation_request'].approval_state == 'pending_approval', and continue with the instance only if the boolean test returns true.

The rel1 relationship of this instance performs a profile lookup based on our user group, to find the auto-approval state machine instance that should be run. The rel2 relationship calls this state machine instance (see Fields of the AutomationRequest_created instance).

Screenshot
Figure 9. Fields of the AutomationRequest_created instance


AutomationRequest_pending

The AutomationRequest_pending instance contains a single relationship to our AutomationRequest_pending email instance (see Fields of the AutomationRequest_pending instance).

Screenshot
Figure 10. Fields of the AutomationRequest_pending instance


Testing

We’ll submit three automation requests via the RESTful API, calling a simple Test instance. The calls will be made as follows:

  • As user admin, specifying :auto_approve ⇒ true

  • As user admin, specifying :auto_approve ⇒ false

  • As a user who is a member of the group Bit63Group_vm_user

For the first call, our assertion correctly prevents our custom approval workflow from running (the request has already been auto-approved). From automation.log we see:

Evaluating substituted assertion ["approved" == "pending_approval"]
Assertion Failed: <"approved" == "pending_approval">
Followed  Relationship [miqaedb:/System/Policy/AutomationRequest_created#create]
Followed  Relationship [miqaedb:/System/Policy/request_created#create]
Followed  Relationship [miqaedb:/System/Event/request_created#create]

For the second call we see that the assertion evaulates to true, but the user admin's group (EVMGroup-super_administrator) doesn’t have a group profile. The .missing profile is used, and the automation request is not auto-approved.

The admin user receives an email:

Request was not auto-approved.

Please review your Request and update or wait for approval from an Administrator.

To view this Request go to: https://192.168.1.45/miq_request/show/125

Thank you,
Virtualization Infrastructure Team

The approving user also receives an email:

Approver,
An automation request received from admin@bit63.com is pending.

Request was not auto-approved.

For more information you can go to: https://192.168.1.45/miq_request/show/125

Thank you,
Virtualization Infrastructure Team

Clicking the link takes us to an approval page, and we can approve the request, which then continues.

For the third call we see that the assertion evaluates to true, but this time we see the valid group profile being used:

Evaluating substituted assertion ["pending_approval" == "pending_approval"]
Following Relationship [miqaedb:/Automation/Profile/Bit63Group_vm_user#get_auto..

This group’s profile auto-approves the automation request, and the Test instance is successfully run:

Q-task_id([automation_task_186]) \
                          <AEMethod test> Calling the test method was successful!

Success!

Summary

In this chapter we’ve assembled many of the Automate components that we’ve studied throughout the book to create our own custom approval workflow. We’ve done it by copying and adapting slightly several existing components in the ManageIQ domain, and adding our own pieces where necessary.

We started off by creating our own namespace to work in, and we added an access control group profile so that we can apply the auto-approval to specific groups. We cloned the ProvisionRequestApproval class and its methods to become our AutomationRequestApproval state machine, and we created two instances, one called Auto, and one called RequireApproval. We added an Email class and cloned and adapted the ProvisionRequest_Pending instance and method to become our AutomationRequest_Pending versions. Finally we added two policy instances to handle the two Automation request_created and request_pending events.

Creating an approval workflow such as this is really just a case of putting the pieces in place and wiring it together. We know that approval workflows start with an event, and that the event is translated to a policy. As long as our policy instances route the workflow into the appropriate handlers (generally a state machine or email class), all that is left is to adapt the method code to our specific purposes, and test.

results matching ""

    No results matching ""