terraform-provider-xray icon indicating copy to clipboard operation
terraform-provider-xray copied to clipboard

Terraform provider to manage JFrog Xray

Terraform Provider Xray

Actions Status Go Report Card

To use this provider in your Terraform module, follow the documentation here.

Xray general information

Xray API Documentation

Quick Start

Create a new Terraform file with xray resource (and artifactory resource as well):

HCL 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.9.1"
    }
    
    project = {
      source  = "registry.terraform.io/jfrog/project"
      version = "1.0.1"
    }
    
    xray = {
      source  = "registry.terraform.io/jfrog/xray"
      version = "0.0.1"
    }
  }
}
provider "artifactory" {
  // supply ARTIFACTORY_USERNAME, ARTIFACTORY_PASSWORD and ARTIFACTORY_URL as env vars
}

provider "project" {
  // supply PROJECT_URL, PROJECT_ACCESS_TOKEN as env vars
  url = "${var.project_url}"
  access_token = "${var.project_access_token}"
}

provider "xray" {
// Also user can supply the following env vars:
// JFROG_URL or XRAY_URL
// XRAY_ACCESS_TOKEN or JFROG_ACCESS_TOKEN
}

resource "random_id" "randid" {
  byte_length = 2
}

resource "artifactory_user" "user1" {
  name     = "user1"
  email    = "[email protected]"
  groups   = ["readers"]
  password = "Passw0rd!"
}

resource "artifactory_local_docker_v2_repository" "docker-local" {
  key             = "docker-local"
  description     = "hello docker-local"
  tag_retention   = 3
  max_unique_tags = 5
  xray_index = true # must be set to true to be able to assign the watch to the repo
}

resource "artifactory_local_gradle_repository" "local-gradle-repo" {
  key                             = "local-gradle-repo-basic"
  checksum_policy_type            = "client-checksums"
  snapshot_version_behavior       = "unique"
  max_unique_snapshots            = 10
  handle_releases                 = true
  handle_snapshots                = true
  suppress_pom_consistency_checks = true
  xray_index = true # must be set to true to be able to assign the watch to the repo
}

resource "project" "myproject" {
  key          = "test"
  display_name = "My Project"
  description  = "My Project"
  admin_privileges {
    manage_members   = true
    manage_resources = true
    index_resources  = true
  }
}

resource "project" "myproject1" {
  key          = "test1"
  display_name = "My Project"
  description  = "My Project"
  admin_privileges {
    manage_members   = true
    manage_resources = true
    index_resources  = true
  }
}


resource "xray_security_policy" "security1" {
  name        = "test-security-policy-severity-${random_id.randid.dec}"
  description = "Security policy description"
  type        = "security"

  rule {
    name     = "rule-name-severity"
    priority = 1

    criteria {
      min_severity = "High"
    }

    actions {
      webhooks = []
      mails    = ["[email protected]"]
      block_release_bundle_distribution  = true
      fail_build                         = true
      notify_watch_recipients            = true
      notify_deployer                    = true
      create_ticket_enabled              = false // set to true only if Jira integration is enabled
      build_failure_grace_period_in_days = 5     // use only if fail_build is enabled

      block_download {
        unscanned = true
        active    = true
      }
    }
  }
}

resource "xray_security_policy" "security2" {
  name        = "test-security-policy-cvss-${random_id.randid.dec}"
  description = "Security policy description"
  type        = "security"

  rule {
    name     = "rule-name-cvss"
    priority = 1

    criteria {

      cvss_range {
        from = 1.5
        to   = 5.3
      }
    }

    actions {
      webhooks = []
      mails    = ["[email protected]"]
      block_release_bundle_distribution  = true
      fail_build                         = true
      notify_watch_recipients            = true
      notify_deployer                    = true
      create_ticket_enabled              = false // set to true only if Jira integration is enabled
      build_failure_grace_period_in_days = 5     // use only if fail_build is enabled

      block_download {
        unscanned = true
        active    = true
      }
    }
  }
}

