From e85633930920baa7c326e9caa9df2a38943c597b Mon Sep 17 00:00:00 2001 From: James Nugent Date: Mon, 27 Feb 2017 07:51:38 -0600 Subject: [PATCH] build/amazon-ebssurrogate: Add region copy, attributes, tags steps As pointed out in the initial code review of #4351, some of the steps from the standard EBS builder were (intetionally) omitted. It turns out that these actually are useful, and the original rationale for the omission was wrong. Consequently, this commit adds in the following steps: - `StepPrevalidate` - `StepTagEBSVolumes` - `StepDeregisterAMI` - `StepCreateEncryptedAMICopy` - `StepAMIRegionCopy` - `StepModifyAMIAttribute` - `StepCreateTags` We also fix the interpolation filter and documentation to reflect these additions, though the majority were already documented and just not functional. --- .../{ebs => common}/step_tag_ebs_volumes.go | 11 +++-- builder/amazon/ebs/builder.go | 2 +- builder/amazon/ebssurrogate/builder.go | 42 ++++++++++++++++++- .../amazon/ebssurrogate/step_register_ami.go | 41 +++++++++++++++++- .../docs/builders/amazon-ebssurrogate.html.md | 8 ++++ 5 files changed, 95 insertions(+), 9 deletions(-) rename builder/amazon/{ebs => common}/step_tag_ebs_volumes.go (80%) diff --git a/builder/amazon/ebs/step_tag_ebs_volumes.go b/builder/amazon/common/step_tag_ebs_volumes.go similarity index 80% rename from builder/amazon/ebs/step_tag_ebs_volumes.go rename to builder/amazon/common/step_tag_ebs_volumes.go index ffadb16ae..9a912600a 100644 --- a/builder/amazon/ebs/step_tag_ebs_volumes.go +++ b/builder/amazon/common/step_tag_ebs_volumes.go @@ -1,21 +1,20 @@ -package ebs +package common import ( "fmt" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/builder/amazon/common" "github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/template/interpolate" ) -type stepTagEBSVolumes struct { +type StepTagEBSVolumes struct { VolumeRunTags map[string]string Ctx interpolate.Context } -func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { +func (s *StepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) instance := state.Get("instance").(*ec2.Instance) sourceAMI := state.Get("source_image").(*ec2.Image) @@ -37,7 +36,7 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { } ui.Say("Adding tags to source EBS Volumes") - tags, err := common.ConvertToEC2Tags(s.VolumeRunTags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx) + tags, err := ConvertToEC2Tags(s.VolumeRunTags, *ec2conn.Config.Region, *sourceAMI.ImageId, s.Ctx) if err != nil { err := fmt.Errorf("Error tagging source EBS Volumes on %s: %s", *instance.InstanceId, err) state.Put("error", err) @@ -59,6 +58,6 @@ func (s *stepTagEBSVolumes) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } -func (s *stepTagEBSVolumes) Cleanup(state multistep.StateBag) { +func (s *StepTagEBSVolumes) Cleanup(state multistep.StateBag) { // No cleanup... } diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index a5ce0cd86..13598b96a 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -149,7 +149,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Ctx: b.config.ctx, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, }, - &stepTagEBSVolumes{ + &awscommon.StepTagEBSVolumes{ VolumeRunTags: b.config.VolumeRunTags, Ctx: b.config.ctx, }, diff --git a/builder/amazon/ebssurrogate/builder.go b/builder/amazon/ebssurrogate/builder.go index 7a6c93239..df2db83c1 100644 --- a/builder/amazon/ebssurrogate/builder.go +++ b/builder/amazon/ebssurrogate/builder.go @@ -28,7 +28,8 @@ type Config struct { awscommon.BlockDevices `mapstructure:",squash"` awscommon.AMIConfig `mapstructure:",squash"` - RootDevice RootBlockDevice `mapstructure:"ami_root_device"` + RootDevice RootBlockDevice `mapstructure:"ami_root_device"` + VolumeRunTags map[string]string `mapstructure:"run_volume_tags"` ctx interpolate.Context } @@ -47,6 +48,8 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) { Exclude: []string{ "ami_description", "run_tags", + "run_volume_tags", + "snapshot_tags", "tags", }, }, @@ -119,6 +122,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps steps := []multistep.Step{ + &awscommon.StepPreValidate{ + DestAmiName: b.config.AMIName, + ForceDeregister: b.config.AMIForceDeregister, + }, &awscommon.StepSourceAMIInfo{ SourceAmi: b.config.SourceAmi, EnhancedNetworking: b.config.AMIEnhancedNetworking, @@ -155,6 +162,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Tags: b.config.RunTags, InstanceInitiatedShutdownBehavior: b.config.InstanceInitiatedShutdownBehavior, }, + &awscommon.StepTagEBSVolumes{ + VolumeRunTags: b.config.VolumeRunTags, + Ctx: b.config.ctx, + }, &awscommon.StepGetPassword{ Debug: b.config.PackerDebug, Comm: &b.config.RunConfig.Comm, @@ -181,10 +192,39 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &StepSnapshotNewRootVolume{ NewRootMountPoint: b.config.RootDevice.SourceDeviceName, }, + &awscommon.StepDeregisterAMI{ + ForceDeregister: b.config.AMIForceDeregister, + ForceDeleteSnapshot: b.config.AMIForceDeleteSnapshot, + AMIName: b.config.AMIName, + }, &StepRegisterAMI{ RootDevice: b.config.RootDevice, BlockDevices: b.config.BlockDevices.BuildLaunchDevices(), }, + &awscommon.StepCreateEncryptedAMICopy{ + KeyID: b.config.AMIKmsKeyId, + EncryptBootVolume: b.config.AMIEncryptBootVolume, + Name: b.config.AMIName, + }, + &awscommon.StepAMIRegionCopy{ + AccessConfig: &b.config.AccessConfig, + Regions: b.config.AMIRegions, + Name: b.config.AMIName, + }, + &awscommon.StepModifyAMIAttributes{ + Description: b.config.AMIDescription, + Users: b.config.AMIUsers, + Groups: b.config.AMIGroups, + ProductCodes: b.config.AMIProductCodes, + SnapshotUsers: b.config.SnapshotUsers, + SnapshotGroups: b.config.SnapshotGroups, + Ctx: b.config.ctx, + }, + &awscommon.StepCreateTags{ + Tags: b.config.AMITags, + SnapshotTags: b.config.SnapshotTags, + Ctx: b.config.ctx, + }, } // Run! diff --git a/builder/amazon/ebssurrogate/step_register_ami.go b/builder/amazon/ebssurrogate/step_register_ami.go index 975854b96..dce4f47a8 100644 --- a/builder/amazon/ebssurrogate/step_register_ami.go +++ b/builder/amazon/ebssurrogate/step_register_ami.go @@ -14,6 +14,7 @@ import ( type StepRegisterAMI struct { RootDevice RootBlockDevice BlockDevices []*ec2.BlockDeviceMapping + image *ec2.Image } func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { @@ -74,7 +75,45 @@ func (s *StepRegisterAMI) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{registerResp.ImageId}}) + if err != nil { + err := fmt.Errorf("Error searching for AMI: %s", err) + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt + } + s.image = imagesResp.Images[0] + + snapshots := make(map[string][]string) + for _, blockDeviceMapping := range imagesResp.Images[0].BlockDeviceMappings { + if blockDeviceMapping.Ebs != nil && blockDeviceMapping.Ebs.SnapshotId != nil { + + snapshots[*ec2conn.Config.Region] = append(snapshots[*ec2conn.Config.Region], *blockDeviceMapping.Ebs.SnapshotId) + } + } + state.Put("snapshots", snapshots) + return multistep.ActionContinue } -func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) {} +func (s *StepRegisterAMI) Cleanup(state multistep.StateBag) { + if s.image == nil { + return + } + + _, cancelled := state.GetOk(multistep.StateCancelled) + _, halted := state.GetOk(multistep.StateHalted) + if !cancelled && !halted { + return + } + + ec2conn := state.Get("ec2").(*ec2.EC2) + ui := state.Get("ui").(packer.Ui) + + ui.Say("Deregistering the AMI because cancelation or error...") + deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId} + if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil { + ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err)) + return + } +} diff --git a/website/source/docs/builders/amazon-ebssurrogate.html.md b/website/source/docs/builders/amazon-ebssurrogate.html.md index beb676071..6dae386a8 100644 --- a/website/source/docs/builders/amazon-ebssurrogate.html.md +++ b/website/source/docs/builders/amazon-ebssurrogate.html.md @@ -163,6 +163,14 @@ builder. - `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.