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).
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).
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.
The .missing profile for all other groups is shown in 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).
Instances
The RequireApproval instance has an approval_type value of require_approval (see 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).
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).
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).
AutomationRequest_pending
The AutomationRequest_pending instance contains a single relationship to our AutomationRequest_pending email instance (see 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 [email protected] 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.