diff --git a/builder/amazon/ebs/step_modify_instance.go b/builder/amazon/common/step_modify_ebs_instance.go similarity index 74% rename from builder/amazon/ebs/step_modify_instance.go rename to builder/amazon/common/step_modify_ebs_instance.go index b9976db6d..ab0cd9e6c 100644 --- a/builder/amazon/ebs/step_modify_instance.go +++ b/builder/amazon/common/step_modify_ebs_instance.go @@ -1,4 +1,4 @@ -package ebs +package common import ( "fmt" @@ -8,16 +8,17 @@ import ( "github.com/mitchellh/packer/packer" ) -type stepModifyInstance struct{} +type StepModifyEBSBackedInstance struct { + EnableEnhancedNetworking bool +} -func (s *stepModifyInstance) Run(state multistep.StateBag) multistep.StepAction { - config := state.Get("config").(Config) +func (s *StepModifyEBSBackedInstance) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) // Set SriovNetSupport to "simple". See http://goo.gl/icuXh5 - if config.AMIEnhancedNetworking { + if s.EnableEnhancedNetworking { ui.Say("Enabling Enhanced Networking...") simple := "simple" _, err := ec2conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{ @@ -35,6 +36,6 @@ func (s *stepModifyInstance) Run(state multistep.StateBag) multistep.StepAction return multistep.ActionContinue } -func (s *stepModifyInstance) Cleanup(state multistep.StateBag) { +func (s *StepModifyEBSBackedInstance) Cleanup(state multistep.StateBag) { // No cleanup... } diff --git a/builder/amazon/ebs/step_stop_instance.go b/builder/amazon/common/step_stop_ebs_instance.go similarity index 76% rename from builder/amazon/ebs/step_stop_instance.go rename to builder/amazon/common/step_stop_ebs_instance.go index a704406f8..cca60e75f 100644 --- a/builder/amazon/ebs/step_stop_instance.go +++ b/builder/amazon/common/step_stop_ebs_instance.go @@ -1,20 +1,19 @@ -package ebs +package common import ( "fmt" "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" ) -type stepStopInstance struct { +type StepStopEBSBackedInstance struct { SpotPrice string DisableStopInstance bool } -func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction { +func (s *StepStopEBSBackedInstance) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) ui := state.Get("ui").(packer.Ui) @@ -44,13 +43,13 @@ func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction { // Wait for the instance to actual stop ui.Say("Waiting for the instance to stop...") - stateChange := awscommon.StateChangeConf{ + stateChange := StateChangeConf{ Pending: []string{"running", "stopping"}, Target: "stopped", - Refresh: awscommon.InstanceStateRefreshFunc(ec2conn, *instance.InstanceId), + Refresh: InstanceStateRefreshFunc(ec2conn, *instance.InstanceId), StepState: state, } - _, err = awscommon.WaitForState(&stateChange) + _, err = WaitForState(&stateChange) if err != nil { err := fmt.Errorf("Error waiting for instance to stop: %s", err) state.Put("error", err) @@ -61,6 +60,6 @@ func (s *stepStopInstance) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepStopInstance) Cleanup(multistep.StateBag) { +func (s *StepStopEBSBackedInstance) Cleanup(multistep.StateBag) { // No cleanup... } diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index c0ab2e44d..980a3fe61 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -154,12 +154,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe b.config.RunConfig.Comm.SSHPassword), }, &common.StepProvision{}, - &stepStopInstance{ + &awscommon.StepStopEBSBackedInstance{ SpotPrice: b.config.SpotPrice, DisableStopInstance: b.config.DisableStopInstance, }, - // TODO(mitchellh): verify works with spots - &stepModifyInstance{}, + &awscommon.StepModifyEBSBackedInstance{ + EnableEnhancedNetworking: b.config.AMIEnhancedNetworking, + }, &awscommon.StepDeregisterAMI{ ForceDeregister: b.config.AMIForceDeregister, AMIName: b.config.AMIName, diff --git a/builder/amazon/ebsvolume/block_device.go b/builder/amazon/ebsvolume/block_device.go new file mode 100644 index 000000000..fab20e0dd --- /dev/null +++ b/builder/amazon/ebsvolume/block_device.go @@ -0,0 +1,23 @@ +package ebsvolume + +import ( + awscommon "github.com/mitchellh/packer/builder/amazon/common" +) + +type BlockDevice struct { + awscommon.BlockDevice `mapstructure:"-,squash"` + Tags map[string]string `mapstructure:"tags"` +} + +func commonBlockDevices(mappings []BlockDevice) awscommon.BlockDevices { + result := make([]awscommon.BlockDevice, len(mappings)) + for i, mapping := range mappings { + result[i] = mapping.BlockDevice + } + + return awscommon.BlockDevices{ + LaunchBlockDevices: awscommon.LaunchBlockDevices{ + LaunchMappings: result, + }, + } +} diff --git a/builder/amazon/ebsvolume/builder.go b/builder/amazon/ebsvolume/builder.go new file mode 100644 index 000000000..22724f10f --- /dev/null +++ b/builder/amazon/ebsvolume/builder.go @@ -0,0 +1,180 @@ +// The ebsvolume package contains a packer.Builder implementation that +// builds EBS volumes for Amazon EC2 using an ephemeral instance, +package ebsvolume + +import ( + "fmt" + "log" + "strings" + + "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" +) + +type Config struct { + common.PackerConfig `mapstructure:",squash"` + awscommon.AccessConfig `mapstructure:",squash"` + awscommon.RunConfig `mapstructure:",squash"` + + VolumeMappings []BlockDevice `mapstructure:"ebs_volumes"` + AMIEnhancedNetworking bool `mapstructure:"enhanced_networking"` + + 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, + }, 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)...) + + 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) { + config, err := b.config.Config() + if err != nil { + return nil, err + } + + session, err := session.NewSession(config) + if err != nil { + return nil, errwrap.Wrapf("Error creating AWS Session: {{err}}", err) + } + + ec2conn := ec2.New(session) + + // 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) + + launchBlockDevices := commonBlockDevices(b.config.VolumeMappings) + + var volumeIds *[]string + + // 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, + 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: launchBlockDevices, + Tags: b.config.RunTags, + InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, + }, + &stepTagEBSVolumes{ + VolumeMapping: b.config.VolumeMappings, + VolumeIDs: &volumeIds, + }, + &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, + }, + } + + // 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) + } + + ui.Say(fmt.Sprintf("Created Volumes: %s", strings.Join(*volumeIds, ", "))) + return nil, nil +} + +func (b *Builder) Cancel() { + if b.runner != nil { + log.Println("Cancelling the step runner...") + b.runner.Cancel() + } +} diff --git a/builder/amazon/ebsvolume/builder_test.go b/builder/amazon/ebsvolume/builder_test.go new file mode 100644 index 000000000..9b1040f01 --- /dev/null +++ b/builder/amazon/ebsvolume/builder_test.go @@ -0,0 +1,91 @@ +package ebsvolume + +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.Fatalf("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.Fatalf("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") + } +} + +func TestBuilderPrepare_InvalidShutdownBehaviour(t *testing.T) { + var b Builder + config := testConfig() + + // Test good + config["shutdown_behaviour"] = "terminate" + warnings, err := b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Test good + config["shutdown_behaviour"] = "stop" + warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err != nil { + t.Fatalf("should not have error: %s", err) + } + + // Test bad + config["shutdown_behaviour"] = "foobar" + warnings, err = b.Prepare(config) + if len(warnings) > 0 { + t.Fatalf("bad: %#v", warnings) + } + if err == nil { + t.Fatal("should have error") + } +} diff --git a/builder/amazon/ebsvolume/step_tag_ebs_volumes.go b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go new file mode 100644 index 000000000..92fb56900 --- /dev/null +++ b/builder/amazon/ebsvolume/step_tag_ebs_volumes.go @@ -0,0 +1,77 @@ +package ebsvolume + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/packer" +) + +type stepTagEBSVolumes struct { + VolumeMapping []BlockDevice + VolumeIDs **[]string +} + +func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { + ec2conn := state.Get("ec2").(*ec2.EC2) + instance := state.Get("instance").(*ec2.Instance) + ui := state.Get("ui").(packer.Ui) + + volumeIds := make([]string, 0, len(s.VolumeMapping)) + for _, instanceBlockDevices := range instance.BlockDeviceMappings { + for _, configVolumeMapping := range s.VolumeMapping { + if configVolumeMapping.DeviceName == *instanceBlockDevices.DeviceName { + volumeIds = append(volumeIds, *instanceBlockDevices.Ebs.VolumeId) + } + } + } + *s.VolumeIDs = &volumeIds + + if len(s.VolumeMapping) > 0 { + ui.Say("Tagging EBS volumes...") + + toTag := map[string][]*ec2.Tag{} + for _, mapping := range s.VolumeMapping { + if len(mapping.Tags) == 0 { + ui.Say(fmt.Sprintf("No tags specified for volume on %s...", mapping.DeviceName)) + continue + } + + tags := make([]*ec2.Tag, 0, len(mapping.Tags)) + for key, value := range mapping.Tags { + tags = append(tags, &ec2.Tag{ + Key: aws.String(fmt.Sprintf("%s", key)), + Value: aws.String(fmt.Sprintf("%s", value)), + }) + } + + for _, v := range instance.BlockDeviceMappings { + if *v.DeviceName == mapping.DeviceName { + toTag[*v.Ebs.VolumeId] = tags + } + } + } + + for volumeId, tags := range toTag { + _, err := ec2conn.CreateTags(&ec2.CreateTagsInput{ + Resources: []*string{&volumeId}, + Tags: tags, + }) + if err != nil { + err := fmt.Errorf("Error tagging EBS Volume %s on %s: %s", volumeId, *instance.InstanceId, err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + + } + } + + return multistep.ActionContinue +} + +func (s *stepTagEBSVolumes) Cleanup(state multistep.StateBag) { + // No cleanup... +} diff --git a/command/plugin.go b/command/plugin.go index 36c9247e7..b20fa0674 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -15,6 +15,7 @@ import ( amazonchrootbuilder "github.com/mitchellh/packer/builder/amazon/chroot" amazonebsbuilder "github.com/mitchellh/packer/builder/amazon/ebs" + amazonebsvolumebuilder "github.com/mitchellh/packer/builder/amazon/ebsvolume" amazoninstancebuilder "github.com/mitchellh/packer/builder/amazon/instance" azurearmbuilder "github.com/mitchellh/packer/builder/azure/arm" cloudstackbuilder "github.com/mitchellh/packer/builder/cloudstack" @@ -67,25 +68,26 @@ type PluginCommand struct { } var Builders = map[string]packer.Builder{ - "amazon-chroot": new(amazonchrootbuilder.Builder), - "amazon-ebs": new(amazonebsbuilder.Builder), - "amazon-instance": new(amazoninstancebuilder.Builder), - "azure-arm": new(azurearmbuilder.Builder), - "cloudstack": new(cloudstackbuilder.Builder), - "digitalocean": new(digitaloceanbuilder.Builder), - "docker": new(dockerbuilder.Builder), - "file": new(filebuilder.Builder), - "googlecompute": new(googlecomputebuilder.Builder), - "null": new(nullbuilder.Builder), - "openstack": new(openstackbuilder.Builder), - "parallels-iso": new(parallelsisobuilder.Builder), - "parallels-pvm": new(parallelspvmbuilder.Builder), - "profitbricks": new(profitbricksbuilder.Builder), - "qemu": new(qemubuilder.Builder), - "virtualbox-iso": new(virtualboxisobuilder.Builder), - "virtualbox-ovf": new(virtualboxovfbuilder.Builder), - "vmware-iso": new(vmwareisobuilder.Builder), - "vmware-vmx": new(vmwarevmxbuilder.Builder), + "amazon-chroot": new(amazonchrootbuilder.Builder), + "amazon-ebs": new(amazonebsbuilder.Builder), + "amazon-ebsvolume": new(amazonebsvolumebuilder.Builder), + "amazon-instance": new(amazoninstancebuilder.Builder), + "azure-arm": new(azurearmbuilder.Builder), + "cloudstack": new(cloudstackbuilder.Builder), + "digitalocean": new(digitaloceanbuilder.Builder), + "docker": new(dockerbuilder.Builder), + "file": new(filebuilder.Builder), + "googlecompute": new(googlecomputebuilder.Builder), + "null": new(nullbuilder.Builder), + "openstack": new(openstackbuilder.Builder), + "parallels-iso": new(parallelsisobuilder.Builder), + "parallels-pvm": new(parallelspvmbuilder.Builder), + "profitbricks": new(profitbricksbuilder.Builder), + "qemu": new(qemubuilder.Builder), + "virtualbox-iso": new(virtualboxisobuilder.Builder), + "virtualbox-ovf": new(virtualboxovfbuilder.Builder), + "vmware-iso": new(vmwareisobuilder.Builder), + "vmware-vmx": new(vmwarevmxbuilder.Builder), } var Provisioners = map[string]packer.Provisioner{ diff --git a/website/source/docs/builders/amazon-ebs-volume.html.md b/website/source/docs/builders/amazon-ebs-volume.html.md new file mode 100644 index 000000000..15d4603e2 --- /dev/null +++ b/website/source/docs/builders/amazon-ebs-volume.html.md @@ -0,0 +1,267 @@ +--- +description: | + The `amazon-ebs-volume` Packer builder is like the EBS builder, but is + intended to create EBS volumes rather than a machine image. +layout: docs +page_title: 'Amazon EBS Volume Builder' +... + +# EBS Volume Builder + +Type: `amazon-ebs-volume` + +The `amazon-ebs-volume` Packer builder is able to create Amazon Elastic Block +Store volumes which are prepopulated with filesystems or data. + +This builder builds EBS volumes by launching an EC2 instance from a source AMI, +provisioning that running machine, and then destroying the source machine, keeping +the volumes intact. + +This is all done in your own AWS account. The builder will create temporary +keypairs, security group rules, etc. that provide it temporary access to the +instance while the image is being created. + +The builder does *not* manage EBS Volumes. Once it creates volumes and stores +it in your account, it is up to you to use, delete, etc. the volumes. + +## 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. + +### Optional: + +- `ebs_volumes` (array of block device mappings) - Add the block + device mappings to the AMI. The block device mappings allow for keys: + + - `device_name` (string) - The device name exposed to the instance (for + example, "/dev/sdh" or "xvdh"). Required when specifying `volume_size`. + - `delete_on_termination` (boolean) - Indicates whether the EBS volume is + deleted on instance termination + - `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 + - `tags` (map) - Tags to apply to the volume. These are retained after the + builder completes. + +- `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. + +- `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. + +- `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. + +- `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`. + +- `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`. + +- `skip_region_validation` (boolean) - Set to true if you want to skip + validation of the region configuration option. Defaults to false. + +- `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. 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) + must be specified with this. + +- `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. + +- `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" + +- `shutdown_behaviour` (string) - Automatically terminate instances on shutdown + incase packer exits ungracefully. Possible values are "stop" and "terminate", + default is stop. + +## Basic Example + +``` +{ + "type" : "amazon-ebsinit", + "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", + "ebs_volumes" : [ + { + "volume_type" : "gp2", + "device_name" : "/dev/xvdf", + "delete_on_termination" : false, + "tags" : { + "zpool" : "data", + "Name" : "Data1" + }, + "volume_size" : 10 + }, + { + "volume_type" : "gp2", + "device_name" : "/dev/xvdg", + "tags" : { + "zpool" : "data", + "Name" : "Data2" + }, + "delete_on_termination" : false, + "volume_size" : 10 + }, + { + "volume_size" : 10, + "tags" : { + "Name" : "Data3", + "zpool" : "data" + }, + "delete_on_termination" : false, + "device_name" : "/dev/xvdh", + "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. diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index 407025dfe..81cb1911b 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -32,6 +32,15 @@ Packer supports the following builders at the moment: builder](/docs/builders/amazon-ebs.html). It is much easier to use and Amazon generally recommends EBS-backed images nowadays. +# Amazon EBS Volume Builder + +Packer is able to create Amazon EBS Volumes which are preinitialized with a +filesystem and data. + +- [amazon-ebs](/docs/builders/amazon-ebsv-volume.html) - Create EBS volumes + by launching a source AMI with block devices mapped. Provision the instance, + then destroy it, retaining the EBS volumes. + ## Specifying Amazon Credentials