resource "xray_license_policy" "license1" {
  name        = "test-license-policy-allowed-${random_id.randid.dec}"
  description = "License policy, allow certain licenses"
  type        = "license"

  rule {
    name     = "License_rule"
    priority = 1

    criteria {
      allowed_licenses         = ["Apache-1.0", "Apache-2.0"]
      allow_unknown            = false
      multi_license_permissive = true
    }

    actions {
      webhooks = []
      mails    = ["[email protected]"]
      block_release_bundle_distribution  = false
      fail_build                         = true
      notify_watch_recipients            = true
      notify_deployer                    = true
      create_ticket_enabled              = false // set to true only if Jira integration is enabled
      custom_severity                    = "High"
      build_failure_grace_period_in_days = 5 // use only if fail_build is enabled

      block_download {
        unscanned = true
        active    = true
      }
    }
  }
}

resource "xray_license_policy" "license2" {
  name        = "test-license-policy-banned-${random_id.randid.dec}"
  description = "License policy, block certain licenses"
  type        = "license"

  rule {
    name     = "License_rule"
    priority = 1

    criteria {
      banned_licenses          = ["Apache-1.1", "APAFML"]
      allow_unknown            = false
      multi_license_permissive = false
    }

    actions {
      webhooks = []
      mails    = ["[email protected]"]
      block_release_bundle_distribution  = false
      fail_build                         = true
      notify_watch_recipients            = true
      notify_deployer                    = true
      create_ticket_enabled              = false // set to true only if Jira integration is enabled
      custom_severity                    = "Medium"
      build_failure_grace_period_in_days = 5 // use only if fail_build is enabled

      block_download {
        unscanned = true
        active    = true
      }
    }
  }
}

resource "xray_watch" "all-repos" {
  name        = "all-repos-watch-${random_id.randid.dec}"
  description = "Watch for all repositories, matching the filter"
  active      = true

  watch_resource {
    type = "all-repos"

    filter {
      type  = "regex"
      value = ".*"
    }
  }

  assigned_policy {
    name = xray_security_policy.security1.name
    type = "security"
  }

  assigned_policy {
    name = xray_license_policy.license1.name
    type = "license"
  }
  watch_recipients = ["[email protected]", "[email protected]"]
}

resource "xray_watch" "repository" {
  name        = "repository-watch-${random_id.randid.dec}"
  description = "Watch a single repo or a list of repositories"
  active      = true

  watch_resource {
    type       = "repository"
    bin_mgr_id = "default"
    name       = artifactory_local_docker_v2_repository.docker-local.key

    filter {
      type  = "regex"
      value = ".*"
    }
  }

  watch_resource {
    type       = "repository"
    bin_mgr_id = "default"
    name       = artifactory_local_gradle_repository.local-gradle-repo.key

    filter {
      type  = "package-type"
      value = "Docker"
    }
  }

  assigned_policy {
    name = xray_security_policy.security1.name
    type = "security"
  }

  assigned_policy {
    name = xray_license_policy.license1.name
    type = "license"
  }

  watch_recipients = ["[email protected]", "[email protected]"]
}

resource "xray_watch" "build" {
  name        = "build-watch-${random_id.randid.dec}"
  description = "Watch a single build or a list of builds"
  active      = true

  watch_resource {
    type       = "build"
    bin_mgr_id = "default"
    name       = "your-build-name"
  }

  watch_resource {
    type       = "build"
    bin_mgr_id = "default"
    name       = "your-other-build-name"
  }

  assigned_policy {
    name = xray_security_policy.security1.name
    type = "security"
  }
  assigned_policy {
    name = xray_license_policy.license1.name
    type = "license"
  }

  watch_recipients = ["[email protected]", "[email protected]"]
}

resource "xray_watch" "all-projects" {
  name        = "all-projects-watch-${random_id.randid.dec}"
  description = "Watch all the projects"
  active      = true

  watch_resource {
    type       	= "all-projects"
    bin_mgr_id  = "default"
  }

  assigned_policy {
    name = xray_security_policy.security1.name
    type = "security"
  }

  assigned_policy {
    name = xray_license_policy.license1.name
    type = "license"
  }

  watch_recipients = ["[email protected]", "[email protected]"]
}

