Contents

VMware vRealize SaltStack Config API

Using SaltStack Config api for Automation from vRA Catalog or cmd


A personal goal of mine for 2022 was to start doing more with Python when working with VMware SaltStack Config. As a Windows Admin most of my automation was done with PowerShell. All the examples you see in the salt documentation are written in Python, so to use Python for some processes makes sense.

I had a use case that made sense to use Python. I want to schedule reboots of AWS EC2 instances using SaltStack Config. For Servers that are on-prem I would always automate the process to create a server reboot scheduled task in vCenter. With AWS I want to use VMware SaltStack Config to schedule the Server Reboot.

Manual Steps:

  • Create a SaltStack Config Job to do the reboot.
    I create a job named “vRA | Server Reboot”. This job will be used for all scheduled server reboots.
  • Copy the Python Code below and create a ScheduleServerReboot.py file that will be copied to the SSC server.

The Python Code completes the following steps:

  • Creates a Target that uses the grain “id” and will only target the Server that you want to reboot.
  • Creates a Run Once Schedule to use the job and new target that was created.
  • Sends an email to show that the run once schedule to reboot server was created.

SaltStack Config Server:
My SaltStack Config Server (SSC) is what VMware provides when using Life Cycle Manager (LCM) running Photon OS.


Python Code:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# --- Python Code ---
# --- In my code I show the Password. In Production DO NOT DO THIS.
# --- There are so many different ways to include encrypted PWs in the code. 
# --- Use what works best in your environment.

# example to run the script
# python3 /scripts/ScheduleServerReboot.py -name '2019DC' -dateTime '2022-06-25T23:00'

import argparse
import pprint
import json
from datetime import datetime
from datetime import timedelta
import random
import smtplib, ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

# --- Generate a random ID number
randomNumber = random.randint(0,1000)
randomNumber = '0000' + str(randomNumber)
randomNumber = randomNumber[-4:]

print('ID:',randomNumber)

# --- parse arguments
parser = argparse.ArgumentParser(description="Create Targets", formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-name", help="Target Name")
parser.add_argument("-dateTime", help="Schedule Date | Time")

args = vars(parser.parse_args())

# --- Set up variables
argName     = args["name"]
argDateTime = args["dateTime"]
argDate     = argDateTime[0:10]

# --- Print the variables
print('Server Name: ',argName)
print('Date | Time: ',argDateTime)

# --- Connect to SSC Server
host     = 'https://192.168.86.110'
user     = 'root'
password = 'HackMe!'

from sseapiclient.tornado import SyncClient
client = SyncClient.connect(host, user, password, ssl_validate_cert=False)

# --- Check to make sure minion exists
minionName = ''
minionReturn = client.api.minions.get_minion_presence(minion_id = argName)
#print(targetReturn)

for x in minionReturn.ret['results']:
    #print(x)
    minionName = x['minion']
    print('minionName:', minionName)

if minionName == '':
    print('Minion not found!')
    minionExists = 'false'
else:
    print('Minion found!')
    minionExists = 'true'

    # --- Create Target ID | Name
    targetID = 'id:' + argName
    targetName = "vRA | Reboot | " + argName + ' | ' + argDate + ' | ID:' + randomNumber
    print('Target ID:',targetID)
    print('Target Name:',targetName)

    # --- Create New SSC Target
    client.api.tgt.save_target_group(tgt={'*': {'tgt_type': 'grain', 'tgt':targetID}}, name=targetName)

    # --- Get UUID of new Target Created
    targetReturn = client.api.tgt.get_target_group(name=targetName)
    for x in targetReturn.ret['results']:
        #print(x)
        targetUUID = x['uuid']
        print('Target UUID:', targetUUID)


    # --- Get UUID of Job to run in Schedule
    #jobReturn=client.api.job.get_jobs()
    jobName = 'vRA | Server Reboot'
    print('Job Name: ',jobName)
    jobReturn = client.api.job.get_jobs(name=jobName)

    for x in jobReturn.ret['results']:
        #print(x)
        jobUUID = x['uuid']
        print('Job UUID:', jobUUID)


    # -- Create a run once Schedule
    # 2022-06-21T13:21 - Date|Time format from vRA
    print('Schedule Time:',argDateTime)

    # --- Create Schedule Name
    scheduleName = 'vRA | Reboot | ' + argName + ' | ' + argDate + ' | ID:' + randomNumber
    print('Schedule Name:', scheduleName)

    # --- Create Run Once Schedule
    scheduleReturn = client.api.schedule.save(
        name=scheduleName,
        schedule={'once': argDateTime, 'timezone': 'America/New_York'},
        cmd="local",
        tgt_uuid=targetUUID,
        job_uuid=jobUUID
    )

# --- Send Email about vRA Request using gmail.
sender_email   = "dale.hassinger@gmail.com"
receiver_email = "dale.hassinger@vcrocs.info"
password       = "HackMe!"

message = MIMEMultipart("alternative")
message["Subject"] = "vRA Scheduled EC2 Reboot | " + argName
message["From"]    = sender_email
message["To"]      = receiver_email

# --- Create HTML Body
if minionExists == 'true':
    html = """
    <html>
    <body>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"><b>A Request to reboot a AWS EC2 was run from vRA.</b></div>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"></div><br>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"><b>EC2 Information:</b></div>
    <ul style="list-style-type:disc">
    <div style="font-family: Arial, sans-serif; font-size: 12px;"><li><b>EC2 Name: """         + argName      + """</b></li></div>
    <div style="font-family: Arial, sans-serif; font-size: 12px;"><li>Target Created: """      + targetName   + """</li></div>
    <div style="font-family: Arial, sans-serif; font-size: 12px;"><li>Schedule Created: """    + scheduleName + """</li></div>
    <div style="font-family: Arial, sans-serif; font-size: 12px;"><li>Reboot Date | Time: """  + argDateTime  + """</li></div>
    </ul>
    <div style="font-family: Arial, sans-serif; font-size: 11px;"><b>vCROCS - Automated IT </b></div>
    <div style="font-family: Arial, sans-serif; font-size: 10px;">#VMware #vRealize #SaltStackConfig</div>
    </body>
    </html>
    """
elif minionExists == 'false':
    html = """
    <html>
    <body>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"><b>A Request to reboot a AWS EC2 was run from vRA.</b></div>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"></div><br>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"><b>EC2 Information:</b></div>
    <ul style="list-style-type:disc">
    <div style="font-family: Arial, sans-serif; font-size: 12px;"><li><b>EC2 Name: """         + argName      + """</b></li></div>
    </ul>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"><b>The Server Name was not found in Salt! Please double check name and try again!</b></div>
    <div style="font-family: Arial, sans-serif; font-size: 14px;"></div><br>
    <div style="font-family: Arial, sans-serif; font-size: 11px;"><b>vCROCS - Automated IT </b></div>
    <div style="font-family: Arial, sans-serif; font-size: 10px;">#VMware #vRealize #SaltStackConfig</div>
    </body>
    </html>
    """

# --- HTML MIMEText objects
emailHTML = MIMEText(html, "html")

# --- Add the HTML part to MIMEMultipart message
message.attach(emailHTML)

# --- Create secure connection and send HTML email
context = ssl.create_default_context()
with smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context) as server:
    server.login(sender_email, password)
    server.sendmail(
        sender_email, receiver_email, message.as_string()
    )

