Merge branch 'master' into datasource_documentation

This commit is contained in:
Megan Marsh 2021-01-19 09:38:48 -08:00 committed by GitHub
commit a4a70e0bb8
28 changed files with 286 additions and 92 deletions

View File

@ -70,9 +70,6 @@ type Driver interface {
// DeleteOSLoginSSHKey deletes the SSH public key for OSLogin with the given key.
DeleteOSLoginSSHKey(user, fingerprint string) error
// If packer is running on a GCE, derives the user from it for use with OSLogin.
GetOSLoginUserFromGCE() string
// Add to the instance metadata for the existing instance
AddToInstanceMetadata(zone string, name string, metadata map[string]string) error
}

View File

@ -10,11 +10,9 @@ import (
"errors"
"fmt"
"log"
"net/http"
"strings"
"time"
metadata "cloud.google.com/go/compute/metadata"
compute "google.golang.org/api/compute/v1"
"google.golang.org/api/option"
oslogin "google.golang.org/api/oslogin/v1"
@ -34,7 +32,6 @@ import (
type driverGCE struct {
projectId string
service *compute.Service
thisGCEUser string
osLoginService *oslogin.Service
ui packersdk.Ui
}
@ -136,8 +133,6 @@ func NewClientOptionGoogle(account *ServiceAccount, vaultOauth string, impersona
func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
var thisGCEUser string
opts, err := NewClientOptionGoogle(config.Account, config.VaultOauthEngineName, config.ImpersonateServiceAccountName)
if err != nil {
return nil, err
@ -149,15 +144,6 @@ func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
return nil, err
}
if metadata.OnGCE() {
log.Printf("[INFO] On GCE, capture service account for OSLogin...")
thisGCEUser, err = metadata.NewClient(&http.Client{}).Email("")
if err != nil {
return nil, err
}
}
log.Printf("[INFO] Instantiating OS Login client...")
osLoginService, err := oslogin.NewService(context.TODO(), opts)
if err != nil {
@ -170,7 +156,6 @@ func NewDriverGCE(config GCEDriverConfig) (Driver, error) {
return &driverGCE{
projectId: config.ProjectId,
service: service,
thisGCEUser: thisGCEUser,
osLoginService: osLoginService,
ui: config.Ui,
}, nil
@ -644,10 +629,6 @@ func (d *driverGCE) getPasswordResponses(zone, instance string) ([]windowsPasswo
return passwordResponses, nil
}
func (d *driverGCE) GetOSLoginUserFromGCE() string {
return d.thisGCEUser
}
func (d *driverGCE) ImportOSLoginSSHKey(user, sshPublicKey string) (*oslogin.LoginProfile, error) {
parent := fmt.Sprintf("users/%s", user)

View File

@ -94,8 +94,6 @@ type DriverMock struct {
AddToInstanceMetadataKVPairs map[string]string
AddToInstanceMetadataErrCh <-chan error
AddToInstanceMetadataErr error
OSLoginUserFromGCE string
}
func (d *DriverMock) CreateImage(name, description, family, zone, disk string, image_labels map[string]string, image_licenses []string, image_encryption_key *compute.CustomerEncryptionKey, imageStorageLocations []string) (<-chan *Image, <-chan error) {
@ -310,7 +308,3 @@ func (d *DriverMock) AddToInstanceMetadata(zone string, name string, metadata ma
return nil
}
func (d *DriverMock) GetOSLoginUserFromGCE() string {
return d.OSLoginUserFromGCE
}

View File

@ -5,7 +5,11 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"log"
"net/http"
"time"
metadata "cloud.google.com/go/compute/metadata"
"github.com/hashicorp/packer-plugin-sdk/multistep"
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
"google.golang.org/api/oauth2/v2"
@ -37,7 +41,7 @@ func (s *StepImportOSLoginSSHKey) Run(ctx context.Context, state multistep.State
}
// Are we running packer on a GCE ?
s.accountEmail = driver.GetOSLoginUserFromGCE()
s.accountEmail = getGCEUser()
if s.TokeninfoFunc == nil && s.accountEmail == "" {
s.TokeninfoFunc = tokeninfo
@ -140,3 +144,28 @@ func tokeninfo(ctx context.Context) (*oauth2.Tokeninfo, error) {
return svc.Tokeninfo().Context(ctx).Do()
}
// getGCEUser determines if we're running packer on a GCE, and if we are, gets the associated service account email for subsequent use with OSLogin.
// There are cases where we are running on a GCE, but the GCP metadata server isn't accessible. GitLab docker-engine runners are an edge case example of this.
// It makes little sense to run packer on GCP in this way, however, we defensively timeout in those cases, rather than abort.
func getGCEUser() string {
metadataCheckTimeout := 5 * time.Second
metadataCheckChl := make(chan string, 1)
go func() {
if metadata.OnGCE() {
GCEUser, _ := metadata.NewClient(&http.Client{}).Email("")
metadataCheckChl <- GCEUser
}
}()
select {
case thisGCEUser := <-metadataCheckChl:
log.Printf("[INFO] OSLogin: GCE service account %s will be used for identity", thisGCEUser)
return thisGCEUser
case <-time.After(metadataCheckTimeout):
log.Printf("[INFO] OSLogin: Could not derive a GCE service account from google metadata server after %s", metadataCheckTimeout)
return ""
}
}

View File

@ -150,37 +150,3 @@ func TestStepImportOSLoginSSHKey_withPrivateSSHKey(t *testing.T) {
t.Errorf("expected to not see a public key when using a dedicated private key, but got %q", pubKey)
}
}
func TestStepImportOSLoginSSHKey_onGCE(t *testing.T) {
state := testState(t)
d := state.Get("driver").(*DriverMock)
step := new(StepImportOSLoginSSHKey)
defer step.Cleanup(state)
fakeAccountEmail := "testing@packer.io"
config := state.Get("config").(*Config)
config.UseOSLogin = true
config.Comm.SSHPublicKey = []byte{'k', 'e', 'y'}
d.OSLoginUserFromGCE = fakeAccountEmail
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
t.Fatalf("bad action: %#v", action)
}
if step.accountEmail != fakeAccountEmail {
t.Fatalf("expected accountEmail to be %q but got %q", fakeAccountEmail, step.accountEmail)
}
pubKey, ok := state.GetOk("ssh_key_public_sha256")
if !ok {
t.Fatal("expected to see a public key")
}
sha256sum := sha256.Sum256(config.Comm.SSHPublicKey)
if pubKey != hex.EncodeToString(sha256sum[:]) {
t.Errorf("expected to see a matching public key, but got %q", pubKey)
}
}

View File

@ -46,6 +46,7 @@ type FlatConfig struct {
ImageDiskFormat *string `mapstructure:"image_disk_format" required:"false" cty:"image_disk_format" hcl:"image_disk_format"`
ImageTags []string `mapstructure:"image_tags" required:"false" cty:"image_tags" hcl:"image_tags"`
ImageMinDisk *int `mapstructure:"image_min_disk" required:"false" cty:"image_min_disk" hcl:"image_min_disk"`
SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"`
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
@ -177,6 +178,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
"image_disk_format": &hcldec.AttrSpec{Name: "image_disk_format", Type: cty.String, Required: false},
"image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.List(cty.String), Required: false},
"image_min_disk": &hcldec.AttrSpec{Name: "image_min_disk", Type: cty.Number, Required: false},
"skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false},
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},

View File

@ -33,6 +33,8 @@ type ImageConfig struct {
ImageTags []string `mapstructure:"image_tags" required:"false"`
// Minimum disk size needed to boot image, in gigabytes.
ImageMinDisk int `mapstructure:"image_min_disk" required:"false"`
// Skip creating the image. Useful for setting to `true` during a build test stage. Defaults to `false`.
SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"`
}
func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {

View File

@ -12,10 +12,16 @@ import (
type stepAddImageMembers struct{}
func (s *stepAddImageMembers) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
imageId := state.Get("image").(string)
ui := state.Get("ui").(packersdk.Ui)
config := state.Get("config").(*Config)
if config.SkipCreateImage {
ui.Say("Skipping image add members...")
return multistep.ActionContinue
}
imageId := state.Get("image").(string)
if len(config.ImageMembers) == 0 {
return multistep.ActionContinue
}

View File

@ -24,6 +24,11 @@ func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) mul
server := state.Get("server").(*servers.Server)
ui := state.Get("ui").(packersdk.Ui)
if config.SkipCreateImage {
ui.Say("Skipping image creation...")
return multistep.ActionContinue
}
// We need the v2 compute client
computeClient, err := config.computeV2Client()
if err != nil {

View File

@ -12,10 +12,16 @@ import (
type stepUpdateImageMinDisk struct{}
func (s *stepUpdateImageMinDisk) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
imageId := state.Get("image").(string)
ui := state.Get("ui").(packersdk.Ui)
config := state.Get("config").(*Config)
if config.SkipCreateImage {
ui.Say("Skipping image update mindisk...")
return multistep.ActionContinue
}
imageId := state.Get("image").(string)
if config.ImageMinDisk == 0 {
return multistep.ActionContinue
}

View File

@ -13,10 +13,16 @@ import (
type stepUpdateImageTags struct{}
func (s *stepUpdateImageTags) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
imageId := state.Get("image").(string)
ui := state.Get("ui").(packersdk.Ui)
config := state.Get("config").(*Config)
if config.SkipCreateImage {
ui.Say("Skipping image update tags...")
return multistep.ActionContinue
}
imageId := state.Get("image").(string)
if len(config.ImageTags) == 0 {
return multistep.ActionContinue
}

View File

@ -12,10 +12,16 @@ import (
type stepUpdateImageVisibility struct{}
func (s *stepUpdateImageVisibility) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
imageId := state.Get("image").(string)
ui := state.Get("ui").(packersdk.Ui)
config := state.Get("config").(*Config)
if config.SkipCreateImage {
ui.Say("Skipping image update visibility...")
return multistep.ActionContinue
}
imageId := state.Get("image").(string)
if config.ImageVisibility == "" {
return multistep.ActionContinue
}

View File

@ -114,7 +114,7 @@ func (s *StepRegisterOMI) Cleanup(state multistep.StateBag) {
}
}
func (s *StepRegisterOMI) combineDevices(snapshotIds map[string]string) []osc.BlockDeviceMappingImage {
func (s *StepRegisterOMI) combineDevices(snapshotIDs map[string]string) []osc.BlockDeviceMappingImage {
devices := map[string]osc.BlockDeviceMappingImage{}
for _, device := range s.OMIDevices {
@ -125,12 +125,20 @@ func (s *StepRegisterOMI) combineDevices(snapshotIds map[string]string) []osc.Bl
// the same name in ami_block_device_mappings, except for the
// one designated as the root device in ami_root_device
for _, device := range s.LaunchDevices {
snapshotId, ok := snapshotIds[device.DeviceName]
snapshotID, ok := snapshotIDs[device.DeviceName]
if ok {
device.Bsu.SnapshotId = snapshotId
device.Bsu.SnapshotId = snapshotID
}
if device.DeviceName == s.RootDevice.SourceDeviceName {
device.DeviceName = s.RootDevice.DeviceName
if device.Bsu.VolumeType != "" {
device.Bsu.VolumeType = s.RootDevice.VolumeType
if device.Bsu.VolumeType != "io1" {
device.Bsu.Iops = 0
}
}
}
devices[device.DeviceName] = copyToDeviceMappingImage(device)
}

View File

@ -72,6 +72,11 @@ func (s *stepFinalizeTemplateConfig) Run(ctx context.Context, state multistep.St
ui.Error(err.Error())
return multistep.ActionHalt
}
} else {
err := fmt.Errorf("cloud_init is set to true, but cloud_init_storage_pool is empty and could not be set automatically. set cloud_init_storage_pool in your configuration")
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}

View File

@ -7,6 +7,7 @@ import (
"io"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
texttemplate "text/template"
@ -14,6 +15,7 @@ import (
"github.com/hashicorp/hcl/v2/hclwrite"
"github.com/hashicorp/packer-plugin-sdk/template"
hcl2shim "github.com/hashicorp/packer/hcl2template/shim"
"github.com/mitchellh/mapstructure"
"github.com/posener/complete"
"github.com/zclconf/go-cty/cty"
)
@ -55,7 +57,7 @@ func (c *HCL2UpgradeCommand) ParseArgs(args []string) (*HCL2UpgradeArgs, int) {
}
const (
hcl2UpgradeFileHeader = `# This file was autogenerated by the BETA 'packer hcl2_upgrade' command. We
hcl2UpgradeFileHeader = `# This file was autogenerated by the 'packer hcl2_upgrade' command. We
# recommend double checking that everything is correct before going forward. We
# also recommend treating this file as disposable. The HCL2 blocks in this
# file can be moved to other files. For example, the variable blocks could be
@ -91,10 +93,14 @@ const (
# https://www.packer.io/docs/templates/hcl_templates/blocks/build
build {
`
amazonAmiDataHeader = `
# The amazon-ami data block is generated from your amazon builder source_ami_filter; a data
# from this block can be referenced in source and locals blocks.
# Read the documentation for data blocks here:
# https://www.packer.io/docs/templates/hcl_templates/blocks/data`
)
func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2UpgradeArgs) int {
out := &bytes.Buffer{}
var output io.Writer
if err := os.MkdirAll(filepath.Dir(cla.OutputFile), 0); err != nil {
@ -179,11 +185,16 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
for _, builder := range tpl.Builders {
builders = append(builders, builder)
}
sort.Slice(builders, func(i, j int) bool {
return builders[i].Type+builders[i].Name < builders[j].Type+builders[j].Name
})
}
if err := c.writeAmazonAmiDatasource(builders, out); err != nil {
return 1
}
sort.Slice(builders, func(i, j int) bool {
return builders[i].Type+builders[i].Name < builders[j].Type+builders[j].Name
})
out.Write([]byte(sourcesHeader))
for i, builderCfg := range builders {
@ -287,6 +298,61 @@ func (c *HCL2UpgradeCommand) RunContext(buildCtx context.Context, cla *HCL2Upgra
return 0
}
func (c *HCL2UpgradeCommand) writeAmazonAmiDatasource(builders []*template.Builder, out *bytes.Buffer) error {
amazonAmiFilters := []map[string]interface{}{}
first := true
i := 1
for _, builder := range builders {
if strings.HasPrefix(builder.Type, "amazon-") {
if sourceAmiFilter, ok := builder.Config["source_ami_filter"]; ok {
sourceAmiFilterCfg := map[string]interface{}{}
if err := mapstructure.Decode(sourceAmiFilter, &sourceAmiFilterCfg); err != nil {
c.Ui.Error(fmt.Sprintf("Failed to write amazon-ami data source: %v", err))
return err
}
duplicate := false
dataSourceName := fmt.Sprintf("autogenerated_%d", i)
for j, filter := range amazonAmiFilters {
if reflect.DeepEqual(filter, sourceAmiFilter) {
duplicate = true
dataSourceName = fmt.Sprintf("autogenerated_%d", j+1)
continue
}
}
// This is a hack...
// Use templating so that it could be correctly transformed later into a data resource
sourceAmiDataRef := fmt.Sprintf("{{ data `amazon-ami.%s.id` }}", dataSourceName)
if duplicate {
delete(builder.Config, "source_ami_filter")
builder.Config["source_ami"] = sourceAmiDataRef
continue
}
amazonAmiFilters = append(amazonAmiFilters, sourceAmiFilterCfg)
delete(builder.Config, "source_ami_filter")
builder.Config["source_ami"] = sourceAmiDataRef
i++
if first {
out.Write([]byte(amazonAmiDataHeader))
first = false
}
datasourceContent := hclwrite.NewEmptyFile()
body := datasourceContent.Body()
body.AppendNewline()
sourceBody := body.AppendNewBlock("data", []string{"amazon-ami", dataSourceName}).Body()
jsonBodyToHCL2Body(sourceBody, sourceAmiFilterCfg)
_, _ = out.Write(transposeTemplatingCalls(datasourceContent.Bytes()))
}
}
}
return nil
}
type UnhandleableArgumentError struct {
Call string
Correspondance string
@ -327,6 +393,9 @@ func transposeTemplatingCalls(s []byte) []byte {
"build": func(a string) string {
return fmt.Sprintf("${build.%s}", a)
},
"data": func(a string) string {
return fmt.Sprintf("${data.%s}", a)
},
"template_dir": func() string {
return fmt.Sprintf("${path.root}")
},

View File

@ -1,4 +1,4 @@
# This file was autogenerated by the BETA 'packer hcl2_upgrade' command. We
# This file was autogenerated by the 'packer hcl2_upgrade' command. We
# recommend double checking that everything is correct before going forward. We
# also recommend treating this file as disposable. The HCL2 blocks in this
# file can be moved to other files. For example, the variable blocks could be
@ -50,6 +50,20 @@ variable "secret_account" {
# "timestamp" template function replacement
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }
# The amazon-ami data block is generated from your amazon builder source_ami_filter; a data
# from this block can be referenced in source and locals blocks.
# Read the documentation for data blocks here:
# https://www.packer.io/docs/templates/hcl_templates/blocks/data
data "amazon-ami" "autogenerated_1" {
filters = {
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["099720109477"]
}
# source blocks are generated from your builders; a source can be referenced in
# build blocks. A build block runs provisioner and post-processors on a
# source. Read the documentation for source blocks here:
@ -65,17 +79,37 @@ source "amazon-ebs" "autogenerated_1" {
volume_size = 48
volume_type = "gp2"
}
region = "${var.aws_region}"
secret_key = "${var.aws_secret_key}"
source_ami_filter {
filters = {
name = "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*"
root-device-type = "ebs"
virtualization-type = "hvm"
region = "${var.aws_region}"
secret_key = "${var.aws_secret_key}"
source_ami = "${data.amazon-ami.autogenerated_1.id}"
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
spot_price = "0.0075"
ssh_interface = "session_manager"
ssh_username = "ubuntu"
temporary_iam_instance_profile_policy_document {
Statement {
Action = ["*"]
Effect = "Allow"
Resource = ["*"]
}
most_recent = true
owners = ["099720109477"]
Version = "2012-10-17"
}
}
source "amazon-ebs" "named_builder" {
access_key = "${var.aws_access_key}"
ami_description = "Ubuntu 16.04 LTS - expand root partition"
ami_name = "ubuntu-16-04-test-${local.timestamp}"
encrypt_boot = true
launch_block_device_mappings {
delete_on_termination = true
device_name = "/dev/sda1"
volume_size = 48
volume_type = "gp2"
}
region = "${var.aws_region}"
secret_key = "${var.aws_secret_key}"
source_ami = "${data.amazon-ami.autogenerated_1.id}"
spot_instance_types = ["t2.small", "t2.medium", "t2.large"]
spot_price = "0.0075"
ssh_interface = "session_manager"
@ -94,7 +128,7 @@ source "amazon-ebs" "autogenerated_1" {
# documentation for build blocks can be found here:
# https://www.packer.io/docs/templates/hcl_templates/blocks/build
build {
sources = ["source.amazon-ebs.autogenerated_1"]
sources = ["source.amazon-ebs.autogenerated_1", "source.amazon-ebs.named_builder"]
provisioner "shell" {
except = ["amazon-ebs"]

View File

@ -61,6 +61,55 @@
]
},
"ssh_interface": "session_manager"
},
{
"type": "amazon-ebs",
"name": "named_builder",
"region": "{{ user `aws_region` }}",
"secret_key": "{{ user `aws_secret_key` }}",
"access_key": "{{ user `aws_access_key` }}",
"ami_name": "ubuntu-16-04-test-{{ timestamp }}",
"ami_description": "Ubuntu 16.04 LTS - expand root partition",
"source_ami_filter": {
"filters": {
"virtualization-type": "hvm",
"name": "ubuntu/images/*/ubuntu-xenial-16.04-amd64-server-*",
"root-device-type": "ebs"
},
"owners": [
"099720109477"
],
"most_recent": true
},
"launch_block_device_mappings": [
{
"delete_on_termination": true,
"device_name": "/dev/sda1",
"volume_type": "gp2",
"volume_size": 48
}
],
"spot_price": "0.0075",
"spot_instance_types": [
"t2.small",
"t2.medium",
"t2.large"
],
"encrypt_boot": true,
"ssh_username": "ubuntu",
"temporary_iam_instance_profile_policy_document": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"*"
],
"Resource": ["*"]
}
]
},
"ssh_interface": "session_manager"
}
],
"provisioners": [

View File

@ -26,6 +26,12 @@
},
{
"pattern": "^/docs/extending/custom-datasources"
},
{
"pattern": "^/docs/templates/legacy_json_templates"
},
{
"pattern": "^https://packer.io/docs/templates/legacy_json_templates"
}
],
"replacementPatterns": [

View File

@ -8,6 +8,8 @@ page_title: Builders - Templates
sidebar_title: Builders
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# Template Builders
Within the template, the builders section contains an array of all the builders

View File

@ -6,6 +6,8 @@ page_title: Communicators - Templates
sidebar_title: Communicators
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# Template Communicators
Communicators are the mechanism Packer uses to upload files, execute scripts,

View File

@ -7,6 +7,8 @@ page_title: Template Engine - Templates
sidebar_title: Engine
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# Template Engine
All strings within templates are processed by a common Packer templating

View File

@ -10,6 +10,8 @@ page_title: JSON Templates
sidebar_title: JSON Templates
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# JSON Templates
These Packer templates are JSON files that configure the various components

View File

@ -7,6 +7,8 @@ page_title: Post-Processors - Templates
sidebar_title: Post-Processors
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# Template Post-Processors
The post-processor section within a template configures any post-processing

View File

@ -7,6 +7,8 @@ page_title: Provisioners - Templates
sidebar_title: Provisioners
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# Template Provisioners
Within the template, the provisioners section contains an array of all the

View File

@ -9,6 +9,8 @@ page_title: User Variables - Templates
sidebar_title: User Variables
---
`@include 'from-1.5/legacy-json-warning.mdx'`
# Template User Variables
User variables allow your templates to be further configured with variables

View File

@ -18,3 +18,5 @@
- `image_tags` ([]string) - List of tags to add to the image after creation.
- `image_min_disk` (int) - Minimum disk size needed to boot image, in gigabytes.
- `skip_create_image` (bool) - Skip creating the image. Useful for setting to `true` during a build test stage. Defaults to `false`.

View File

@ -1,6 +1,7 @@
-> **Note:** This page is about HCL2 in Packer 1.5 and later. HCL2 support for
Packer is still in Beta. Please see the [Packer Issue
Tracker](https://github.com/hashicorp/packer/issues/9176) for a list of
supported features. For the old-style stable configuration language see
[template docs](/docs/templates). You can now transform your JSON file into an
HCL2 config file using the [hcl2_upgrade command](/docs/commands/hcl2_upgrade).
-> **Note:** This page is about HCL2 Packer templates. HCL2 templates
were first introduced as a beta feature into Packer version 1.5. As of v1.7,
HCL2 support is no longer in beta, and is the preferred way to write Packer
configuration. For the old-style stable configuration language see
[template docs](/docs/templates/legacy_json_templates). As of v1.6.2, you can
convert your legacy JSON template into an HCL2 config file using the
[hcl2_upgrade command](/docs/commands/hcl2_upgrade).

View File

@ -0,0 +1,6 @@
-> **Note:** This page is about older-style JSON Packer templates. JSON
templates are still supported by the Packer core, but new features added to the
Packer core may not be implemented for JSON templates. We recommend you
transition to HCL templates as soon as is convenient for you, in order to have
the best possible experience with Packer. To help you upgrade your templates,
we have written an [hcl2_upgrade command](/docs/commands/hcl2_upgrade) command.