resource "xray_watch" "project" {
  name        = "project-watch-${random_id.randid.dec}"
  description = "Watch selected projects"
  active      = true

  watch_resource {
    type       	= "project"
    name        = project.myproject.key
  }
  watch_resource {
    type       	= "project"
    name        = project.myproject1.key
  }

  assigned_policy {
    name = xray_security_policy.security1.name
    type = "security"
  }

  assigned_policy {
    name = xray_license_policy.license1.name
    type = "license"
  }

  watch_recipients = ["[email protected]", "[email protected]"]
}

License requirements:

This provider requires Xray to be added to your Artifactory installation. Xray requires minimum Pro Team license (Public Marketplace version or SaaS) or Pro X license (Self-hosted). See the details here You can determine which license you have by accessing the following Artifactory URL ${host}/artifactory/api/system/licenses/

Limitations of functionality

Currently, Xray provider is not supporting JSON objects in the Watch filter value. We are working on adding this functionality.

Build the Provider

Simply run make install - this will compile the provider and install it to ~/.terraform.d. When running this, it will take the current tag and bump it 1 minor version. It does not actually create a new tag (that is make release). If you wish to use the locally installed provider, make sure your TF script refers to the new version number.

Requirements:

  • Terraform 0.13
  • Go 1.15+ (to build the provider plugin)

Building on macOS

This provider uses GNU sed as part of the build toolchain, in both Linux and macOS. This provides consistency across OSes.

If you are building this on macOS, you have two options:

Using gnu-sed

After installing with brew, get the GNU sed information:

$ brew info gnu-sed

You should see something like:

GNU "sed" has been installed as "gsed".
If you need to use it as "sed", you can add a "gnubin" directory
to your PATH from your bashrc like:

     PATH="$(brew --prefix)/opt/gnu-sed/libexec/gnubin:$PATH"

Add the gnubin directory to your .bashrc or .zshrc per instruction so that sed command uses gnu-sed.

Testing

Since JFrog Xray is an addon for Artifactory, you will need a running instance of the JFrog platform (Artifactory and Xray). However, there is no currently supported dockerized, local version. The fastest way to install Artifactory and Xray as a self-hosted installation is to use Platform Helm chart. Free 30 days trial version is available here If you want to test on SaaS instance - 30 day trial can be freely obtained and will allow local development.

Then, you have to set some environment variables as this is how the acceptance tests pick up their config:

JFROG_URL=http://localhost:8081
XRAY_ACCESS_TOKEN=your-admin-key
TF_ACC=true

a crucial, and very much hidden, env var to set is TF_ACC=true - you can literally set TF_ACC to anything you want, so long as it's set. The acceptance tests use terraform testing libraries that, if this flag isn't set, will skip all tests.

XRAY_ACCESS_TOKEN can be generated in the UI. Go to Settings -> Identity and Access -> Access Tokens -> Generate Admin Token

You can then run the tests as make acceptance. You can check what it's doing on the background in the GNUmakefile in the project.

We've found that it's very convenient to use Charles proxy to see the payload, generated by Terraform Provider during the testing process. You can also use any other network packet reader, like Wireshark and so on.

Registry documentation generation

All the documentation in the project is generated by tfplugindocs. If you make any changes to the resource schemas, you will need to re-generate documentation. Install tfplugindocs, then run:

$ make doc

Versioning

In general, this project follows semver as closely as we can for tagging releases of the package. We've adopted the following versioning policy:

  • We increment the major version with any incompatible change to functionality, including changes to the exported Go API surface or behavior of the API.
  • We increment the minor version with any backwards-compatible changes to functionality.
  • We increment the patch version with any backwards-compatible bug fixes.

Contributors

Pull requests, issues and comments are welcomed. For pull requests:

  • Add tests for new features and bug fixes
  • Follow the existing style
  • Separate unrelated changes into multiple pull requests

See the existing issues for things to start contributing.

For bigger changes, make sure you start a discussion first by creating an issue and explaining the intended change.

JFrog requires contributors to sign a Contributor License Agreement, known as a CLA. This serves as a record stating that the contributor is entitled to contribute the code/documentation/translation to the project and is willing to have it used in distributions and derivative works (or is willing to transfer ownership).

License

Copyright (c) 2021 JFrog.

Apache 2.0 licensed, see LICENSE file.