Triggering a Jenkins job using API call and passing parameters

Jenkins is one of the important tool in DevOps and most of the time we would require to execute Jenkins job using remote REST API call. Jobs can either be parameterized or non parameterized. A Parameterized job will need certain input from user side for execution. Here we will discuss how to call both types of jobs using REST API.

We will discuss on following steps. Read more

  1. Setting Jenkins Job respond to REST API
  2. Finding out the API URL for the Job
  3. Handling Authentication
  4. Building REST API request
  5. How to trigger a non-parametrized Job.
  6. Triggering paremeterized job by sending values in URL
  7. Triggering parametrized Job by sending values in JSON file

Introduction to Jenkins API

[From Jenkins Documentation] “Jenkins is the market leading continuous integration system, originally created by Kohsuke Kawaguchi. This API makes Jenkins even easier to use by providing an easy to use conventional python interface.”

——————- advertisements ——————-  

———————————————————

Jenkins provides rich set of REST based APIs. 

Setting Jenkins Job to respond REST API

The REST API feature can be enabled per Job basis. To enable REST API trigger for a Job, Navigate to Your JobName ->Configure -> Build triggers TAB and Check on ‘Trigger build remotely’.

Find out the API URL for the Job

Once you enabled the check box to trigger build remotely , Jenkins will show you the URL to access the particular Job and gives an option to provide the API token for the build. Consider my Jenkins server URL is 10.10.10.100:8080 and my job name is ‘test-job’ , then the URL will be as follows

‘ http://10.10.10.100:8080/job/test-job/build?token=MyTestAPIToken’ -> For non parameterized build

‘ http://10.10.10.100:8080/job/test-job/buildWithParameters?token=MyTestAPIToken’ -> For parameterized build

——————- advertisements ——————-  

———————————————————

Handling Authentication

Jenkins using combination of user credential based authentication and API token authentication. We can build token for each build as shown above. In user credential authentication, you can either pass the usename+password or username+token . To access the token for your username, login with your user account , navigate to Manage Jenkins -> Manage Users ->Select User name -> Click Add New Token ->Give Token Name -> Click generate . This will display token for your user . Copy this token and save on safe place as this token can not be recovered in future.

Building REST API request

There are two steps involved in making a successful API request. The first step is to send authentication request and get the CRUMB variable. This crumb data required to be send as header on further API requests. Jenkins use this to prevent Cross Site Forgery. The second one include the actual job request where you will specify job name and parameters for the job. Following are the example for getting CRUMB data using CURL query

——————- advertisements ——————-  

———————————————————

Getting CRUMB data :

