> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/plugin-step.md).

# Plugin step

Run predefined tasks in a Continuous Delivery stage using plugins written in any programming language

Plugins perform predefined tasks. They are templated scripts that can be written in any programming language.

You can use plugins for anything that can be scripted. For example, you can use plugins that:

* Scrape commit information from a Git repository.
* Extract Jira issue numbers from Git commit messages.
* Print an extended build history.
* Create a file, write to it, and store it remotely.

***

## What you will learn from this topic

* How to [add a Plugin step](#add-the-plugin-step) in a containerized step group
* How to [create a custom plugin](#creating-custom-plugins), [test it locally](#test-plugins-locally), and [distribute the image](#distribute-plugins)
* How to [set the container registry, image, and plugin settings](#container-registry-and-image)
* How to [use output variables and output secrets](#output-variables) from the Plugin step in a later step

***

## Before you begin

* **Containerized step group**: The Plugin step runs in a containerized step group. Go to [Containerize step groups](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/containerized-step-groups.md) to enable container-based execution.
* **Plugin image**: You need a plugin image from the [Drone Plugins Marketplace](https://plugins.drone.io/), the [GitHub Actions Marketplace](https://github.com/marketplace?type=actions), or a Docker registry where you published your own plugin.

***

## Supported plugins <a href="#supported-plugins" id="supported-plugins"></a>

You can build your own plugins or use one of the preexisting plugins from the [Drone Plugins Marketplace](https://plugins.drone.io/) and [GitHub Actions Marketplace](https://github.com/marketplace?type=actions).

{% hint style="warning" %}
**Bitrise Workflow Steps**

Bitrise Workflow Steps are not supported in the Continuous Delivery Plugin step.
{% endhint %}

***

## Add the Plugin step <a href="#add-the-plugin-step" id="add-the-plugin-step"></a>

The Plugin step uses a containerized step group. Go to [Containerize step groups](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/containerized-step-groups.md) to configure the group.

The following example runs the custom `git-clone-plugin` image described in [Create custom plugins](#creating-custom-plugins). The plugin script's `PLUGIN_PATH`, `PLUGIN_REPO_URL`, and `PLUGIN_BRANCH` variables become the `path`, `repo_url`, and `branch` **Settings** keys shown below, each without its `PLUGIN_` prefix.

{% tabs %}
{% tab title="YAML" %}

```yaml
- stepGroup:
    identifier: container_group
    name: container_group
    sharedPaths:
      - /harness
    steps:
      - step:
          type: Plugin
          name: Clone with plugin
          identifier: Clone_with_plugin
          spec:
            connectorRef: account.harnessImage
            image: my-docker-repo/git-clone-plugin
            settings:
              path: codebase
              repo_url: https://github.com/<account>/<repo>.git
              branch: main
    stepGroupInfra:
      type: KubernetesDirect
      spec:
        connectorRef: account.k8sconnector
```

{% endtab %}

{% tab title="UI" %}
Perform the following steps to add a Plugin step:

1. In your Harness Deploy stage, in **Execution**, select **Add Step**.
2. Select **Add Step Group**, and then enter a name for the step group.
3. Enable **Enable container based execution**, and then configure the Kubernetes infrastructure. Go to [Containerize step groups](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/containerized-step-groups.md) to configure those settings.
4. Inside the step group, select **Add Step**, and then select **Plugin**.
5. In **Container Registry**, select a connector that can pull the plugin image.
6. In **Image**, enter the plugin image and tag, for example `my-docker-repo/git-clone-plugin`.
7. In **Optional Configuration**, expand **Settings** and add each plugin variable as a key-value pair, for example `path: codebase`, `repo_url: https://github.com/<account>/<repo>.git`, and `branch: main`. You can also configure report paths and an entry point in this section.
   {% endtab %}
   {% endtabs %}

You can also use a plugin from the [Drone Plugins Marketplace](https://plugins.drone.io/). The following example posts a message with the Slack plugin. Replace the connector, webhook, and channel with your values.

```yaml
- step:
    type: Plugin
    name: Slack notify
    identifier: Slack_notify
    spec:
      connectorRef: account.harnessImage
      image: plugins/slack
      settings:
        webhook: <+secrets.getValue("slack_webhook")>
        channel: deployments
        template: Deployment finished for <+pipeline.name>
```

Add this step inside the same containerized step group as the other container steps in the stage.

***

## Create custom plugins <a href="#creating-custom-plugins" id="creating-custom-plugins"></a>

To create a plugin, prepare a script, create a Docker image to run the script, and then build and publish the plugin image to a Docker registry.

Perform the following steps to create a plugin:

1. Write the script that you want the plugin to run. You can write plugins in any programming language. For example, the following Bash script, called `clone.sh`, clones a Git repository and prints the latest commit information.

   ```bash
   #!/bin/sh
   set -xe

   # If path setting is not set, then use current directory
   path=${PLUGIN_PATH:-.}
   mkdir -p ${path}
   cd ${path}

   # Clones the public git repo and checkout to a branch
   git clone ${PLUGIN_REPO_URL} .
   git checkout ${PLUGIN_BRANCH}

   # Prints the last commit
   git log -1 --stat
   ```

   The example script includes variable inputs: `PLUGIN_PATH`, `PLUGIN_REPO_URL`, and `PLUGIN_BRANCH`. Variables in plugin scripts become the plugin's **Settings** when you add the Plugin step to your Deploy stage.
2. Create a Docker image to run your script by specifying the script as the [ENTRYPOINT in a Dockerfile](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#entrypoint). For example, the following Dockerfile runs the `clone.sh` script:

   ```dockerfile
   FROM alpine/git

   # Copies the clone script to the Docker image
   COPY clone.sh /usr/local/bin/

   # Makes the clone script executable
   RUN chmod +x /usr/local/bin/clone.sh

   ENTRYPOINT [ "/usr/local/bin/clone.sh" ]
   ```
3. Using the method of your choice, build and publish the plugin image to a Docker registry. Take note of the Docker repo and image name, such as `my-docker-repo/git-clone-plugin`. You need it when you add the Plugin step to your Deploy stage.

   ```bash
   docker build -t my-docker-repo/git-clone-plugin .
   docker push my-docker-repo/git-clone-plugin
   ```

### Test plugins locally <a href="#test-plugins-locally" id="test-plugins-locally"></a>

You can test your plugin in a local environment by running it as a Docker container. For example, the following Docker command runs the `clone.sh` plugin locally by supplying the required inputs (`PLUGIN_PATH`, `PLUGIN_REPO_URL`, and `PLUGIN_BRANCH`) and specifying the plugin's Docker repo and image (`my-docker-repo/git-clone-plugin`).

```bash
docker run --rm \
 -e PLUGIN_PATH=codebase \
 -e PLUGIN_REPO_URL=https://github.com/<some-account>/<some-repo>.git \
 -e PLUGIN_BRANCH=main \
 my-docker-repo/git-clone-plugin
```

### Distribute plugins <a href="#distribute-plugins" id="distribute-plugins"></a>

Plugins are distributed as Docker images.

If your plugin image is private, others in your organization can use your plugin in their Harness Continuous Integration pipelines by using a [Docker connector](https://developer.harness.io/docs/platform/connectors/cloud-providers/ref-cloud-providers/docker-registry-connector-settings-reference/) configured for the private registry where your plugin image is stored.

If your plugin image is public, you can share it with anyone. You can submit a pull request to the [drone-plugin-index repository](https://github.com/drone/drone-plugin-index) if you want your plugin to be considered for the [Drone Plugins Marketplace](https://plugins.drone.io/).

***

## Plugin step settings <a href="#plugin-step-settings" id="plugin-step-settings"></a>

The Plugin step has the following settings.

### Container Registry and Image <a href="#container-registry-and-image" id="container-registry-and-image"></a>

**Container Registry** is a Harness container registry connector that has access to Docker Hub. If you have created your own plugin, the connector must have access to the container registry where your plugin image is located.

The name of the plugin's Docker image. The image name should include the tag, or it defaults to the `latest` tag if unspecified. Go to [Docker build tags](https://docs.docker.com/engine/reference/commandline/build/#tag) to review image tags.

You can use any Docker image from any Docker registry, including Docker images from private registries.

### Privileged <a href="#privileged" id="privileged"></a>

Select this option to run the container with escalated privileges. This is the equivalent of running a container with the Docker `--privileged` flag.

### Settings <a href="#settings" id="settings"></a>

Specify plugin-specific settings according to the plugin's documentation.

### Output variables <a href="#output-variables" id="output-variables"></a>

{% hint style="warning" %}
**Not all plugins write output variables.**
{% endhint %}

Output variables are exposed values that can be used by other steps or stages in the pipeline. If the plugin writes output to the `DRONE_OUTPUT.env` file, you can use [expressions](https://developer.harness.io/docs/platform/variables-and-expressions/runtime-inputs#expressions) to reference output variables in other steps and stages in the pipeline.

For example, to write to the `DRONE_OUTPUT.env` file, the plugin must use a command such as the following:

```bash
echo "VAR_NAME=somevalue" >> $DRONE_OUTPUT
```

To reference the resulting output variable in another step in the same stage, use either of the following expressions:

```
<+steps.STEP_ID.output.outputVariables.VAR_NAME>
<+execution.steps.STEP_ID.output.outputVariables.VAR_NAME>
```

To reference an output variable in a stage other than the one where the output variable originated, use either of the following expressions:

```
<+stages.STAGE_ID.spec.execution.steps.STEP_ID.output.outputVariables.VAR_NAME>
<+pipeline.stages.STAGE_ID.spec.execution.steps.STEP_ID.output.outputVariables.VAR_NAME>
```

For each expression:

* Replace `STEP_ID` with the ID of the **Plugin** step.
* Replace `VAR_NAME` with the relevant variable name.
* In cross-stage references, replace `STAGE_ID` with the ID of the stage where the **Plugin** step exists.

If the step is within a step group, include the step group identifier in the expression, such as:

```
<+execution.steps.STEP_GROUP_ID.steps.STEP_ID.output.outputVariables.VAR_NAME>
<+pipeline.stages.STAGE_ID.spec.execution.steps.STEP_GROUP_ID.steps.STEP_ID.output.outputVariables.VAR_NAME>
```

**Output secrets**

The Plugin step can export output secrets, which can be used in subsequent steps or stages. Output secrets behave like output variables but are automatically masked in logs and handled securely.

{% hint style="warning" %}
**Export secrets from plugin code** To mask secrets, export them from within your plugin code using the `$HARNESS_OUTPUT_SECRET_FILE` environment variable. The `outputVariables` field in the step YAML does not mask secrets. Plugins ignore that field. Secret masking works only when secrets are written to `$HARNESS_OUTPUT_SECRET_FILE` inside the plugin.
{% endhint %}

To export an output secret from your plugin, write to the `$HARNESS_OUTPUT_SECRET_FILE` environment variable:

```bash
echo "SECRET_KEY=supersecretvalue" >> $HARNESS_OUTPUT_SECRET_FILE
```

For example, the [Harness OIDC plugin](https://github.com/harness-community/drone-aws-oidc/blob/main/plugin/plugin.go#L64) exports output secrets using this approach in Go:

```go
secretOutput := fmt.Sprintf("AWS_ACCESS_KEY_ID=%s\n", credentials.AccessKeyID)
f.WriteString(secretOutput)
```

Output secrets use the same expressions as output variables:

* **Same stage**: `<+steps.STEP_ID.output.outputVariables.SECRET_KEY>`
* **Cross-stage**: `<+stages.STAGE_ID.spec.execution.steps.STEP_ID.output.outputVariables.SECRET_KEY>`

Harness masks secret values in logs and handles them securely.

{% hint style="info" %}
**Feature flags**

To use output secrets, the following feature flags must be enabled for your account:

* **`CI_SKIP_NON_EXPRESSION_EVALUATION`**: Required for proper expression evaluation
* **`CI_ENABLE_OUTPUT_SECRETS`**: Enables output secret support
* **`CI_ENABLE_PLUGIN_OUTPUT_SECRETS`**: Required for Docker Runner only

To enable these flags, [contact Harness Support](mailto:support@harness.io).
{% endhint %}

### Image Pull Policy <a href="#image-pull-policy" id="image-pull-policy"></a>

Select an option to set the pull policy for the image:

* **Always**: The kubelet queries the container image registry to resolve the name to an image digest every time the kubelet launches a container. If the kubelet encounters an exact digest cached locally, it uses its cached image; otherwise, the kubelet downloads (pulls) the image with the resolved digest, and uses that image to launch the container.
* **If Not Present**: The image is pulled only if it is not already present locally.
* **Never**: The image is assumed to exist locally. No attempt is made to pull the image.

### Set Container Resources <a href="#set-container-resources" id="set-container-resources"></a>

Maximum resource limits for containers that clone the codebase at runtime. Go to [Resource units in Kubernetes](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes) to review resource units.

***

## Advanced settings <a href="#advanced-settings" id="advanced-settings"></a>

In **Advanced**, you can use the following options:

* [Conditional Execution](https://developer.harness.io/docs/platform/pipelines/step-skip-condition-settings)
* [Failure Strategy](https://developer.harness.io/docs/platform/pipelines/failure-handling/define-a-failure-strategy-on-stages-and-steps)
* [Looping Strategy](https://developer.harness.io/docs/platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism)
* [Policy Enforcement](https://developer.harness.io/docs/platform/governance/policy-as-code/harness-governance-overview)

***

## Next steps

* Go to [Containerize step groups](/continuous-delivery/use-continuous-delivery/cd-building-blocks/cd-steps/containerized-steps/containerized-step-groups.md) to run the Plugin step on your Kubernetes cluster.
* Go to [Docker Registry connector settings reference](https://developer.harness.io/docs/platform/connectors/cloud-providers/ref-cloud-providers/docker-registry-connector-settings-reference/) to connect a private registry that stores your plugin image.
* Go to [Conditional executions for stages and steps](https://developer.harness.io/docs/platform/pipelines/step-skip-condition-settings) to control when the Plugin step runs.
* Go to [Define failure strategies for stages and steps](https://developer.harness.io/docs/platform/pipelines/failure-handling/define-a-failure-strategy-on-stages-and-steps) to configure what happens if the plugin fails.

{% @harness-feedback/feedback %}
