amazon/ebssurrogate: Add New Builder
This commit adds a new type of builder which builds an AMI based on a snapshot of an EBS volume which is provisioned on a "surrogate" instance. This can be used to build operating system images from scratch, but unlike the `chroot` builder does not require running from an AWS EC2 instance.
This commit is contained in:
parent
0d16df1427
commit
635aeb765b
|
@ -0,0 +1,218 @@
|
||||||
|
// The ebssurrogate package contains a packer.Builder implementation that
|
||||||
|
// builds a new EBS-backed AMI using an ephemeral instance.
|
||||||
|
package ebssurrogate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/hashicorp/errwrap"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
awscommon "github.com/mitchellh/packer/builder/amazon/common"
|
||||||
|
"github.com/mitchellh/packer/common"
|
||||||
|
"github.com/mitchellh/packer/helper/communicator"
|
||||||
|
"github.com/mitchellh/packer/helper/config"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
"github.com/mitchellh/packer/template/interpolate"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BuilderId = "mitchellh.amazon.ebssurrogate"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
common.PackerConfig `mapstructure:",squash"`
|
||||||
|
awscommon.AccessConfig `mapstructure:",squash"`
|
||||||
|
awscommon.RunConfig `mapstructure:",squash"`
|
||||||
|
awscommon.BlockDevices `mapstructure:",squash"`
|
||||||
|
awscommon.AMIConfig `mapstructure:",squash"`
|
||||||
|
|
||||||
|
RootDevice RootBlockDevice `mapstructure:"ami_root_device"`
|
||||||
|
|
||||||
|
ctx interpolate.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type Builder struct {
|
||||||
|
config Config
|
||||||
|
runner multistep.Runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
|
||||||
|
b.config.ctx.Funcs = awscommon.TemplateFuncs
|
||||||
|
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||||
|
Interpolate: true,
|
||||||
|
InterpolateContext: &b.config.ctx,
|
||||||
|
InterpolateFilter: &interpolate.RenderFilter{
|
||||||
|
Exclude: []string{
|
||||||
|
"ami_description",
|
||||||
|
"run_tags",
|
||||||
|
"tags",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, raws...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate any errors
|
||||||
|
var errs *packer.MultiError
|
||||||
|
errs = packer.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, b.config.AMIConfig.Prepare(&b.config.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, b.config.BlockDevices.Prepare(&b.config.ctx)...)
|
||||||
|
errs = packer.MultiErrorAppend(errs, b.config.RootDevice.Prepare(&b.config.ctx)...)
|
||||||
|
|
||||||
|
if b.config.AMIVirtType == "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, errors.New("ami_virtualization_type is required."))
|
||||||
|
}
|
||||||
|
|
||||||
|
foundRootVolume := false
|
||||||
|
for _, launchDevice := range b.config.BlockDevices.LaunchMappings {
|
||||||
|
if launchDevice.DeviceName == b.config.RootDevice.SourceDeviceName {
|
||||||
|
foundRootVolume = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !foundRootVolume {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("no volume with name '%s' is found", b.config.RootDevice.SourceDeviceName))
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs != nil && len(errs.Errors) > 0 {
|
||||||
|
return nil, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println(common.ScrubConfig(b.config, b.config.AccessKey, b.config.SecretKey))
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
|
||||||
|
awsConfig, err := b.config.Config()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
awsSession, err := session.NewSession(awsConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ec2conn := ec2.New(awsSession)
|
||||||
|
|
||||||
|
// If the subnet is specified but not the AZ, try to determine the AZ automatically
|
||||||
|
if b.config.SubnetId != "" && b.config.AvailabilityZone == "" {
|
||||||
|
log.Printf("[INFO] Finding AZ for the given subnet '%s'", b.config.SubnetId)
|
||||||
|
resp, err := ec2conn.DescribeSubnets(&ec2.DescribeSubnetsInput{SubnetIds: []*string{&b.config.SubnetId}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
b.config.AvailabilityZone = *resp.Subnets[0].AvailabilityZone
|
||||||
|
log.Printf("[INFO] AZ found: '%s'", b.config.AvailabilityZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the state bag and initial state for the steps
|
||||||
|
state := new(multistep.BasicStateBag)
|
||||||
|
state.Put("config", &b.config)
|
||||||
|
state.Put("ec2", ec2conn)
|
||||||
|
state.Put("hook", hook)
|
||||||
|
state.Put("ui", ui)
|
||||||
|
|
||||||
|
// Build the steps
|
||||||
|
steps := []multistep.Step{
|
||||||
|
&awscommon.StepSourceAMIInfo{
|
||||||
|
SourceAmi: b.config.SourceAmi,
|
||||||
|
EnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||||
|
AmiFilters: b.config.SourceAmiFilter,
|
||||||
|
},
|
||||||
|
&awscommon.StepKeyPair{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
SSHAgentAuth: b.config.Comm.SSHAgentAuth,
|
||||||
|
DebugKeyPath: fmt.Sprintf("ec2_%s.pem", b.config.PackerBuildName),
|
||||||
|
KeyPairName: b.config.SSHKeyPairName,
|
||||||
|
TemporaryKeyPairName: b.config.TemporaryKeyPairName,
|
||||||
|
PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey,
|
||||||
|
},
|
||||||
|
&awscommon.StepSecurityGroup{
|
||||||
|
SecurityGroupIds: b.config.SecurityGroupIds,
|
||||||
|
CommConfig: &b.config.RunConfig.Comm,
|
||||||
|
VpcId: b.config.VpcId,
|
||||||
|
},
|
||||||
|
&awscommon.StepRunSourceInstance{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
ExpectedRootDevice: "ebs",
|
||||||
|
SpotPrice: b.config.SpotPrice,
|
||||||
|
SpotPriceProduct: b.config.SpotPriceAutoProduct,
|
||||||
|
InstanceType: b.config.InstanceType,
|
||||||
|
UserData: b.config.UserData,
|
||||||
|
UserDataFile: b.config.UserDataFile,
|
||||||
|
SourceAMI: b.config.SourceAmi,
|
||||||
|
IamInstanceProfile: b.config.IamInstanceProfile,
|
||||||
|
SubnetId: b.config.SubnetId,
|
||||||
|
AssociatePublicIpAddress: b.config.AssociatePublicIpAddress,
|
||||||
|
EbsOptimized: b.config.EbsOptimized,
|
||||||
|
AvailabilityZone: b.config.AvailabilityZone,
|
||||||
|
BlockDevices: b.config.BlockDevices,
|
||||||
|
Tags: b.config.RunTags,
|
||||||
|
InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior,
|
||||||
|
},
|
||||||
|
&awscommon.StepGetPassword{
|
||||||
|
Debug: b.config.PackerDebug,
|
||||||
|
Comm: &b.config.RunConfig.Comm,
|
||||||
|
Timeout: b.config.WindowsPasswordTimeout,
|
||||||
|
},
|
||||||
|
&communicator.StepConnect{
|
||||||
|
Config: &b.config.RunConfig.Comm,
|
||||||
|
Host: awscommon.SSHHost(
|
||||||
|
ec2conn,
|
||||||
|
b.config.SSHPrivateIp),
|
||||||
|
SSHConfig: awscommon.SSHConfig(
|
||||||
|
b.config.RunConfig.Comm.SSHAgentAuth,
|
||||||
|
b.config.RunConfig.Comm.SSHUsername,
|
||||||
|
b.config.RunConfig.Comm.SSHPassword),
|
||||||
|
},
|
||||||
|
&common.StepProvision{},
|
||||||
|
&awscommon.StepStopEBSBackedInstance{
|
||||||
|
SpotPrice: b.config.SpotPrice,
|
||||||
|
DisableStopInstance: b.config.DisableStopInstance,
|
||||||
|
},
|
||||||
|
&awscommon.StepModifyEBSBackedInstance{
|
||||||
|
EnableEnhancedNetworking: b.config.AMIEnhancedNetworking,
|
||||||
|
},
|
||||||
|
&StepSnapshotNewRootVolume{
|
||||||
|
NewRootMountPoint: b.config.RootDevice.SourceDeviceName,
|
||||||
|
},
|
||||||
|
&StepRegisterAMI{
|
||||||
|
RootDevice: b.config.RootDevice,
|
||||||
|
BlockDevices: b.config.BlockDevices.BuildLaunchDevices(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run!
|
||||||
|
b.runner = common.NewRunner(steps, b.config.PackerConfig, ui)
|
||||||
|
b.runner.Run(state)
|
||||||
|
|
||||||
|
// If there was an error, return that
|
||||||
|
if rawErr, ok := state.GetOk("error"); ok {
|
||||||
|
return nil, rawErr.(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if amis, ok := state.GetOk("amis"); ok {
|
||||||
|
// Build the artifact and return it
|
||||||
|
artifact := &awscommon.Artifact{
|
||||||
|
Amis: amis.(map[string]string),
|
||||||
|
BuilderIdValue: BuilderId,
|
||||||
|
Conn: ec2conn,
|
||||||
|
}
|
||||||
|
|
||||||
|
return artifact, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Builder) Cancel() {
|
||||||
|
if b.runner != nil {
|
||||||
|
log.Println("Cancelling the step runner...")
|
||||||
|
b.runner.Cancel()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package ebssurrogate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testConfig() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"access_key": "foo",
|
||||||
|
"secret_key": "bar",
|
||||||
|
"source_ami": "foo",
|
||||||
|
"instance_type": "foo",
|
||||||
|
"region": "us-east-1",
|
||||||
|
"ssh_username": "root",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilder_ImplementsBuilder(t *testing.T) {
|
||||||
|
var raw interface{}
|
||||||
|
raw = &Builder{}
|
||||||
|
if _, ok := raw.(packer.Builder); !ok {
|
||||||
|
t.Fatal("Builder should be a builder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilder_Prepare_BadType(t *testing.T) {
|
||||||
|
b := &Builder{}
|
||||||
|
c := map[string]interface{}{
|
||||||
|
"access_key": []string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings, err := b.Prepare(c)
|
||||||
|
if len(warnings) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warnings)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("prepare should fail")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuilderPrepare_InvalidKey(t *testing.T) {
|
||||||
|
var b Builder
|
||||||
|
config := testConfig()
|
||||||
|
|
||||||
|
// Add a random key
|
||||||
|
config["i_should_not_be_valid"] = true
|
||||||
|
warnings, err := b.Prepare(config)
|
||||||
|
if len(warnings) > 0 {
|
||||||
|
t.Fatalf("bad: %#v", warnings)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have error")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package ebssurrogate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/mitchellh/packer/template/interpolate"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RootBlockDevice struct {
|
||||||
|
SourceDeviceName string `mapstructure:"source_device_name"`
|
||||||
|
DeviceName string `mapstructure:"device_name"`
|
||||||
|
DeleteOnTermination bool `mapstructure:"delete_on_termination"`
|
||||||
|
IOPS int64 `mapstructure:"iops"`
|
||||||
|
VolumeType string `mapstructure:"volume_type"`
|
||||||
|
VolumeSize int64 `mapstructure:"volume_size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *RootBlockDevice) Prepare(ctx *interpolate.Context) []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
if c.SourceDeviceName == "" {
|
||||||
|
errs = append(errs, errors.New("source_device_name for the root_device must be specified"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.DeviceName == "" {
|
||||||
|
errs = append(errs, errors.New("device_name for the root_device must be specified"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.VolumeType == "gp2" && c.IOPS != 0 {
|
||||||
|
errs = append(errs, errors.New("iops may not be specified for a gp2 volume"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.IOPS < 0 {
|
||||||
|
errs = append(errs, errors.New("iops must be greater than 0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.VolumeSize < 0 {
|
||||||
|
errs = append(errs, errors.New("volume_size must be greater than 0"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *RootBlockDevice) createBlockDeviceMapping(snapshotId string) *ec2.BlockDeviceMapping {
|
||||||
|
rootBlockDevice := &ec2.EbsBlockDevice{
|
||||||
|
SnapshotId: aws.String(snapshotId),
|
||||||
|
VolumeType: aws.String(d.VolumeType),
|
||||||
|
VolumeSize: aws.Int64(d.VolumeSize),
|
||||||
|
DeleteOnTermination: aws.Bool(d.DeleteOnTermination),
|
||||||
|
}
|
||||||
|
|
||||||
|
if d.IOPS != 0 {
|
||||||
|
rootBlockDevice.Iops = aws.Int64(d.IOPS)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ec2.BlockDeviceMapping{
|
||||||
|
DeviceName: aws.String(d.DeviceName),
|
||||||
|
Ebs: rootBlockDevice,
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package ebssurrogate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
awscommon "github.com/mitchellh/packer/builder/amazon/common"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StepRegisterAMI creates the AMI.
|
||||||
|
type StepRegisterAMI struct {
|
||||||
|
RootDevice RootBlockDevice
|
||||||
|
BlockDevices []*ec2.BlockDeviceMapping
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
config := state.Get("config").(*Config)
|
||||||
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
snapshotId := state.Get("snapshot_id").(string)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
ui.Say("Registering the AMI...")
|
||||||
|
|
||||||
|
blockDevices := s.BlockDevices
|
||||||
|
blockDevices = append(blockDevices, s.RootDevice.createBlockDeviceMapping(snapshotId))
|
||||||
|
|
||||||
|
registerOpts := &ec2.RegisterImageInput{
|
||||||
|
Name: &config.AMIName,
|
||||||
|
Architecture: aws.String(ec2.ArchitectureValuesX8664),
|
||||||
|
RootDeviceName: aws.String(s.RootDevice.DeviceName),
|
||||||
|
VirtualizationType: aws.String(config.AMIVirtType),
|
||||||
|
BlockDeviceMappings: blockDevices,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set SriovNetSupport to "simple". See http://goo.gl/icuXh5
|
||||||
|
if config.AMIEnhancedNetworking {
|
||||||
|
registerOpts.SriovNetSupport = aws.String("simple")
|
||||||
|
}
|
||||||
|
|
||||||
|
registerResp, err := ec2conn.RegisterImage(registerOpts)
|
||||||
|
if err != nil {
|
||||||
|
state.Put("error", fmt.Errorf("Error registering AMI: %s", err))
|
||||||
|
ui.Error(state.Get("error").(error).Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the AMI ID in the state
|
||||||
|
ui.Say(fmt.Sprintf("AMI: %s", *registerResp.ImageId))
|
||||||
|
amis := make(map[string]string)
|
||||||
|
amis[*ec2conn.Config.Region] = *registerResp.ImageId
|
||||||
|
state.Put("amis", amis)
|
||||||
|
|
||||||
|
// Wait for the image to become ready
|
||||||
|
stateChange := awscommon.StateChangeConf{
|
||||||
|
Pending: []string{"pending"},
|
||||||
|
Target: "available",
|
||||||
|
Refresh: awscommon.AMIStateRefreshFunc(ec2conn, *registerResp.ImageId),
|
||||||
|
StepState: state,
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say("Waiting for AMI to become ready...")
|
||||||
|
if _, err := awscommon.WaitForState(&stateChange); err != nil {
|
||||||
|
err := fmt.Errorf("Error waiting for AMI: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package ebssurrogate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go/service/ec2"
|
||||||
|
"github.com/mitchellh/multistep"
|
||||||
|
awscommon "github.com/mitchellh/packer/builder/amazon/common"
|
||||||
|
"github.com/mitchellh/packer/packer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StepSnapshotNewRootVolume creates a snapshot of the created volume.
|
||||||
|
//
|
||||||
|
// Produces:
|
||||||
|
// snapshot_id string - ID of the created snapshot
|
||||||
|
type StepSnapshotNewRootVolume struct {
|
||||||
|
NewRootMountPoint string
|
||||||
|
snapshotId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepSnapshotNewRootVolume) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
instance := state.Get("instance").(*ec2.Instance)
|
||||||
|
|
||||||
|
var newRootVolume string
|
||||||
|
for _, volume := range instance.BlockDeviceMappings {
|
||||||
|
if *volume.DeviceName == s.NewRootMountPoint {
|
||||||
|
newRootVolume = *volume.Ebs.VolumeId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Say(fmt.Sprintf("Creating snapshot of EBS Volume %s...", newRootVolume))
|
||||||
|
description := fmt.Sprintf("Packer: %s", time.Now().String())
|
||||||
|
|
||||||
|
createSnapResp, err := ec2conn.CreateSnapshot(&ec2.CreateSnapshotInput{
|
||||||
|
VolumeId: &newRootVolume,
|
||||||
|
Description: &description,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error creating snapshot: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the snapshot ID so we can delete it later
|
||||||
|
s.snapshotId = *createSnapResp.SnapshotId
|
||||||
|
ui.Message(fmt.Sprintf("Snapshot ID: %s", s.snapshotId))
|
||||||
|
|
||||||
|
// Wait for the snapshot to be ready
|
||||||
|
stateChange := awscommon.StateChangeConf{
|
||||||
|
Pending: []string{"pending"},
|
||||||
|
StepState: state,
|
||||||
|
Target: "completed",
|
||||||
|
Refresh: func() (interface{}, string, error) {
|
||||||
|
resp, err := ec2conn.DescribeSnapshots(&ec2.DescribeSnapshotsInput{SnapshotIds: []*string{&s.snapshotId}})
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resp.Snapshots) == 0 {
|
||||||
|
return nil, "", errors.New("No snapshots found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := resp.Snapshots[0]
|
||||||
|
return s, *s.State, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = awscommon.WaitForState(&stateChange)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Error waiting for snapshot: %s", err)
|
||||||
|
state.Put("error", err)
|
||||||
|
ui.Error(err.Error())
|
||||||
|
return multistep.ActionHalt
|
||||||
|
}
|
||||||
|
|
||||||
|
state.Put("snapshot_id", s.snapshotId)
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *StepSnapshotNewRootVolume) Cleanup(state multistep.StateBag) {
|
||||||
|
if s.snapshotId == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, cancelled := state.GetOk(multistep.StateCancelled)
|
||||||
|
_, halted := state.GetOk(multistep.StateHalted)
|
||||||
|
|
||||||
|
if cancelled || halted {
|
||||||
|
ec2conn := state.Get("ec2").(*ec2.EC2)
|
||||||
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
ui.Say("Removing snapshot since we cancelled or halted...")
|
||||||
|
_, err := ec2conn.DeleteSnapshot(&ec2.DeleteSnapshotInput{SnapshotId: &s.snapshotId})
|
||||||
|
if err != nil {
|
||||||
|
ui.Error(fmt.Sprintf("Error: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
amazonchrootbuilder "github.com/mitchellh/packer/builder/amazon/chroot"
|
amazonchrootbuilder "github.com/mitchellh/packer/builder/amazon/chroot"
|
||||||
amazonebsbuilder "github.com/mitchellh/packer/builder/amazon/ebs"
|
amazonebsbuilder "github.com/mitchellh/packer/builder/amazon/ebs"
|
||||||
|
amazonebssurrogatebuilder "github.com/mitchellh/packer/builder/amazon/ebssurrogate"
|
||||||
amazonebsvolumebuilder "github.com/mitchellh/packer/builder/amazon/ebsvolume"
|
amazonebsvolumebuilder "github.com/mitchellh/packer/builder/amazon/ebsvolume"
|
||||||
amazoninstancebuilder "github.com/mitchellh/packer/builder/amazon/instance"
|
amazoninstancebuilder "github.com/mitchellh/packer/builder/amazon/instance"
|
||||||
azurearmbuilder "github.com/mitchellh/packer/builder/azure/arm"
|
azurearmbuilder "github.com/mitchellh/packer/builder/azure/arm"
|
||||||
|
@ -75,6 +76,7 @@ var Builders = map[string]packer.Builder{
|
||||||
"amazon-chroot": new(amazonchrootbuilder.Builder),
|
"amazon-chroot": new(amazonchrootbuilder.Builder),
|
||||||
"amazon-ebs": new(amazonebsbuilder.Builder),
|
"amazon-ebs": new(amazonebsbuilder.Builder),
|
||||||
"amazon-ebsvolume": new(amazonebsvolumebuilder.Builder),
|
"amazon-ebsvolume": new(amazonebsvolumebuilder.Builder),
|
||||||
|
"amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder),
|
||||||
"amazon-instance": new(amazoninstancebuilder.Builder),
|
"amazon-instance": new(amazoninstancebuilder.Builder),
|
||||||
"azure-arm": new(azurearmbuilder.Builder),
|
"azure-arm": new(azurearmbuilder.Builder),
|
||||||
"cloudstack": new(cloudstackbuilder.Builder),
|
"cloudstack": new(cloudstackbuilder.Builder),
|
||||||
|
|
|
@ -0,0 +1,399 @@
|
||||||
|
---
|
||||||
|
description: |
|
||||||
|
The `amazon-ebssurrogate` Packer builder is like the chroot builder, but does
|
||||||
|
not require running inside an EC2 instance.
|
||||||
|
layout: docs
|
||||||
|
page_title: 'Amazon EBS Surrogate Builder'
|
||||||
|
...
|
||||||
|
|
||||||
|
# EBS Surrogate Builder
|
||||||
|
|
||||||
|
Type: `amazon-ebssurrogate`
|
||||||
|
|
||||||
|
The `amazon-ebssurrogate` Packer builder is able to create Amazon AMIs by running
|
||||||
|
a source instance with an attached volume, provisioning the attached volume in such
|
||||||
|
a way that it can be used as the root volume for the AMI, and then snapshotting and
|
||||||
|
creating the AMI from that volume.
|
||||||
|
|
||||||
|
This builder can therefore be used to bootstrap scratch-build images - for example
|
||||||
|
FreeBSD or Ubuntu using ZFS as the root file system.
|
||||||
|
|
||||||
|
This is all done in your own AWS account. The builder will create temporary
|
||||||
|
key pairs, security group rules, etc. that provide it temporary access to the
|
||||||
|
instance while the image is being created.
|
||||||
|
|
||||||
|
## Configuration Reference
|
||||||
|
|
||||||
|
There are many configuration options available for the builder. They are
|
||||||
|
segmented below into two categories: required and optional parameters. Within
|
||||||
|
each category, the available configuration keys are alphabetized.
|
||||||
|
|
||||||
|
In addition to the options listed here, a
|
||||||
|
[communicator](/docs/templates/communicator.html) can be configured for this
|
||||||
|
builder.
|
||||||
|
|
||||||
|
### Required:
|
||||||
|
|
||||||
|
- `access_key` (string) - The access key used to communicate with AWS. [Learn
|
||||||
|
how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
|
||||||
|
|
||||||
|
- `instance_type` (string) - The EC2 instance type to use while building the
|
||||||
|
AMI, such as `m1.small`.
|
||||||
|
|
||||||
|
- `region` (string) - The name of the region, such as `us-east-1`, in which to
|
||||||
|
launch the EC2 instance to create the AMI.
|
||||||
|
|
||||||
|
- `secret_key` (string) - The secret key used to communicate with AWS. [Learn
|
||||||
|
how to set this.](/docs/builders/amazon.html#specifying-amazon-credentials)
|
||||||
|
|
||||||
|
- `source_ami` (string) - The initial AMI used as a base for the newly
|
||||||
|
created machine. `source_ami_filter` may be used instead to populate this
|
||||||
|
automatically.
|
||||||
|
|
||||||
|
- `ami_root_device` (block device mapping) - A block device mapping describing
|
||||||
|
the root device of the AMI. This looks like the mappings in `ami_block_device_mapping`,
|
||||||
|
except with an additional field:
|
||||||
|
|
||||||
|
- `source_device_name` (string) - The device name of the block device on the
|
||||||
|
source instance to be used as the root device for the AMI. This must correspond
|
||||||
|
to a block device in `launch_block_device_mapping`.
|
||||||
|
|
||||||
|
### Optional:
|
||||||
|
|
||||||
|
- `ami_block_device_mappings` (array of block device mappings) - Add one or
|
||||||
|
more [block device mappings](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html)
|
||||||
|
to the AMI. These will be attached when booting a new instance from your
|
||||||
|
AMI. To add a block device during the packer build see
|
||||||
|
`launch_block_device_mappings` below. Your options here may vary depending
|
||||||
|
on the type of VM you use. The block device mappings allow for the following
|
||||||
|
configuration:
|
||||||
|
|
||||||
|
- `delete_on_termination` (boolean) - Indicates whether the EBS volume is
|
||||||
|
deleted on instance termination. Default `false`. **NOTE**: If this
|
||||||
|
value is not explicitly set to `true` and volumes are not cleaned up by
|
||||||
|
an alternative method, additional volumes will accumulate after
|
||||||
|
every build.
|
||||||
|
|
||||||
|
- `device_name` (string) - The device name exposed to the instance (for
|
||||||
|
example, `/dev/sdh` or `xvdh`). Required when specifying `volume_size`.
|
||||||
|
|
||||||
|
- `encrypted` (boolean) - Indicates whether to encrypt the volume or not
|
||||||
|
|
||||||
|
- `iops` (integer) - The number of I/O operations per second (IOPS) that the
|
||||||
|
volume supports. See the documentation on
|
||||||
|
[IOPs](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_EbsBlockDevice.html)
|
||||||
|
for more information
|
||||||
|
|
||||||
|
- `no_device` (boolean) - Suppresses the specified device included in the
|
||||||
|
block device mapping of the AMI
|
||||||
|
|
||||||
|
- `snapshot_id` (string) - The ID of the snapshot
|
||||||
|
|
||||||
|
- `virtual_name` (string) - The virtual device name. See the documentation on
|
||||||
|
[Block Device
|
||||||
|
Mapping](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_BlockDeviceMapping.html)
|
||||||
|
for more information
|
||||||
|
|
||||||
|
- `volume_size` (integer) - The size of the volume, in GiB. Required if not
|
||||||
|
specifying a `snapshot_id`
|
||||||
|
|
||||||
|
- `volume_type` (string) - The volume type. `gp2` for General Purpose (SSD)
|
||||||
|
volumes, `io1` for Provisioned IOPS (SSD) volumes, and `standard` for Magnetic
|
||||||
|
volumes
|
||||||
|
|
||||||
|
- `ami_description` (string) - The description to set for the
|
||||||
|
resulting AMI(s). By default this description is empty. This is a
|
||||||
|
[configuration template](/docs/templates/configuration-templates.html)
|
||||||
|
where the `SourceAMI` variable is replaced with the source AMI ID and
|
||||||
|
`BuildRegion` variable is replaced with the value of `region`.
|
||||||
|
|
||||||
|
- `ami_groups` (array of strings) - A list of groups that have access to
|
||||||
|
launch the resulting AMI(s). By default no groups have permission to launch
|
||||||
|
the AMI. `all` will make the AMI publicly accessible. AWS currently doesn't
|
||||||
|
accept any value other than `all`.
|
||||||
|
|
||||||
|
- `ami_product_codes` (array of strings) - A list of product codes to
|
||||||
|
associate with the AMI. By default no product codes are associated with
|
||||||
|
the AMI.
|
||||||
|
|
||||||
|
- `ami_regions` (array of strings) - A list of regions to copy the AMI to.
|
||||||
|
Tags and attributes are copied along with the AMI. AMI copying takes time
|
||||||
|
depending on the size of the AMI, but will generally take many minutes.
|
||||||
|
|
||||||
|
- `ami_users` (array of strings) - A list of account IDs that have access to
|
||||||
|
launch the resulting AMI(s). By default no additional users other than the
|
||||||
|
user creating the AMI has permissions to launch it.
|
||||||
|
|
||||||
|
- `ami_virtualization_type` (string) - The type of virtualization for the AMI
|
||||||
|
you are building. This option must match the supported virtualization
|
||||||
|
type of `source_ami`. Can be `paravirtual` or `hvm`.
|
||||||
|
|
||||||
|
- `associate_public_ip_address` (boolean) - If using a non-default VPC, public
|
||||||
|
IP addresses are not provided by default. If this is toggled, your new
|
||||||
|
instance will get a Public IP.
|
||||||
|
|
||||||
|
- `availability_zone` (string) - Destination availability zone to launch
|
||||||
|
instance in. Leave this empty to allow Amazon to auto-assign.
|
||||||
|
|
||||||
|
- `disable_stop_instance` (boolean) - Packer normally stops the build instance
|
||||||
|
after all provisioners have run. For Windows instances, it is sometimes
|
||||||
|
desirable to [run Sysprep](http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ami-create-standard.html)
|
||||||
|
which will stop the instance for you. If this is set to true, Packer *will not*
|
||||||
|
stop the instance and will wait for you to stop it manually. You can do this
|
||||||
|
with a [windows-shell provisioner](https://www.packer.io/docs/provisioners/windows-shell.html).
|
||||||
|
|
||||||
|
``` {.javascript}
|
||||||
|
{
|
||||||
|
"type": "windows-shell",
|
||||||
|
"inline": ["\"c:\\Program Files\\Amazon\\Ec2ConfigService\\ec2config.exe\" -sysprep"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `ebs_optimized` (boolean) - Mark instance as [EBS
|
||||||
|
Optimized](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSOptimized.html).
|
||||||
|
Default `false`.
|
||||||
|
|
||||||
|
- `enhanced_networking` (boolean) - Enable enhanced
|
||||||
|
networking (SriovNetSupport) on HVM-compatible AMIs. If true, add
|
||||||
|
`ec2:ModifyInstanceAttribute` to your AWS IAM policy.
|
||||||
|
|
||||||
|
- `force_deregister` (boolean) - Force Packer to first deregister an existing
|
||||||
|
AMI if one with the same name already exists. Default `false`.
|
||||||
|
|
||||||
|
- `force_delete_snapshot` (boolean) - Force Packer to delete snapshots associated with
|
||||||
|
AMIs, which have been deregistered by `force_deregister`. Default `false`.
|
||||||
|
|
||||||
|
- `encrypt_boot` (boolean) - Instruct packer to automatically create a copy of the
|
||||||
|
AMI with an encrypted boot volume (discarding the initial unencrypted AMI in the
|
||||||
|
process). Default `false`.
|
||||||
|
|
||||||
|
- `kms_key_id` (string) - The ID of the KMS key to use for boot volume encryption.
|
||||||
|
This only applies to the main `region`, other regions where the AMI will be copied
|
||||||
|
will be encrypted by the default EBS KMS key.
|
||||||
|
|
||||||
|
- `iam_instance_profile` (string) - The name of an [IAM instance
|
||||||
|
profile](https://docs.aws.amazon.com/IAM/latest/UserGuide/instance-profiles.html)
|
||||||
|
to launch the EC2 instance with.
|
||||||
|
|
||||||
|
- `launch_block_device_mappings` (array of block device mappings) - Add one or
|
||||||
|
more block devices before the packer build starts. These are not necessarily
|
||||||
|
preserved when booting from the AMI built with packer. See
|
||||||
|
`ami_block_device_mappings`, above, for details.
|
||||||
|
|
||||||
|
- `run_tags` (object of key/value strings) - Tags to apply to the instance
|
||||||
|
that is *launched* to create the AMI. These tags are *not* applied to the
|
||||||
|
resulting AMI unless they're duplicated in `tags`. This is a
|
||||||
|
[configuration template](/docs/templates/configuration-templates.html)
|
||||||
|
where the `SourceAMI` variable is replaced with the source AMI ID and
|
||||||
|
`BuildRegion` variable is replaced with the value of `region`.
|
||||||
|
|
||||||
|
- `run_volume_tags` (object of key/value strings) - Tags to apply to the volumes
|
||||||
|
that are *launched* to create the AMI. These tags are *not* applied to the
|
||||||
|
resulting AMI unless they're duplicated in `tags`. This is a
|
||||||
|
[configuration template](/docs/templates/configuration-templates.html)
|
||||||
|
where the `SourceAMI` variable is replaced with the source AMI ID and
|
||||||
|
`BuildRegion` variable is replaced with the value of `region`.
|
||||||
|
|
||||||
|
- `security_group_id` (string) - The ID (*not* the name) of the security group
|
||||||
|
to assign to the instance. By default this is not set and Packer will
|
||||||
|
automatically create a new temporary security group to allow SSH access.
|
||||||
|
Note that if this is specified, you must be sure the security group allows
|
||||||
|
access to the `ssh_port` given below.
|
||||||
|
|
||||||
|
- `security_group_ids` (array of strings) - A list of security groups as
|
||||||
|
described above. Note that if this is specified, you must omit the
|
||||||
|
`security_group_id`.
|
||||||
|
|
||||||
|
- `shutdown_behavior` (string) - Automatically terminate instances on shutdown
|
||||||
|
incase packer exits ungracefully. Possible values are "stop" and "terminate",
|
||||||
|
default is `stop`.
|
||||||
|
|
||||||
|
- `skip_region_validation` (boolean) - Set to true if you want to skip
|
||||||
|
validation of the region configuration option. Default `false`.
|
||||||
|
|
||||||
|
- `snapshot_groups` (array of strings) - A list of groups that have access to
|
||||||
|
create volumes from the snapshot(s). By default no groups have permission to create
|
||||||
|
volumes form the snapshot(s). `all` will make the snapshot publicly accessible.
|
||||||
|
|
||||||
|
- `snapshot_users` (array of strings) - A list of account IDs that have access to
|
||||||
|
create volumes from the snapshot(s). By default no additional users other than the
|
||||||
|
user creating the AMI has permissions to create volumes from the backing snapshot(s).
|
||||||
|
|
||||||
|
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
|
||||||
|
They will override AMI tags if already applied to snapshot. This is a
|
||||||
|
[configuration template](/docs/templates/configuration-templates.html)
|
||||||
|
where the `SourceAMI` variable is replaced with the source AMI ID and
|
||||||
|
`BuildRegion` variable is replaced with the value of `region`.
|
||||||
|
|
||||||
|
- `source_ami_filter` (object) - Filters used to populate the `source_ami` field.
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` {.javascript}
|
||||||
|
"source_ami_filter": {
|
||||||
|
"filters": {
|
||||||
|
"virtualization-type": "hvm",
|
||||||
|
"name": "*ubuntu-xenial-16.04-amd64-server-*",
|
||||||
|
"root-device-type": "ebs"
|
||||||
|
},
|
||||||
|
"owners": ["099720109477"],
|
||||||
|
"most_recent": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This selects the most recent Ubuntu 16.04 HVM EBS AMI from Canonical.
|
||||||
|
NOTE: This will fail unless *exactly* one AMI is returned. In the above
|
||||||
|
example, `most_recent` will cause this to succeed by selecting the newest image.
|
||||||
|
|
||||||
|
- `filters` (map of strings) - filters used to select a `source_ami`.
|
||||||
|
NOTE: This will fail unless *exactly* one AMI is returned.
|
||||||
|
Any filter described in the docs for [DescribeImages](http://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html)
|
||||||
|
is valid.
|
||||||
|
|
||||||
|
- `owners` (array of strings) - This scopes the AMIs to certain Amazon account IDs.
|
||||||
|
This is helpful to limit the AMIs to a trusted third party, or to your own account.
|
||||||
|
|
||||||
|
- `most_recent` (bool) - Selects the newest created image when true.
|
||||||
|
This is most useful for selecting a daily distro build.
|
||||||
|
|
||||||
|
- `spot_price` (string) - The maximum hourly price to pay for a spot instance
|
||||||
|
to create the AMI. Spot instances are a type of instance that EC2 starts
|
||||||
|
when the current spot price is less than the maximum price you specify. Spot
|
||||||
|
price will be updated based on available spot instance capacity and current
|
||||||
|
spot instance requests. It may save you some costs. You can set this to
|
||||||
|
`auto` for Packer to automatically discover the best spot price or to "0"
|
||||||
|
to use an on demand instance (default).
|
||||||
|
|
||||||
|
- `spot_price_auto_product` (string) - Required if `spot_price` is set
|
||||||
|
to `auto`. This tells Packer what sort of AMI you're launching to find the
|
||||||
|
best spot price. This must be one of: `Linux/UNIX`, `SUSE Linux`, `Windows`,
|
||||||
|
`Linux/UNIX (Amazon VPC)`, `SUSE Linux (Amazon VPC)`, `Windows (Amazon VPC)`
|
||||||
|
|
||||||
|
- `ssh_keypair_name` (string) - If specified, this is the key that will be
|
||||||
|
used for SSH with the machine. The key must match a key pair name loaded
|
||||||
|
up into Amazon EC2. By default, this is blank, and Packer will
|
||||||
|
generate a temporary keypair unless
|
||||||
|
[`ssh_password`](/docs/templates/communicator.html#ssh_password) is used.
|
||||||
|
[`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file)
|
||||||
|
or `ssh_agent_auth` must be specified when `ssh_keypair_name` is utilized.
|
||||||
|
|
||||||
|
- `ssh_agent_auth` (boolean) - If true, the local SSH agent will be used to
|
||||||
|
authenticate connections to the source instance. No temporary keypair will
|
||||||
|
be created, and the values of `ssh_password` and `ssh_private_key_file` will
|
||||||
|
be ignored. To use this option with a key pair already configured in the source
|
||||||
|
AMI, leave the `ssh_keypair_name` blank. To associate an existing key pair
|
||||||
|
in AWS with the source instance, set the `ssh_keypair_name` field to the name
|
||||||
|
of the key pair.
|
||||||
|
|
||||||
|
- `ssh_private_ip` (boolean) - If true, then SSH will always use the private
|
||||||
|
IP if available.
|
||||||
|
|
||||||
|
- `subnet_id` (string) - If using VPC, the ID of the subnet, such as
|
||||||
|
`subnet-12345def`, where Packer will launch the EC2 instance. This field is
|
||||||
|
required if you are using an non-default VPC.
|
||||||
|
|
||||||
|
- `tags` (object of key/value strings) - Tags applied to the AMI and
|
||||||
|
relevant snapshots. This is a
|
||||||
|
[configuration template](/docs/templates/configuration-templates.html)
|
||||||
|
where the `SourceAMI` variable is replaced with the source AMI ID and
|
||||||
|
`BuildRegion` variable is replaced with the value of `region`.
|
||||||
|
|
||||||
|
- `temporary_key_pair_name` (string) - The name of the temporary keypair
|
||||||
|
to generate. By default, Packer generates a name with a UUID.
|
||||||
|
|
||||||
|
- `token` (string) - The access token to use. This is different from the
|
||||||
|
access key and secret key. If you're not sure what this is, then you
|
||||||
|
probably don't need it. This will also be read from the `AWS_SESSION_TOKEN`
|
||||||
|
environmental variable.
|
||||||
|
|
||||||
|
- `user_data` (string) - User data to apply when launching the instance. Note
|
||||||
|
that you need to be careful about escaping characters due to the templates
|
||||||
|
being JSON. It is often more convenient to use `user_data_file`, instead.
|
||||||
|
|
||||||
|
- `user_data_file` (string) - Path to a file that will be used for the user
|
||||||
|
data when launching the instance.
|
||||||
|
|
||||||
|
- `vpc_id` (string) - If launching into a VPC subnet, Packer needs the VPC ID
|
||||||
|
in order to create a temporary security group within the VPC. Requires `subnet_id`
|
||||||
|
to be set.
|
||||||
|
|
||||||
|
- `windows_password_timeout` (string) - The timeout for waiting for a Windows
|
||||||
|
password for Windows instances. Defaults to 20 minutes. Example value: `10m`
|
||||||
|
|
||||||
|
## Basic Example
|
||||||
|
|
||||||
|
Here is a basic example. You will need to provide access keys, and may need to
|
||||||
|
change the AMI IDs according to what images exist at the time the template is run:
|
||||||
|
|
||||||
|
``` {.javascript}
|
||||||
|
{
|
||||||
|
"type": "amazon-ebs",
|
||||||
|
"access_key": "YOUR KEY HERE",
|
||||||
|
"secret_key": "YOUR SECRET KEY HERE",
|
||||||
|
"region": "us-east-1",
|
||||||
|
"source_ami": "ami-fce3c696",
|
||||||
|
"instance_type": "t2.micro",
|
||||||
|
"ssh_username": "ubuntu",
|
||||||
|
"ami_name": "packer-quick-start {{timestamp}}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
-> **Note:** Packer can also read the access key and secret access key from
|
||||||
|
environmental variables. See the configuration reference in the section above
|
||||||
|
for more information on what environmental variables Packer will look for.
|
||||||
|
|
||||||
|
Further information on locating AMI IDs and their relationship to instance types
|
||||||
|
and regions can be found in the AWS EC2 Documentation
|
||||||
|
[for Linux](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)
|
||||||
|
or [for Windows](http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/finding-an-ami.html).
|
||||||
|
|
||||||
|
## Basic Example
|
||||||
|
|
||||||
|
``` {.javascript}
|
||||||
|
{
|
||||||
|
"type" : "amazon-surrogate",
|
||||||
|
"secret_key" : "YOUR SECRET KEY HERE",
|
||||||
|
"access_key" : "YOUR KEY HERE",
|
||||||
|
"region" : "us-east-1",
|
||||||
|
"ssh_username" : "ubuntu",
|
||||||
|
"instance_type" : "t2.medium",
|
||||||
|
"source_ami" : "ami-40d28157",
|
||||||
|
"launch_block_device_mappings" : [
|
||||||
|
{
|
||||||
|
"volume_type" : "gp2",
|
||||||
|
"device_name" : "/dev/xvdf",
|
||||||
|
"delete_on_termination" : false,
|
||||||
|
"volume_size" : 10
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"ami_root_device": {
|
||||||
|
"source_device_name": "/dev/xvdf",
|
||||||
|
"device_name": "/dev/xvda",
|
||||||
|
"delete_on_termination": true,
|
||||||
|
"volume_size": 16,
|
||||||
|
"volume_type": "gp2"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
-> **Note:** Packer can also read the access key and secret access key from
|
||||||
|
environmental variables. See the configuration reference in the section above
|
||||||
|
for more information on what environmental variables Packer will look for.
|
||||||
|
|
||||||
|
Further information on locating AMI IDs and their relationship to instance
|
||||||
|
types and regions can be found in the AWS EC2 Documentation
|
||||||
|
[for Linux](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)
|
||||||
|
or [for Windows](http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/finding-an-ami.html).
|
||||||
|
|
||||||
|
## Accessing the Instance to Debug
|
||||||
|
|
||||||
|
If you need to access the instance to debug for some reason, run the builder
|
||||||
|
with the `-debug` flag. In debug mode, the Amazon builder will save the private
|
||||||
|
key in the current directory and will output the DNS or IP information as well.
|
||||||
|
You can use this information to access the instance as it is running.
|
||||||
|
|
||||||
|
-> **Note:** Packer uses pre-built AMIs as the source for building images.
|
||||||
|
These source AMIs may include volumes that are not flagged to be destroyed on
|
||||||
|
termination of the instance building the new image. In addition to those volumes
|
||||||
|
created by this builder, any volumes inn the source AMI which are not marked for
|
||||||
|
deletion on termination will remain in your account.
|
|
@ -28,6 +28,11 @@ Packer supports the following builders at the moment:
|
||||||
newcomers**. However, it is also the fastest way to build an EBS-backed AMI
|
newcomers**. However, it is also the fastest way to build an EBS-backed AMI
|
||||||
since no new EC2 instance needs to be launched.
|
since no new EC2 instance needs to be launched.
|
||||||
|
|
||||||
|
- [amazon-ebssurrogate](/docs/builders/amazone-ebssurrogate.html) - Create EBS
|
||||||
|
-backed AMIs from scratch. Works similarly to the `chroot` builder but does
|
||||||
|
not require running in AWS. This is an **advanced builder and should not be
|
||||||
|
used by newcomers**.
|
||||||
|
|
||||||
-> **Don't know which builder to use?** If in doubt, use the [amazon-ebs
|
-> **Don't know which builder to use?** If in doubt, use the [amazon-ebs
|
||||||
builder](/docs/builders/amazon-ebs.html). It is much easier to use and Amazon
|
builder](/docs/builders/amazon-ebs.html). It is much easier to use and Amazon
|
||||||
generally recommends EBS-backed images nowadays.
|
generally recommends EBS-backed images nowadays.
|
||||||
|
|
Loading…
Reference in New Issue