github.com/amanya/packer@v0.12.1-0.20161117214323-902ac5ab2eb6/builder/amazon/ebs/step_encrypted_ami.go (about)

     1  package ebs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/aws/aws-sdk-go/aws"
     7  	"github.com/aws/aws-sdk-go/service/ec2"
     8  	"github.com/mitchellh/multistep"
     9  	awscommon "github.com/mitchellh/packer/builder/amazon/common"
    10  	"github.com/mitchellh/packer/packer"
    11  )
    12  
    13  type stepCreateEncryptedAMICopy struct {
    14  	image *ec2.Image
    15  }
    16  
    17  func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.StepAction {
    18  	config := state.Get("config").(Config)
    19  	ec2conn := state.Get("ec2").(*ec2.EC2)
    20  	ui := state.Get("ui").(packer.Ui)
    21  
    22  	// Encrypt boot not set, so skip step
    23  	if !config.AMIConfig.AMIEncryptBootVolume {
    24  		return multistep.ActionContinue
    25  	}
    26  
    27  	ui.Say("Creating Encrypted AMI Copy")
    28  
    29  	amis := state.Get("amis").(map[string]string)
    30  	var region, id string
    31  	if amis != nil {
    32  		for region, id = range amis {
    33  			break // Only get the first
    34  		}
    35  	}
    36  
    37  	ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id))
    38  
    39  	copyOpts := &ec2.CopyImageInput{
    40  		Name:          &config.AMIName, // Try to overwrite existing AMI
    41  		SourceImageId: aws.String(id),
    42  		SourceRegion:  aws.String(region),
    43  		Encrypted:     aws.Bool(true),
    44  	}
    45  
    46  	copyResp, err := ec2conn.CopyImage(copyOpts)
    47  	if err != nil {
    48  		err := fmt.Errorf("Error copying AMI: %s", err)
    49  		state.Put("error", err)
    50  		ui.Error(err.Error())
    51  		return multistep.ActionHalt
    52  	}
    53  
    54  	// Wait for the copy to become ready
    55  	stateChange := awscommon.StateChangeConf{
    56  		Pending:   []string{"pending"},
    57  		Target:    "available",
    58  		Refresh:   awscommon.AMIStateRefreshFunc(ec2conn, *copyResp.ImageId),
    59  		StepState: state,
    60  	}
    61  
    62  	ui.Say("Waiting for AMI copy to become ready...")
    63  	if _, err := awscommon.WaitForState(&stateChange); err != nil {
    64  		err := fmt.Errorf("Error waiting for AMI Copy: %s", err)
    65  		state.Put("error", err)
    66  		ui.Error(err.Error())
    67  		return multistep.ActionHalt
    68  	}
    69  
    70  	// Get the unencrypted AMI image
    71  	unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}})
    72  	if err != nil {
    73  		err := fmt.Errorf("Error searching for AMI: %s", err)
    74  		state.Put("error", err)
    75  		ui.Error(err.Error())
    76  		return multistep.ActionHalt
    77  	}
    78  	unencImage := unencImagesResp.Images[0]
    79  
    80  	// Remove unencrypted AMI
    81  	ui.Say("Deregistering unencrypted AMI")
    82  	deregisterOpts := &ec2.DeregisterImageInput{ImageId: aws.String(id)}
    83  	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
    84  		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
    85  		return multistep.ActionHalt
    86  	}
    87  
    88  	// Remove associated unencrypted snapshot(s)
    89  	ui.Say("Deleting unencrypted snapshots")
    90  
    91  	for _, blockDevice := range unencImage.BlockDeviceMappings {
    92  		if blockDevice.Ebs != nil {
    93  			if blockDevice.Ebs.SnapshotId != nil {
    94  				ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
    95  				deleteSnapOpts := &ec2.DeleteSnapshotInput{
    96  					SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
    97  				}
    98  				if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil {
    99  					ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err))
   100  					return multistep.ActionHalt
   101  				}
   102  			}
   103  		}
   104  	}
   105  
   106  	// Replace original AMI ID with Encrypted ID in state
   107  	amis[region] = *copyResp.ImageId
   108  	state.Put("amis", amis)
   109  
   110  	imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}})
   111  	if err != nil {
   112  		err := fmt.Errorf("Error searching for AMI: %s", err)
   113  		state.Put("error", err)
   114  		ui.Error(err.Error())
   115  		return multistep.ActionHalt
   116  	}
   117  	s.image = imagesResp.Images[0]
   118  
   119  	return multistep.ActionContinue
   120  }
   121  
   122  func (s *stepCreateEncryptedAMICopy) Cleanup(state multistep.StateBag) {
   123  	if s.image == nil {
   124  		return
   125  	}
   126  
   127  	_, cancelled := state.GetOk(multistep.StateCancelled)
   128  	_, halted := state.GetOk(multistep.StateHalted)
   129  	if !cancelled && !halted {
   130  		return
   131  	}
   132  
   133  	ec2conn := state.Get("ec2").(*ec2.EC2)
   134  	ui := state.Get("ui").(packer.Ui)
   135  
   136  	ui.Say("Deregistering the AMI because cancelation or error...")
   137  	deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId}
   138  	if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil {
   139  		ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err))
   140  		return
   141  	}
   142  }