Using golang to create jenkins credentials

How do I create jenkins credentials using a golang script?
In this blog article, we are going use GoJenkins library to create jenkins Username, String and SSH credentials.

The Jenkins credentials plugin provides a default internal credentials store, which can be used to store high value or privileged credentials, such as Amazon bucket deployment username/password combinations and GitHub user tokens.

The jenkins credentials binding plugin – https://plugins.jenkins.io/credentials-binding – Allows credentials to be bound to environment variables for use from miscellaneous build steps.

Prerequisites

To follow along, ensure:

  • you have a working jenkins with admin username and password
  • You have golang set up in your local machine
  • You know basics of golang and Jenkins

Table of content

  1. Install the Credential and Credential Binding Plugin
  2. Create directory structure and initialize module
  3. Adding imports

1. Install the Credential and Credential Binding Plugin

Go to Jenkins > Plugins Manager and select Credentials and Credential Bindings Plugin. Then click install.

1. Create directory structure and initialize module

Create a directory.

mkdir jenkins-credentials

Switch to the directory, Initialize a module in that directory using the go mod init command:

➜ go mod init jenkins-credentials
go: creating new go.mod: module jenkins-credentials
go: to add module requirements and sums:
    go mod tidy

2. Adding imports

We need to instantiate the package and add imports. Let’s do it with this code block:

package main

import (
    "context"
    "log"

    "github.com/bndr/gojenkins"
)

In the above code snippet, the most important import is the gojenkins one – github.com/bndr/gojenkins. This is the module that we will use manage jenkins credentials.

2. Define constants

The next bit is to define constants. We need these to instantiate things like jenkins url, username and password that do not change. These will be used in the script.

Define the constants:

const (
    jenkinsUrl  = "https://jenkins.citizix.dev/"
    jenkinsUser = "admin"
    jenkinsPass = "Sup3rSecu4"
    domain      = "_"
    scope       = "GLOBAL"
)

3. Initialize connection to jenkins

Next is the main function. We need to connect to jenkins. We can also provide a CA certificate if server is using self-signed certificate. Checkout this code block:

    ctx := context.Background()
    jenkins := gojenkins.CreateJenkins(nil, jenkinsUrl, jenkinsUser, jenkinsPass)
    // Provide CA certificate if server is using self-signed certificate
    // caCert, _ := ioutil.ReadFile("/tmp/ca.crt")
    // jenkins.Requester.CACert = caCert

    _, err := jenkins.Init(ctx)
    if err != nil {
        log.Fatalf("Jenkins init failed: %v", err)
    }

4. Define credentials structs

Next, for each credential that we are defining we need a struct. gojenkins already defines structs that we can just pass values.

Username credentials

These are credentials that have a key and a value. Let’s instantiate a struct that an be expanded:

    usernameCreds := []*gojenkins.UsernameCredentials{
        &gojenkins.UsernameCredentials{
            ID:          "app-one-mysql-credentials",
            Description: "Username and Password Credentials used bu App one",
            Scope:       scope,
            Username:    "app_one_user",
            Password:    "Sup3rS3cret",
        },
    }

Use this block to create the credentials:

    for i, cred := range usernameCreds {
        log.Printf("Adding username cred %v: %v to %v", i, cred.ID, jenkinsUrl)
        err = cm.Add(ctx, domain, cred)
        if err != nil {
            log.Fatalf("failed to add username credential =[%v]: %v", err, cred.ID)
        }
    }

String credentials

These are strings that are credentials. Let us define with:

secretCreds := []*gojenkins.StringCredentials{
        &gojenkins.StringCredentials{
            ID:          "dev-appone-postgres-url",
            Description: "Dev appone postgres url",
            Scope:       scope,
            Secret:      "postgres://dev_appone:SuperSecret@192.168.10.20:5432/dev_appone?sslmode=disable",
        },
    }

Now create:

    for i, cred := range secretCreds {
        log.Printf("Adding secret cred %v: %v to %v", i, cred.ID, jenkinsUrl)
        err = cm.Add(ctx, domain, cred)
        if err != nil {
            log.Fatalf("failed to add secret credential =[%v]: %v", err, cred.ID)
        }
    }

SSH credentials

This is private ssh key creds. To define, use this snippet:

    sshCreds := []*gojenkins.SSHCredentials{
        &gojenkins.SSHCredentials{
            ID:               "citizix-ssh-key",
            Description:      "Citizix proivate ssh key",
            Scope:            scope,
            Username:         "spay",
            PrivateKeySource: "~/.ssh/id_citizix",
        },
    }

