--- description: > Post-processors compress files, upload files, and perform other tasks that transform artifacts. Learn how to create customm post-processors that extend Packer. page_title: Create custom post-processors --- # Create custom post-processors Packer post-processors transform one artifact into another. For example, a post-processor might compress or upload files. In the compression example, the transformation would be taking an artifact with a set of files, compressing those files, and returning a new artifact with only a single file (the compressed archive). For the upload example, the transformation would be taking an artifact with some set of files, uploading those files, and returning an artifact with a single ID: the URL of the upload. Post-processor plugins implement the [`packer.PostProcessor`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#PostProcessor) interface and are served using the `plugin.ServePostProcessor` function. This page explains how to implement and serve custom post-processors. If you want your post-processor to support HashiCorp Cloud Platform (HCP) Packer, you should also review the [HCP Packer Support](/packer/docs/plugins/creation/hcp-support) documentation. ~> **Warning:** This is an advanced topic that requires strong knowledge of Packer and Packer plugins. ## Before You Begin We recommend reviewing the following resources before you begin development: - [Developing Plugins - Overview](/packer/docs/plugins/creation) - The [Go](https://go.dev/) language. You must write custom plugins in Go, so this guide assumes you are familiar with the language. ## The Interface The interface that must be implemented for a post-processor is the [`packer.PostProcessor`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#PostProcessor) interface. It is reproduced below for reference. The actual interface in the source code contains some basic documentation as well explaining what each method should do. ```go type PostProcessor interface { ConfigSpec() hcldec.ObjectSpec Configure(interface{}) error PostProcess(context.Context, Ui, Artifact) (a Artifact, keep, mustKeep bool, err error) } ``` ### The "ConfigSpec" Method This method returns a hcldec.ObjectSpec, which is a spec necessary for using HCL2 templates with Packer. For information on how to use and implement this function, check our [object spec docs](/packer/guides/hcl/component-object-spec) ### The "Configure" Method The `Configure` method for each post-processor is called early in the build process to configure the post-processor. The configuration is passed in as a raw `interface{}`. The configure method is responsible for translating this configuration into an internal structure, validating it, and returning any errors. For decoding the `interface{}` into a meaningful structure, the [mapstructure](https://github.com/mitchellh/mapstructure) library is recommended. Mapstructure will take an `interface{}` and decode it into an arbitrarily complex struct. If there are any errors, it generates very human-friendly errors that can be returned directly from the configure method. While it is not actively enforced, **no side effects** should occur from running the `Configure` method. Specifically, don't create files, don't create network connections, etc. Configure's purpose is solely to setup internal state and validate the configuration as much as possible. `Configure` being run is not an indication that `PostProcess` will ever run. For example, `packer validate` will run `Configure` to verify the configuration validates, but will never actually run the build. ### The "PostProcess" Method The `PostProcess` method is where the real work goes. PostProcess is responsible for taking one `packer.Artifact` implementation, and transforming it into another. A `PostProcess` call can be cancelled at any moment. Cancellation is triggered when the done chan of the context struct (`<-ctx.Done()`) unblocks . When we say "transform," we don't mean actually modifying the existing `packer.Artifact` value itself. We mean taking the contents of the artifact and creating a new artifact from that. For example, if we were creating a "compress" post-processor that is responsible for compressing files, the transformation would be taking the `Files()` from the original artifact, compressing them, and creating a new artifact with a single file: the compressed archive. The result signature of this method is `(Artifact, bool, bool, error)`. Each return value is explained below: - `Artifact` - The newly created artifact if no errors occurred. - `bool` - If keep true, the input artifact will forcefully be kept. By default, Packer typically deletes all input artifacts, since the user doesn't generally want intermediary artifacts. However, some post-processors depend on the previous artifact existing. If this is `true`, it forces packer to keep the artifact around. - `bool` - If forceOverride is true, then any user input for keep_input_artifact is ignored and the artifact is either kept or discarded according to the value set in `keep`. - `error` - Non-nil if there was an error in any way. If this is the case, the other two return values are ignored.