Part 9: Using salt-api with PowerShell for Windows Server Automation
Update: It was brought to my attention that adding cherypy will break the salt master. After a quick test in my lab I had the same issue with the newest version of SaltStack Config. DO NOT use this blog post until I get a working version in my lab.
If you want to work with the VMware Aria Automation Config API please use this newer Blog Post that I created:
This post is to show how to use PowerShell to make RESTful API calls to SaltStack Config. This is something that I have wanted to do for awhile. I could never find any examples in my Google searches, so I took the time to learn how the examples that use curl work and translated that into PowerShell Code. I hope some Windows Server Admins will find this post helpful. I like using salt to do Windows Server Configuration Management more than remote PowerShell. You can run changes against many servers at the exact same time instead of looping thru a list of servers names.
I am also going to look at use salt-api with vRealize Automation ABX action scripts.
In some previous posts I used the POSH-SSH module but when you use the salt-api, the PowerShell Module POSH-SSH is no longer needed.
Configuration Changes to the SaltStack Config Server:
My SaltStack Config Server (SSC) is what VMware provides when using Life Cycle Manager (LCM) running Photon OS.
The following steps are what I needed to do in my Lab environment from the SSC CLI to enable salt-api:
* Always make sure you have a good snap | backup before making any changes to SSC Server
- Install the PyOpenSSL package:
- Generate a self-signed certificate:
1
|
salt-call --local tls.create_self_signed_cert
|
1
|
iptables -A INPUT -i eth0 -p tcp --dport 8000 -j ACCEPT
|
- Edit /etc/salt/master file:
Add these lines to the /etc/salt/master file:
1
2
3
4
5
6
7
8
9
|
external_auth:
pam:
root:
- .*
rest_cherrypy:
port: 8000
ssl_crt: /etc/pki/tls/certs/localhost.crt
ssl_key: /etc/pki/tls/certs/localhost.key
|
- Restart salt-master and check service status for any errors:
1
2
|
systemctl restart salt-master
systemctl status salt-master
|
- Enable | Start salt-api and check service status for any errors:
1
2
3
|
systemctl enable salt-api
systemctl start salt-api
systemctl status salt-api
|
Tests to make sure SaltStack Config Changes are working:
- I did all this PowerShell code from my mac. To use self-signed certs I use -SkipCertificateCheck. On a Windows OS the code is different.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# --- PowerShell Code ---
Invoke-WebRequest -Uri 'https://192.168.86.141:8000' -SkipCertificateCheck
Results:
StatusCode : 200
StatusDescription : OK
Content : {"return": "Welcome", "clients": ["local", "local_async", "local_batch", "local_subset", "runner", "runner_async", "ssh", "wheel", "wheel_async"]}
RawContent : HTTP/1.1 200 OK
Server: CherryPy/8.9.1
Date: Sat, 11 Jun 2022 20:57:46 GMT
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: GET, POST
Access-Control-Allow-Credentials: true
Vary: Accept-E…
Headers : {[Server, System.String[]], [Date, System.String[]], [Access-Control-Allow-Origin, System.String[]], [Access-Control-Expose-Headers,
System.String[]]…}
Images : {}
InputFields : {}
Links : {}
RawContentLength : 146
RelationLink : {}
|
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
|
# --- PowerShell 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.
$saltServerAddress = 'https://192.168.86.110:8000'
# --- Set the json body
$body = '{ "username": "root","password": "HackMe!","eauth": "pam"}'
# --- Fetch New Token from salt master
$url = "$saltServerAddress/login"
$Params = @{
Method = "Post"
Uri = $url
Body = $Body
ContentType = "application/json"
}
$fetch = Invoke-RestMethod @Params -SkipCertificateCheck
$fetch
$fetch.return.token
Results:
return
------
{@{token=6696846c802f78d5326a69b79d36e95015d37f5a; expire=1655025071.22838; start=1654981871.22838; user=root; eauth=pam; perms=System.Object[]}}
6696846c802f78d5326a69b79d36e95015d37f5a
|
- You will see “token=” in the return data
PowerShell API Code Examples:
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
|
# --- PowerShell Code ---
$minionName = '2019DC'
$saltServerAddress = 'https://192.168.86.110:8000'
$userName = 'root'
$password = 'HackMe!'
$fun = 'test.ping'
$fetch = ''
# --- Set the json body
$body = '{ "username": "' + $userName + '","password": "' + $password + '","eauth": "pam","tgt": "' + $minionName + '","fun": "' + $fun + '","client": "local"}'
# --- Create RESTful API Request
$url = "$saltServerAddress/run"
$Params = @{
Method = "Post"
Uri = $url
Body = $Body
ContentType = "application/json"
}
$fetch = Invoke-RestMethod @Params -SkipCertificateCheck
# --- Showing different ways to show the return data
#$fetch
$fetch.return
$fetch.return.$minionName
|
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
|
# --- PowerShell Code ---
$minionName = '2019DC'
$saltServerAddress = 'https://192.168.86.110:8000'
$userName = 'root'
$password = 'HackMe!'
$fun = 'disk.usage' # -salt function
$fetch = ''
# --- Set the json body
$body = '{ "username": "' + $userName + '","password": "' + $password + '","eauth": "pam","tgt": "' + $minionName + '","fun": "' + $fun + '","client": "local"}'
# --- Create RESTful API Request
$url = "$saltServerAddress/run"
$Params = @{
Method = "Post"
Uri = $url
Body = $Body
ContentType = "application/json"
}
$fetch = Invoke-RestMethod @Params -SkipCertificateCheck
# --- Showing different ways to show the return data
$fetch.return
$fetch.return.$minionName
$fetch.return.$minionName.'C:\'
$fetch.return.$minionName.'C:\'.capacity
|
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
|
# --- PowerShell Code ---
$minionName = '2019DC'
$saltServerAddress = 'https://192.168.86.110:8000'
$userName = 'root'
$password = 'HackMe!'
$fun = 'service.status' # -salt function
$arg = 'spooler' # -Service Name
$fetch = ''
# --- Set the json body
$body = '{"username": "' + $userName + '","password": "' + $password + '","eauth": "pam","tgt": "' + $minionName + '","fun": "' + $fun + '","arg": "' + $arg + '","client": "local"}'
# --- Create RESTful API Request
$url = "$saltServerAddress/run"
$Params = @{
Method = "Post"
Uri = $url
Body = $Body
ContentType = "application/json"
}
$fetch = Invoke-RestMethod @Params -SkipCertificateCheck
# --- Service Status | True = Running | False = Stopped
$fetch.return.$minionName
|
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
|
# --- PowerShell Code ---
$minionName = '2019DC'
$saltServerAddress = 'https://192.168.86.110:8000'
$userName = 'root'
$password = 'HackMe!'
$fun = 'service.stop' # -salt function
$arg = 'spooler' # -Service Name
$fetch = ''
# --- Set the json body
$body = '{"username": "' + $userName + '","password": "' + $password + '","eauth": "pam","tgt": "' + $minionName + '","fun": "' + $fun + '","arg": "' + $arg + '","client": "local"}'
# --- Create RESTful API Request
$url = "$saltServerAddress/run"
$Params = @{
Method = "Post"
Uri = $url
Body = $Body
ContentType = "application/json"
}
$fetch = Invoke-RestMethod @Params -SkipCertificateCheck
# --- Service Stopped | True = Stopped | False = Not Stopped
$fetch.return.$minionName
|
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
|
# --- PowerShell Code ---
$minionName = '2019DC'
$saltServerAddress = 'https://192.168.86.110:8000'
$userName = 'root'
$password = 'HackMe!'
$fun = 'service.disable' # -salt function
$arg = 'spooler' # -Service Name
$fetch = ''
# --- Set the json body
$body = '{"username": "' + $userName + '","password": "' + $password + '","eauth": "pam","tgt": "' + $minionName + '","fun": "' + $fun + '","arg": "' + $arg + '","client": "local"}'
# --- Create RESTful API Request
$url = "$saltServerAddress/run"
$Params = @{
Method = "Post"
Uri = $url
Body = $Body
ContentType = "application/json"
}
$fetch = Invoke-RestMethod @Params -SkipCertificateCheck
# --- Service Disabled | True = Disabled | False = Not Disabled
$fetch.return.$minionName
|
- I hope the code was helpful to get started.
Lessons Learned:
- OOTB (Out of the Box) a SaltStack Config Server is NOT setup to use CherryPY to use api calls.
- Using PowerShell Invoke-RestMethod is a great way to automate SaltStack Config.
- The more I use salt with Windows Servers the more I like how it works. So fast. Many different ways to do automation | configuration Management.
- To get the proper args for a salt function I always test from the CLI.
- SaltStack REST_CHERRYPY Documentation
- SaltStack EXTERNAL AUTHENTICATION SYSTEM Documentation
Salt Links I found to be very helpful:
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.