Now let’s create:

    for i, cred := range sshCreds {
        log.Printf("Adding ssh cred %v: %v to %v", i, cred.ID, jenkinsUrl)
        err = cm.Add(ctx, domain, cred)
        if err != nil {
            log.Fatalf("failed to add ssh credential =[%v]: %v", err, cred.ID)
        }
    }

Getting a list of existing credentials

To get all the credentials, use this:

    existingCreds, err := cm.List(ctx, domain)
    if err != nil {
        log.Fatalf("could not get creds: %v", err)
    }

    log.Printf("existing credentials: %+v", existingCreds)

5. Whole code

This is the whole code:

package main

import (
    "context"
    "log"

    "github.com/bndr/gojenkins"
)

const (
    jenkinsUrl  = "https://jenkins.citizix.dev/"
    jenkinsUser = "admin"
    jenkinsPass = "Sup3rSecu4"
    domain      = "_"
    scope       = "GLOBAL"
)

func main() {
    ctx := context.Background()
    jenkins := gojenkins.CreateJenkins(nil, jenkinsUrl, jenkinsUser, jenkinsPass)
    // Provide CA certificate if server is using self-signed certificate
    // caCert, _ := ioutil.ReadFile("/tmp/ca.crt")
    // jenkins.Requester.CACert = caCert

    _, err := jenkins.Init(ctx)
    if err != nil {
        log.Fatalf("Jenkins init failed: %v", err)
    }

    cm := gojenkins.CredentialsManager{J: jenkins}
    usernameCreds := []*gojenkins.UsernameCredentials{
        &gojenkins.UsernameCredentials{
            ID:          "app-one-mysql-credentials",
            Description: "Username and Password Credentials used bu App one",
            Scope:       scope,
            Username:    "app_one_user",
            Password:    "Sup3rS3cret",
        },
    }

    sshCreds := []*gojenkins.SSHCredentials{
        &gojenkins.SSHCredentials{
            ID:               "citizix-ssh-key",
            Description:      "Citizix proivate ssh key",
            Scope:            scope,
            Username:         "spay",
            PrivateKeySource: "~/.ssh/id_citizix",
        },
    }

    secretCreds := []*gojenkins.StringCredentials{
        &gojenkins.StringCredentials{
            ID:          "dev-appone-postgres-url",
            Description: "Dev appone postgres url",
            Scope:       scope,
            Secret:      "postgres://dev_appone:SuperSecret@192.168.10.20:5432/dev_appone?sslmode=disable",
        },
    }

    for i, cred := range usernameCreds {
        log.Printf("Adding username cred %v: %v to %v", i, cred.ID, jenkinsUrl)
        err = cm.Add(ctx, domain, cred)
        if err != nil {
            log.Fatalf("failed to add username credential =[%v]: %v", err, cred.ID)
        }
    }

    for i, cred := range secretCreds {
        log.Printf("Adding secret cred %v: %v to %v", i, cred.ID, jenkinsUrl)
        err = cm.Add(ctx, domain, cred)
        if err != nil {
            log.Fatalf("failed to add secret credential =[%v]: %v", err, cred.ID)
        }
    }

    for i, cred := range sshCreds {
        log.Printf("Adding ssh cred %v: %v to %v", i, cred.ID, jenkinsUrl)
        err = cm.Add(ctx, domain, cred)
        if err != nil {
            log.Fatalf("failed to add ssh credential =[%v]: %v", err, cred.ID)
        }
    }

    // Bonus, to get a list of added credentials use this:
    existingCreds, err := cm.List(ctx, domain)
    if err != nil {
        log.Fatalf("could not get creds: %v", err)
    }

    log.Printf("existing credentials: %+v", existingCreds)
}

Building and testing the script

Go mod tidy:

➜ go mod tidy
go: finding module for package github.com/bndr/gojenkins
go: found github.com/bndr/gojenkins in github.com/bndr/gojenkins v1.1.0

Build:

➜ go build -o jenkins-creds

Test Execution:

➜ ./jenkins-creds
2021/10/07 22:04:58 creds: [github-token docker-registry aws-iac-access spay-ssh-key spay-mssql-sa-pw]
2021/10/07 22:04:58 Adding username cred 0: app-one-mysql-credentials to http://jenkins.citizix.com/
2021/10/07 22:05:00 Adding secret cred 0: dev-appone-postgres-url to http://jenkins.citizix.com/
2021/10/07 22:05:01 Adding ssh cred 0: citizix-ssh-key to http://jenkins.citizix.com/

Conclusion

We managed to automate the process of creating jenkins credentials.

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy