github.com/hashicorp/packer@v1.14.3/website/content/docs/plugins/creation/custom-builders.mdx (about) 1 --- 2 description: | 3 You can create custom builders for Packer that extend building functionality. Learn how write custom builders using the Packer plugin interface. 4 page_title: Create custom builders 5 --- 6 7 # Create Custom Builders 8 9 Packer builders are responsible for creating a virtual machine, setting the virtual machine up for provisioning, and then turning that provisioned virtual machine into a machine image. We officially maintain and distribute several builders, including builders to create images on Amazon EC2, VMware, Google 10 Compute Engine, and many more. Refer to the [Builders](/packer/docs/builders) documentation for details. 11 12 This page explains how to use the Packer plugin interface to write custom builders. If you want your builder to support HashiCorp Cloud Platform (HCP) Packer, you should also review the [HCP Packer Support](/packer/docs/plugins/creation/hcp-support) documentation. 13 14 ~> **Warning:** This is an advanced topic that requires strong knowledge of Packer and Packer plugins. 15 16 ## Before You Begin 17 18 We recommend reviewing the following resources before you begin development: 19 20 - [Developing Plugins - Overview](/packer/docs/plugins/creation) 21 - The [Go](https://go.dev/) language. You must write custom plugins in Go, so this guide assumes you are familiar with the language. 22 23 ## The Interface 24 25 To create your own builder, you must create a struct that implements the 26 [`packer.Builder`](https://pkg.go.dev/github.com/hashicorp/packer-plugin-sdk/packer#Builder) interface. It is reproduced below for reference. 27 28 ```go 29 type Builder interface { 30 ConfigSpec() hcldec.ObjectSpec 31 Prepare(...interface{}) ([]string, []string, error) 32 Run(context.Context, ui Ui, hook Hook) (Artifact, error) 33 } 34 ``` 35 36 ### The "ConfigSpec" Method 37 38 This method returns a hcldec.ObjectSpec, which is a spec necessary for using 39 HCL2 templates with Packer. For information on how to use and implement this 40 function, check our 41 [object spec docs](/packer/guides/hcl/component-object-spec) 42 43 ### The "Prepare" Method 44 45 The `Prepare` method for each builder will be called by the Packer core 46 at the beginning of the build. Its purpose is to parse and validate the 47 configuration template provided to Packer with `packer build your_packer_template.json`, but not to execute API calls or begin creating any 48 resources or artifacts. 49 50 The configuration from your Packer template is passed into the Prepare() method 51 as an array of `interface{}` types, but is generally `map[string]interface{}`. 52 The Prepare method is responsible for translating this configuration into an 53 internal structure, validating it, and returning any errors. 54 55 If multiple parameters are passed into Prepare(), they should be merged together 56 into the final configuration, with later parameters overwriting any previous 57 configuration. The exact semantics of the merge are left to the builder author. 58 59 We recommend that you use the 60 [mapstructure](https://godoc.org/github.com/mitchellh/mapstructure) library to 61 decode the `interface{}` into a meaningful structure. Mapstructure will take an 62 `interface{}` and decode it into an arbitrarily complex struct. If there are any 63 errors, it generates very human friendly errors that can be returned directly 64 from the prepare method. You can find many usage examples of this library within 65 the Prepare() methods of HashiCorp-maintained Packer plugins. 66 67 While Packer does not actively enforce this, **no side effects** should occur 68 from running the `Prepare` method. Specifically: don't create files, don't 69 launch virtual machines, etc. Prepare's purpose is solely to load the 70 configuration from the template into a format usable by your builder, to 71 validate that configuration, and to apply necessary defaults to that 72 configuration. 73 74 In addition to the configuration provided in the Packer template, Packer will 75 also supply a [common.PackerConfig](https://github.com/hashicorp/packer-plugin-sdk/blob/8a28198491f70deca3824ce452adf6f9bd507880/common/packer_config.go#L44) 76 containing meta-information such as the build name, builder type, core version, 77 etc, and coded into a `map[string]interface{}`. One important piece of 78 meta information in this map is the `packer.DebugConfigKey` set to boolean 79 `true` if debug mode is enabled for the build. If this is set to true, then the 80 builder should enable a debug mode which assists builder developers and 81 advanced users to introspect what is going on during a build. During debug 82 builds, parallelism is strictly disabled, so it is safe to request input from 83 stdin and so on. 84 85 Prepare() returns an array of strings and an error. The array of strings is a 86 special list of keys for variables created at runtime which your builder will 87 make accessible to provisioners using the generatedData mechanism (see below for 88 more details) An example could be an instance ID for the cloud instance created 89 by your builder. If you do not plan to make use of the generatedData feature, 90 just return an empty list. The error should be used if there is something wrong 91 with the user-provided configuration and the build should not proceed. 92 93 ### The "Run" Method 94 95 `Run` is executed, often in parallel for multiple builders, to actually build 96 the machine, provision it, and create the resulting machine image, which is 97 returned as an implementation of the `packer.Artifact` interface. 98 99 The `Run` method takes three parameters. The context.Context used to cancel the 100 build. The `packer.Ui` object is used to send output to the console. 101 `packer.Hook` is used to execute hooks, which are covered in more detail in the 102 Provisioning section below. 103 104 Because builder runs are typically a complex set of many steps, the 105 packer-plugin-sdk contains a 106 [multistep](https://godoc.org/github.com/hashicorp/packer-plugin-sdk/multistep) 107 module. Multistep allows you to separate your build logic into multiple distinct 108 "steps" with separate run and cleanup phases, and run them in order. It 109 supports cancellation mid-step, pausing between steps when debugging, the CLI's 110 on-error flag, and more. All of the HashiCorp maintained builders make use of 111 this module, and while it is not required for builder implementation, it will 112 help you create your builder in a way that matches user and Packer Core 113 assumptions. The SDK also provides a number of "helper" generic steps that may 114 prevent you from having to re-implement work that has already been done by the 115 HashiCorp maintainers. Examples include sending boot commands, connecting to 116 SSH, and creating virtual CDs to mount on your VM. Take a look at the 117 [communicator](https://github.com/hashicorp/packer-plugin-sdk/tree/main/communicator) 118 and 119 [multistep/commonsteps](https://github.com/hashicorp/packer-plugin-sdk/tree/main/multistep/commonsteps) 120 modules in the SDK to see what tools are available to you. 121 122 Finally, `Run` should return an implementation of `packer.Artifact`. More 123 details on creating a `packer.Artifact` are covered in the artifact section 124 below. If something goes wrong during the build that prevents an artifact from 125 being correctly created, `Run` should return an error and a nil artifact. Note 126 that your builder is allowed to produce no artifact and no error, although this 127 is a rare use case. 128 129 ### Cancellation 130 131 #### With the "Cancel" Method ( for plugins for Packer < v1.3 ) 132 133 The `Cancel` method can be called at any time and requests cancellation of any 134 builder run in progress. This method should block until the run actually stops. 135 Note that the Cancel method will not be called by Packer versions >= 1.4.0. 136 137 #### Context cancellation ( from Packer v1.4 ) 138 139 The `<-ctx.Done()` can unblock at any time and signifies request for 140 cancellation of any builder run in progress. 141 142 Cancels are most commonly triggered by external interrupts, such as the user 143 pressing `Ctrl-C`. Packer will only exit once all the builders clean up, so it 144 is important that you architect your builder in a way that it is quick to 145 respond to these cancellations and clean up after itself. If your builder makes 146 a long-running call, you should consider the possibility that a user may cancel 147 the build during that call, and make sure that such a cancellation is not 148 blocked. 149 150 ## Creating an Artifact 151 152 The `Run` method is expected to return an implementation of the 153 `packer.Artifact` interface. Each builder must create its own implementation of 154 this interface. 155 156 Most of the pieces of an artifact should be fairly self-explanatory by reading 157 the [packer.Artifact interface 158 documentation](https://godoc.org/github.com/hashicorp/packer-plugin-sdk/packer#Artifact). 159 160 However one part of an artifact that may be confusing is the `BuilderId` method. 161 This method must return an absolutely unique ID for the builder. In general, a 162 reasonable ID would be the github username or organization that created the 163 builder, followed by the platform it is building for. For example, the builder 164 ID of the VMware builder is "hashicorp.vmware". 165 166 Post-processors use the builder ID value in order to make some assumptions 167 about the artifact results and to determine whether they are even able to run 168 against a given artifact, so it is important that this ID never changes once 169 the builder is published. 170 171 The builder ID for each builder is included on the associated documentation page. 172 173 ## Provisioning 174 175 Packer has built-in support for provisioning using the Provisioner plugins. But 176 builders themselves, rather than the Packer core, must determine when to invoke 177 the provisioners since only the builder knows when the machine is running and 178 ready for communication. 179 180 When the machine is ready to be provisioned, run the `packer.HookProvision` 181 hook, making sure the communicator is not nil, since this is required for 182 provisioners. An example of calling the hook is shown below: 183 184 ```go 185 hook.Run(context.Context, packer.HookProvision, ui, comm, nil) 186 ``` 187 188 At this point, Packer will run the provisioners and no additional work is 189 necessary. 190 191 If you are using the multistep tooling, the Packer plugin SDK contains a 192 generic StepProvision which handles execution the provision hook for you and 193 automatically supplies any custom builder generatedData you would like to 194 provide to provisioners (see below for more details on generatedData.) 195 196 ## Template Engine 197 198 ~> Note: In HCL2 the JSON template engine and generated data is slowly going to 199 be deprecated in favor of using HCL2 objects, we will keep on supporting those 200 but it is recommended to avoid using them if you can. 201 202 ### Build variables 203 204 Packer JSON makes it possible to provide custom template engine variables to be 205 shared with provisioners and post-processors using the `build` function. 206 JSON template `build` docs are [here](/packer/docs/templates/legacy_json_templates/engine#build) 207 and HCL template build docs are [here](/packer/docs/templates/hcl_templates/contextual-variables#build-variables). 208 209 As of Packer v1.5.0, builder Prepare() methods return a list of custom variables 210 which we call `generated data`. We use that list of variables to generate a 211 custom placeholder map per builder that combines custom variables with the 212 placeholder map of default build variables created by Packer. Here's an example 213 snippet telling packer what will be made available by the builder: 214 215 ```go 216 func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) { 217 // ... 218 219 generatedData := []string{"SourceImageName"} 220 return generatedData, warns, nil 221 } 222 ``` 223 224 When a user provides a Packer template that uses a `build` function, Packer 225 validates that the key in the `build` function exists for a given builder using 226 this generatedData array. If it does not exist, then Packer validation will 227 fail. 228 229 Once the placeholder is set, it's necessary to pass the variables' real values 230 when calling the provisioner. This can be done as the example below: 231 232 ```go 233 func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { 234 // ... 235 236 // Create map of custom variable 237 generatedData := map[string]interface{}{"SourceImageName": "the source image name value"} 238 // Pass map to provisioner 239 hook.Run(context.Context, packer.HookProvision, ui, comm, generatedData) 240 241 // ... 242 } 243 ``` 244 245 In order to make these same variables and the Packer default ones also available 246 to post-processors, your builder will need to add them to its Artifact. 247 This can be done by adding an attribute of type `map[string]interface{}` to the 248 Artifact and putting the generated data in it. The post-processor will access 249 this data later via the Artifact's `State` method. 250 251 The Artifact code should be implemented similar to the below: 252 253 ```go 254 type Artifact struct { 255 // ... 256 257 // StateData should store data such as GeneratedData 258 // to be shared with post-processors 259 StateData map[string]interface{} 260 } 261 262 // ... 263 264 func (a *Artifact) State(name string) interface{} { 265 return a.StateData[name] 266 } 267 268 // ... 269 ``` 270 271 The builder should return the above Artifact containing the generated data and 272 the code should be similar to the example snippet below: 273 274 ```go 275 func (b *Builder) Run(ctx context.Context, ui packer.Ui, hook packer.Hook) (packer.Artifact, error) { 276 // ... 277 278 return &Artifact{ 279 // ... 280 StateData: map[string]interface{}{"generated_data": state.Get("generated_data")}, 281 }, nil 282 } 283 ``` 284 285 The code above assigns the `generated_data` state to the `StateData` map with 286 the key `generated_data`. 287 288 Here some example of how this data will be used by post-processors: 289 290 ```go 291 func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, source packer.Artifact) (packer.Artifact, bool, bool, error) { 292 generatedData := source.State("generated_data") 293 294 // generatedData will then be used for interpolation 295 296 // ... 297 } 298 ``` 299 300 ## Putting it all together 301 302 This page has focused up until now on the implementation details for the Builder 303 interface. You will need to create a server and store the builder in a binary in 304 order to make it available to the Packer core as a plugin. We have created a 305 [scaffolding](https://github.com/hashicorp/packer-plugin-scaffolding/blob/main/builder/scaffolding/builder.go) 306 repo to give you an idea of the relationship between the builder 307 implementation and the server implementation within a repository, and then read 308 [basics of how Plugins work](/packer/docs/plugins/install), which breaks down all the 309 server details.