Format : crumb=$(curl -vvv -u “username:passsword” -s ‘http://jenkinsurl/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,”:”,//crumb)’)

Example using password :

crumb=$(curl -vvv -u “apiuser:[email protected]″ -s ‘http:// 10.10.10.100 :8080/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,”:”,//crumb)’)

Example using User Token :

crumb=$(curl -vvv -u “apiuser: 1104fbd9d00f4e9e0240365c20a358c2b7 ” -s ‘http:// 10.10.10.100 :8080/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,”:”,//crumb)’)

Triggering a Non-parameterized Job :

Triggering a non-parameterized job will be easy as there is no requirement of sending any additional data for the build. Below are the example for the API request. Assume we have got crumb data from the above step.

curl -H $crumb –user apiuser: 1104fbd9d00f4e9e0240365c20a358c2b7 -X POST http://10.10.10.100:8080/job/test-job/build?token=MyTestAPIToken

Where ‘test-job’ is name of my Job and ‘ MyTestAPIToken ‘ is the token keyword which i have set manually on Job configure page. Refer ‘Find out the API URL for the Job’ section above to understand for setting up token keyword.

——————- advertisements ——————-  

———————————————————

How to create Parameterized Job:

Consider a Jenkins Job where i am asking user inputs before executing Job, these type of jobs are called parameterized job. We can enable parameter request by checking ‘This Job Parameterized’ option under the general tab. Here i am enabling paramerized option for the job name ‘test-job’ and adding two string parameters ‘message1’ and ‘message2’.

Click on Job name -> Configure ->click on General Tab -> enable ‘This project is parameterized’ -> click add parameter and select ‘String Parameter’ from the drop down box. You can see multiple input parameter types which you can use as per your requirement.

On the upcoming window, enter the name as message1 and give the description. Click ‘add parameter’ and repeat the steps for ‘message2’ as well.

——————- advertisements ——————-  

———————————————————

Execute the Job by selecting your job name and clicking ‘Build with parameters’. This will prompt for user input before initiating the build. You can use the data provided by the user inside your build bash script or pipeline steps using ‘$message1’ format

Eg: echo Message 1 to the user $message1

echo Message 2 to the user $message2

 

Triggering Parametrized Job:

You can trigger parametrized job using API call and pass values. You can pass values in a URL encoded format or as part of a JSON file if you have many parameters. 

Passing parameters using URL encoded :

Step 1: Collect the CRUMB data 

——————- advertisements ——————-  

———————————————————

See the above section ‘Building REST API request’

Step 2: Send the parameters in URL

curl -v -H $crumb –user apiuser:apiuser -X POST ‘http://10.10.10.100:8080/job/testjob/buildWithParameters?token=MyTestAPIToken&message1=’hai’&message2=’hello’

Note : Message1 and message2 are the name of the parameter , please see above 

Passing parameters using URL encoded JSON format:

Step 1: Collect the crumb data 

See the above section ‘Building REST API request’

Step 2: Send the parameters in URL encoded Json format 

curl -v –user apiuser:apiuser -X POST http://10.10.10.100:8080/job/testjob/build –data token=MyTestAPIToken –data-urlencode json='{“parameter”:[{“name”:”message1″,”value”:”hai”},{“name”:”message2″,”value”:”hello”}]}’

 

Passing parameters using file:

Step 1: create a JSON file with all the parameters in the following format 

[[email protected] ~]# cat testapi.json
json={
“parameter”:[{“name”:”message1″,”value”:”hai”},{“name”:”message2″,”value”:”hello”}]
}
[[email protected] ~]#

Step 2: Collect the crumb data 

See the above section ‘Building REST API request’

Step 3: Send the api request by specifying file name . 

curl -v –user apiuser:apiuser -X POST http://10.10.10.100:8080/job/testjob/build –data “@testapi.json” -H “Accept: application/json”

 

Hope this helped you. Share your queries/feedback in the comments section below.

Azure cloud provisioning using Ansible

                Automating the IT Infrastructure is today’s one of major focus of all organizations. This reduces the cost and human workloads. When you make a plan to automating your infrastructure, it should start with provisioning of the resources, this makes managing the resources very easy. Many businesses have adopted cloud computing in their operations in the past years because of its flexibility and high sociability features. When you integrate the cloud infrastructure with today’s open source DevOps tools available in the market, this makes your daily life easier to handling huge environments.

I would rather suggest to go with Ansible as the configuration management tool because of its simplicity and straight forward operation features. This came in market late, but gained solid footing and adopted by many DevOps professionals because of its unique features. Ansible offers huge number of modules for managing the cloud operations for all major cloud providers like Azure AWS and GCP.

The Ansible playbooks which I refer below will help you to provisioning cloud resources in Azure environment, which create a Window VM and configure the VM to connect with Ansible host for any post provision activities, The playbook will perform the following tasks.

Read more

  1. Create the resource groups and Network infrastructure
  2. Provisioning of windows VMs
  3. Adding the new host to dynamic inventory for any post provision activities
  4. Enabling the PowerShell execution policy to connect to WinRM
  5. Installing a Firefox package using ansible on the newly created VM
The playbook contains 3 roles which will create Network infrastructure, provision a windows VMs and install the Firefox package on it.
——————- advertisements ——————-
———————————————————-
Let’s go through the main playbook first which includes 3 roles First 2 will run against the localhost which creates the Network infrastructure and Virtual machine respectively. As you can see the third role which install the Firefox package is running against a host group azure_vms which will be created dynamically after provisioning the server

Now let’s go through the first role common which creates the resource group and network infrastructure.

 

- name: Create a resource group
   azure_rm_resourcegroup:      
     name: "{{ rg_name }}"      
     location: "{{ location }}"      
     state: present 

- name: Create a virtual network   
  azure_rm_virtualnetwork:      
    name: "{{ vitual_network }}"      
    resource_group: "{{ rg_name }}"      
    address_prefixes_cidr:         
      - "{{ CIDR }}" 
- name: Create network windows base_security groups   
  azure_rm_securitygroup:     
    resource_group: "{{ rg_name }}"     
    name: windows_base     
    purge_rules: yes     
    rules:        
     - name: 'AllowRDP'          
       protocol: Tcp          
       source_address_prefix: 0.0.0.0/0          
       destination_port_range: 3389          
       access: Allow          
       priority: 100          
       direction: Inbound        
     - name: 'AllowWinRM'          
       protocol: Tcp          
       source_address_prefix: 0.0.0.0/0          
       destination_port_range: 5986          
       priority: 102          
       direction: Inbound        
     - name: 'DenyAll'          
       protocol: Tcp          
       source_address_prefix: 0.0.0.0/0          
       destination_port_range: 0-65535          
       priority: 103          
       direction: Inbound

- name: Create a Subnet and adding the windows_base security group in to it
  azure_rm_subnet:
    name: "{{ subnet }}"
    virtual_network_name: "{{ vitual_network }}"
    resource_group: "{{ rg_name }}"
    address_prefix_cidr: "{{ subnet_CIDR }}"
    security_group_name: windows_base 

——————- advertisements ——————-
———————————————————-

Here it’s creating a Resource group, virtual network and a security group which allow incoming RDP and WinRM traffics. You can either add the security group to the NIC card or to the subnet where we create the Virtual machine. Azure will create a NIC card and allocate to the VM in default if you are not giving any custom NIC cards while provisioning. Here I am not creating any custom NIC cards for the server instead attaching the security group with the subnet.

Let’s go through the second role which creates the Virtual machine.


- name: Create a VM    
  azure_rm_virtualmachine:      
    os_type: Windows      
    resource_group: "{{ rg_name }}"      
    virtual_network_name: "{{ virtual_network_name }}"      
    name: "{{ vm_name }}"      
    admin_username: "{{ admin_user }}"      
    admin_password: "{{ admin_passwd }}"      
    vm_size: Standard_F2s_v2      
    image:         
      offer: WindowsServer         
      publisher: MicrosoftWindowsServer         
      sku: '2016-Datacenter'         
      version: latest    
  register: output  

- name: Add new instance to the host group    
  add_host:       
    hostname: "{{ vm_name }}"       
    ansible_host: "{{ azure_vm.properties.networkProfile.networkInterfaces[0].properties.ipConfigurations[0]. properties.publicIPAddress.properties.ipAddress }}"       
    ansible_user: "{{ admin_user }}"       
    ansible_password: "{{ admin_passwd }}"       
    ansible_connection: winrm       
    ansible_port: 5986       
    ansible_winrm_server_cert_validation: ignore       
    ansible_winrm_transport: ssl 
    groupname: azure_vms    
  with_items: output.instances   

- name: create Azure vm extension to enable HTTPS WinRM listener     
  azure_rm_virtualmachine_extension:        
    name: winrm-extension        
    resource_group: "{{ rg_name }}"        
    virtual_machine_name: "{{ vm_name }}"        
    publisher: Microsoft.Compute        
    virtual_machine_extension_type: CustomScriptExtension        
    type_handler_version: 1.9        
    settings: '{"commandToExecute": "powershell.exe -ExecutionPolicy ByPass -   EncodedCommand {{winrm_enable_script}}"}'        
    auto_upgrade_minor_version: true     
  with_items: output.instances   

- name: wait for the WinRM port to come online     
  wait_for:        
    port: 5986        
    host: '{{azure_vm.properties.networkProfile.networkInterfaces[0].properties.ipConfigurations[ 0].properties.publicIPAddress.properties.ipAddress}}'        
    timeout: 600     
  with_items: output.instances
——————- advertisements ——————-
———————————————————-
As you can see in the second task in the role, the newly created server will be added to a host group azure_vms using the ansible add_host module. The third and 4 th task will enable HTTPS WinRM listener for ansible communication.

The third and final role in the playbook will install a Firefox browser in the newly provisioned VM using the ansible win_chocolatey module.

 - name: Install Firefox 
   win_chocolatey:
     name: firefox
     state: present

Here is the main playbook which calls all the 3 roles

---
- hosts: localhost
  gather_facts: yes
  roles:
   - common
   - vm

- hosts: azure_vms
  gather_facts: no
  roles:
   - install_firefox

Hope this post helped you. Please share your feedback/suggestions in the comments below.