JFrog Artifactory Terraform Provider Gains Xray Functionality

A few months ago, I was asked if I wanted to develop an open-source Terraform provider. Eleanor Saitta, principal at Systems Structure Ltd, had a client who was setting up JFrog Xray across their Github repositories but didn’t want to configure each repository by hand. As an SRE who enjoys working on projects that automate away those sorts of pain points (and someone who works extensively with Terraform during their day job), this sounded like an interesting project to work on.

Xray performs software composition analysis of a variety of software artifacts like libraries or built Docker containers. It’s triggered any time a change happens to a repository managed by Artifactory, JFrog’s universal repository management tool. Let’s use the example of an organization that uses microservices with Docker, and that has dozens or even hundreds of code repositories creating multiple Docker artifacts per day. Xray could be configured to analyze those Docker image artifacts whenever new ones are created. If you have many microservices, each with their own dependencies, being able to automate something like Xray’s setup would be a huge time-saver. Automation also lets the team guarantee that the same policies are consistently applied everywhere.

I was able to use the JFrog Xray REST API to create a Terraform provider that would allow users to use Terraform to create watches — Xray’s term for monitoring builds or repositories whenever changes occur in them — as well as policies, which define what rules should be applied by Xray. These two Terraform resources do their best work together, where a policy is created and then applied to a given watch. 

Here’s an example of what this would look like in Terraform code:

# Required for Terraform 0.13 and up (https://www.terraform.io/upgrade-guides/0-13.html)
terraform {
  required_providers {
	artifactory = {
  	  source  = "registry.terraform.io/jfrog/artifactory"
  	  version = "2.2.7"
	}
  }
}

# For example use - in real-world use don’t store your passwords in plaintext
provider "artifactory" {
  url      = "YOUR_ARTIFACTORY_URL"
  username = "username"
  password = "password"
}

resource "artifactory_xray_policy" "license-compliance" {
  name        = "license-compliance"
  description = "Ensure nobody uses licenses that the legal team says we shouldn’t."
  type        = "license"

  rules {
	name = "license-rule"
	priority = 1
	criteria {
      	     banned_licenses = ["AGPLv3", "sleepycat"]
	}
	actions {
    	  fail_build = true
    	  mails = ["compliance@example.com"]
    	  block_download {
             unscanned = true
             active = true
    	  }
	}
  }
}

# Create a new Xray watch for all repositories and assign the policy
resource "artifactory_xray_watch" "watch-all-repos-license-policy" {
  name  = "watch-all-repositories"
  description = "Make sure all our repos get the license-compliance policy applied."
  resources {
	type = "all-repos"
	name = "All Repositories"
  }
  assigned_policies {
	name = artifactory_xray_policy.license-compliance.name
	type = "license"
  }
}

In this example, the legal team has determined that some particular software licenses might put the hypothetical organization at legal risk if they were to be used in any of the organization’s software. This code will set up an Xray watch to scan all repositories, and connects a policy that will fail the build and email the compliance team if someone tries to use one of those disallowed licenses anywhere in their code or dependencies.

While I had originally written the code as its own separate provider, the team at JFrog graciously volunteered to adopt it so they could maintain all their Terraform provider code going forward, so I worked with the team to get this functionality added into the existing JFrog Artifactory Terraform provider. This provider now allows users to manage their Artifactory and Xray resources with one provider. 

This combination allows teams to do things like configure proxying of external package repositories through Artifactory (ensuring libraries are pulled consistently and centrally-archived) while also checking for and blocking known-vulnerable packages, all in the same piece of Terraform code. At the moment, this integration works only with local Artifactory repositories, but the team is working on adding this functionality to remote repositories in the future. For example:

# Required for Terraform 0.13 and up (https://www.terraform.io/upgrade-guides/0-13.html)
terraform {
  required_providers {
	artifactory = {
  	  source  = "registry.terraform.io/jfrog/artifactory"
  	  version = "2.2.7"
	}
  }
}

# For example use - in real-world use don’t store your passwords in plaintext
provider "artifactory" {
  url      = "YOUR_ARTIFACTORY_URL"
  username = "username"
  password = "password"
}

resource "artifactory_local_repository" "npm-local" {
  key         	 = "npm-local"
  package_type	 = "npm"
  repo_layout_ref = "npm-default"
  xray_index  	 = true
  # The xray_index option, though currently undocumented, is what allows xray to be able to watch a specified local repository
}

resource "artifactory_xray_policy" "test" {
  name        = "test-policy-name-severity"
  description = "test policy description"
  type        = "security"
  rules {
	name = "rule-name"
	priority = 1
	criteria {
  	  min_severity = "High"
	}
	actions {
  	  block_download {
    	    unscanned = true
    	    active = true
  	  }
	}
  }
}

resource "artifactory_xray_watch" "test" {
  name  = "watch-npm-local-repo"
  description = "apply a severity-based policy to the npm local repo"
  resources {
	type = "repository"
	name = artifactory_local_repository.npm-local.key
	bin_mgr_id = "example-com-artifactory-instance"
	filters {
  	  type = "package-type"
  	  value = "npm"
	}
  }
  assigned_policies {
	name = artifactory_xray_policy.test.name
	type = "security"
  }

  depends_on = [
	artifactory_local_repository.npm-local,
  ]
}

This combined functionality should allow teams to get much more value from their existing Artifactory- and Xray-based workflows, and to deploy them at a scale that wasn’t previously practical, and I’m excited to get to help announce its release.