Example of email that is sent when process completes.

/saltstack-config-api/Salt-53.PNG
Click to see Larger Image

Screen Shots of the SaltStack Config Job, Target and Schedule:

SSC Job:

/saltstack-config-api/Salt-58.PNG
Click to see Larger Image
/saltstack-config-api/Salt-59.PNG
Click to see Larger Image

SSC Target:

/saltstack-config-api/Salt-60.PNG
Click to see Larger Image

/saltstack-config-api/Salt-61.PNG
Click to see Larger Image

/saltstack-config-api/Salt-62.PNG
Click to see Larger Image

/saltstack-config-api/Salt-63.PNG
Click to see Larger Image


To run this process to reboot an AWS EC2 from the vRA catalog for Day 2 operations I used the OOTB (Out of The Box) orchestrator workflow named “Run SSH command”. I never modify the OOTB workflows. I cloned this Workflow and named it “Schedule EC2 Reboot”.

Original OOTB Workflow from vRO:

/saltstack-config-api/Salt-54.PNG
Click to see Larger Image

Cloned Worked in vRO:

/saltstack-config-api/Salt-55.PNG
Click to see Larger Image

/saltstack-config-api/Salt-56.PNG
Click to see Larger Image

/saltstack-config-api/Salt-57.PNG
Click to see Larger Image

Very simple form to fill out. Enter EC2 name and Date|Time.

The Workflow takes the data from the vRA custom form and does an SSH Connection to the SSC server and runs a single line command using the data from the form as arguments:

python3 /scripts/ScheduleServerReboot.py -name ‘2019DC’ -dateTime 2022-06-25T23:00’


  • I hope the provided code and screenshots were helpful to get you started.

Lessons Learned:
  • If you are a Windows Server Admin that knows PowerShell, to start using Python will take some time but the languages are similar.
  • SaltStack Config api is a great way to create Day 2 automation processes in vRA.
  • SaltStack Config - RaaS API Documentation

When I write about vRealize Automation ("vRA") I always say there are many ways to accomplish the same task. SaltStack Config is the same way. I am showing what I felt was important to see but every organization/environment will be different. There is no right or wrong way to use Salt. This is a GREAT Tool that is included with your vRealize Suite Advanced/Enterprise license. If you own the vRealize Suite, you own SaltStack Config.

  • If you like wearing Crocs and want to get a pair like I wear, follow this link to Amazon: My Favorite Crocs
  • If you found this Blog article useful and it helped you, Buy me a coffee to start my day.