github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/builder/amazon/ebs/step_encrypted_ami.go (about)

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