Since Ansible 2.2 you can use binary applications as modules for Ansible. This means you can write modules in languages other than Python. The downside is that the modules aren’t integrated as well as if they were written in Python with Ansiballz. The binary modules only takes the filename as an argument which is a temporary file containing the JSON data of the modules parameters.
I took the boilerplate code that Ansible had here and created a small module to generate a random password. The only parameters for the module are the password length and the type of password (alpha, numeric, alphanumeric, full). If you don’t pass any parameters, it defaults to full (numbers, letters, and symbols) and 20 characters. The way it is written right now, it won’t show a change when it generates a password. To show a change just set Response.Changed
to true in your logic.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"os"
"time"
)
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const numbers = "0123456789"
const symbols = `!@#$%^&*()\/><}{[]`
type ModuleArgs struct {
Length int
Type string
}
type Response struct {
Password string `json:"password"`
Changed bool `json:"changed"`
Failed bool `json:"failed"`
}
func ExitJson(responseBody Response) {
returnResponse(responseBody)
}
func FailJson(responseBody Response) {
responseBody.Failed = true
returnResponse(responseBody)
}
func returnResponse(responseBody Response) {
var response []byte
var err error
response, err = json.Marshal(responseBody)
if err != nil {
response, _ = json.Marshal(Response{Password: "Invalid response object"})
}
fmt.Println(string(response))
if responseBody.Failed {
os.Exit(1)
} else {
os.Exit(0)
}
}
func GeneratePassword(l int, s string) string {
data := make([]byte, l)
var characters string
switch s {
case "full":
characters = letters + numbers + symbols
case "alpha":
characters = letters
case "numeric":
characters = numbers
case "alphanumeric":
characters = letters + numbers
}
n := len(characters)
for i := range data {
data[i] = characters[rand.Intn(n)]
}
return string(data)
}
func main() {
var response Response
rand.Seed(time.Now().UTC().UnixNano())
if len(os.Args) != 2 {
response.Password = "No argument file provided"
FailJson(response)
}
argsFile := os.Args[1]
text, err := ioutil.ReadFile(argsFile)
if err != nil {
response.Password = "Could not read configuration file: " + argsFile
FailJson(response)
}
var moduleArgs ModuleArgs
err = json.Unmarshal(text, &moduleArgs)
if err != nil {
response.Password = "Configuration file not valid JSON: " + argsFile
FailJson(response)
}
var length = 20
if moduleArgs.Length != 0 {
length = moduleArgs.Length
}
var style = "full"
if moduleArgs.Type != "" {
style = moduleArgs.Type
}
password := GeneratePassword(length, style)
response.Password = password
ExitJson(response)
}
From here you can just go build random.go
and put the executable in the libraries directory that you’ve defined. Then you just call the module like normal:
playbook:
- name: Generate Password
hosts: localhost
tasks:
- name: Generate random pass
random:
length: 125
type: full
register: password
- name: Print password
debug:
var: password.password
output:
PLAY [localhost] *****************************************************************************************************************************************************************************************************************************
TASK [Generate random pass] ******************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [Print password] *********************************************************************************************************************************************************************************************************************************
ok: [localhost] => {
"password.password": "ylXFw0gtZ[ch/gc>rO7fd/A}gC5{Oe\\o<6MAf5ByHyM7vPIr<)u44!<2eGR)%T&*h8DAYIm[>$^%YSI#$9ES$ma#lLqXm]StW!WA4/xmz3juDjm@JkYDn@dBK/0<a"
}
PLAY RECAP ***********************************************************************************************************************************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0