コード例 #1
0
ファイル: simpledbs.go プロジェクト: murdinc/awsm
// GetSimpleDBDomains returns a slice of SimpleDB Domains that match the provided search term
func GetSimpleDBDomains(search string) (*SimpleDBDomains, []error) {
	var wg sync.WaitGroup
	var errs []error

	domainList := new(SimpleDBDomains)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionSimpleDBDomains(region.RegionName, domainList, search)
			if err != nil {
				// TODO handle regions without service endpoints that work
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering simpledb domain list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}

	wg.Wait()

	return domainList, errs
}
コード例 #2
0
ファイル: launchconfigurations.go プロジェクト: murdinc/awsm
// RotateLaunchConfigurations rotates out older Launch Configurations
func RotateLaunchConfigurations(class string, cfg config.LaunchConfigurationClass, dryRun bool) error {
	var wg sync.WaitGroup
	var errs []error

	autoScaleGroups, err := GetAutoScaleGroups("")
	if err != nil {
		return errors.New("Error while retrieving the list of launch configurations to exclude from rotation!")
	}
	excludedConfigs := autoScaleGroups.LockedLaunchConfigurations()

	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()

			// Get all the launch configs of this class in this region
			launchConfigs, err := GetLaunchConfigurationsByName(*region.RegionName, class)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering launch configuration list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}

			// Exclude the launch configs being used in Autoscale Groups
			for i, lc := range launchConfigs {
				if excludedConfigs[lc.Name] {
					terminal.Information("Launch Configuration [" + lc.Name + ") ] is being used in an autoscale group, skipping!")
					launchConfigs = append(launchConfigs[:i], launchConfigs[i+1:]...)
				}
			}

			// Delete the oldest ones if we have more than the retention number
			if len(launchConfigs) > cfg.Retain {
				sort.Sort(launchConfigs) // important!
				ds := launchConfigs[cfg.Retain:]
				deleteLaunchConfigurations(&ds, dryRun)
			}

		}(region)
	}
	wg.Wait()

	if errs != nil {
		return errors.New("Error rotating snapshots for [" + class + "]!")
	}

	return nil
}
コード例 #3
0
ファイル: snapshots.go プロジェクト: murdinc/awsm
// rotateSnapshots rotates out older Snapshots
func rotateSnapshots(class string, cfg config.SnapshotClass, dryRun bool) error {
	var wg sync.WaitGroup
	var errs []error

	launchConfigs, err := GetLaunchConfigurations("")
	if err != nil {
		return errors.New("Error while retrieving the list of assets to exclude from rotation!")
	}
	excludedSnaps := launchConfigs.LockedSnapshotIds()

	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()

			// Get all the snapshots of this class in this region
			snapshots, err := GetSnapshotsByTag(*region.RegionName, "Class", class)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering snapshot list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}

			// Exclude the snapshots being used in Launch Configurations
			for i, snap := range snapshots {
				if excludedSnaps[snap.SnapshotID] {
					terminal.Information("Snapshot [" + snap.Name + " (" + snap.SnapshotID + ") ] is being used in a launch configuration, skipping!")
					snapshots = append(snapshots[:i], snapshots[i+1:]...)
				}
			}

			// Delete the oldest ones if we have more than the retention number
			if len(snapshots) > cfg.Retain {
				sort.Sort(snapshots) // important!
				ds := snapshots[cfg.Retain:]
				deleteSnapshots(&ds, dryRun)
			}

		}(region)
	}
	wg.Wait()

	if errs != nil {
		return errors.New("Error rotating snapshots for [" + class + "]!")
	}

	return nil
}
コード例 #4
0
ファイル: iam.go プロジェクト: murdinc/awsm
// GetIAMUsers returns a list of IAM Users that match the provided search term
func GetIAMUsers(search string) (*IAMUsers, error) {
	svc := iam.New(session.New())
	result, err := svc.ListUsers(&iam.ListUsersInput{}) // TODO truncated?

	if err != nil {
		terminal.ShowErrorMessage("Error gathering IAM Users list", err.Error())
		return &IAMUsers{}, err
	}

	iamList := make(IAMUsers, len(result.Users))
	for i, user := range result.Users {
		iamList[i].Marshal(user)
	}

	return &iamList, nil
}
コード例 #5
0
ファイル: loadbalancersV2.go プロジェクト: murdinc/awsm
// PrintTable Prints an ascii table of the list of Application Load Balancers
func (i *LoadBalancersV2) PrintTable() {
	if len(*i) == 0 {
		terminal.ShowErrorMessage("Warning", "No Application Load Balancers Found!")
		return
	}

	var header []string
	rows := make([][]string, len(*i))

	for index, lb := range *i {
		models.ExtractAwsmTable(index, lb, &header, &rows)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
コード例 #6
0
ファイル: simpledbs.go プロジェクト: murdinc/awsm
// PrintTable Prints an ascii table of the list of SimpleDB Domains
func (i *SimpleDBDomains) PrintTable() {
	if len(*i) == 0 {
		terminal.ShowErrorMessage("Warning", "No SimpleDB Domains Found!")
		return
	}

	var header []string
	rows := make([][]string, len(*i))

	for index, domain := range *i {
		models.ExtractAwsmTable(index, domain, &header, &rows)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
コード例 #7
0
ファイル: autoscalegroups.go プロジェクト: murdinc/awsm
// PrintTable Prints an ascii table of the list of AutoScaling Groups
func (a *AutoScaleGroups) PrintTable() {
	if len(*a) == 0 {
		terminal.ShowErrorMessage("Warning", "No Autoscale Groups Found!")
		return
	}

	var header []string
	rows := make([][]string, len(*a))

	for index, asg := range *a {
		models.ExtractAwsmTable(index, asg, &header, &rows)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
コード例 #8
0
ファイル: snapshots.go プロジェクト: murdinc/awsm
// PrintTable Prints an ascii table of the list of EBS Snapshots
func (s *Snapshots) PrintTable() {
	if len(*s) == 0 {
		terminal.ShowErrorMessage("Warning", "No Snapshots Found!")
		return
	}

	var header []string
	rows := make([][]string, len(*s))

	for index, snapshot := range *s {
		models.ExtractAwsmTable(index, snapshot, &header, &rows)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
コード例 #9
0
ファイル: volumes.go プロジェクト: murdinc/awsm
// PrintTable Prints an ascii table of the list of EBS Volumes
func (v *Volumes) PrintTable() {
	if len(*v) == 0 {
		terminal.ShowErrorMessage("Warning", "No Volumes Found!")
		return
	}

	var header []string
	rows := make([][]string, len(*v))

	for index, vol := range *v {
		models.ExtractAwsmTable(index, vol, &header, &rows)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
コード例 #10
0
ファイル: launchconfigurations.go プロジェクト: murdinc/awsm
// PrintTable Prints an ascii table of the list of Launch Configurations
func (l *LaunchConfigs) PrintTable() {
	if len(*l) == 0 {
		terminal.ShowErrorMessage("Warning", "No Launch Configurations Found!")
		return
	}

	var header []string
	rows := make([][]string, len(*l))

	for index, lc := range *l {
		models.ExtractAwsmTable(index, lc, &header, &rows)
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader(header)
	table.AppendBulk(rows)
	table.Render()
}
コード例 #11
0
ファイル: snapshots.go プロジェクト: murdinc/awsm
// GetSnapshots returns a slice of EBS Snapshots that match the provided search term and optional completed flag
func GetSnapshots(search string, completed bool) (*Snapshots, []error) {
	var wg sync.WaitGroup
	var errs []error

	snapList := new(Snapshots)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionSnapshots(*region.RegionName, snapList, search, completed)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering snapshot list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return snapList, errs
}
コード例 #12
0
ファイル: loadbalancersV2.go プロジェクト: murdinc/awsm
// GetLoadBalancersV2 returns a slice of Application Load Balancers
func GetLoadBalancersV2() (*LoadBalancersV2, []error) {
	var wg sync.WaitGroup
	var errs []error

	lbList := new(LoadBalancersV2)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionLoadBalancersV2(*region.RegionName, lbList)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering loadbalancer list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return lbList, errs
}
コード例 #13
0
ファイル: volumes.go プロジェクト: murdinc/awsm
// GetVolumes returns a slice of Volumes that match the provided search term and optional available flag
func GetVolumes(search string, available bool) (*Volumes, []error) {
	var wg sync.WaitGroup
	var errs []error

	volList := new(Volumes)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionVolumes(*region.RegionName, volList, search, available)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering volume list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return volList, errs
}
コード例 #14
0
ファイル: autoscalegroups.go プロジェクト: murdinc/awsm
// GetAutoScaleGroups returns a slice of AutoScale Groups based on the given search term
func GetAutoScaleGroups(search string) (*AutoScaleGroups, []error) {
	var wg sync.WaitGroup
	var errs []error

	asgList := new(AutoScaleGroups)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionAutoScaleGroups(*region.RegionName, asgList, search)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering autoscale group list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return asgList, errs
}
コード例 #15
0
ファイル: alarms.go プロジェクト: murdinc/awsm
// GetAlarms returns a slice of CloudWatch Alarms
func GetAlarms() (*Alarms, []error) {
	var wg sync.WaitGroup
	var errs []error

	alList := new(Alarms)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionAlarms(*region.RegionName, alList)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering alarm list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return alList, errs
}
コード例 #16
0
ファイル: scalingpolicies.go プロジェクト: murdinc/awsm
// GetScalingPolicies returns a slice of Scaling Policies
func GetScalingPolicies() (*ScalingPolicies, []error) {
	var wg sync.WaitGroup
	var errs []error

	spList := new(ScalingPolicies)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionScalingPolicies(*region.RegionName, spList)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering scaling policy list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return spList, errs
}
コード例 #17
0
ファイル: launchconfigurations.go プロジェクト: murdinc/awsm
// GetLaunchConfigurations returns a slice of Launch Configurations that match the provided search term
func GetLaunchConfigurations(search string) (*LaunchConfigs, []error) {
	var wg sync.WaitGroup
	var errs []error

	lcList := new(LaunchConfigs)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionLaunchConfigurations(*region.RegionName, lcList, search)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering launch config list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}
	wg.Wait()

	return lcList, errs
}
コード例 #18
0
ファイル: instances.go プロジェクト: murdinc/awsm
// GetInstances returns a list of EC2 Instances that match the provided search term and optional running flag
func GetInstances(search string, running bool) (*Instances, []error) {
	var wg sync.WaitGroup
	var errs []error

	instList := new(Instances)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionInstances(*region.RegionName, instList, search, running)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering instance list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}

	wg.Wait()

	return instList, errs
}
コード例 #19
0
ファイル: keypairs.go プロジェクト: murdinc/awsm
// GetKeyPairs returns a slice of KeyPairs that match the provided search term
func GetKeyPairs(search string) (*KeyPairs, []error) {
	var wg sync.WaitGroup
	var errs []error

	keyList := new(KeyPairs)
	regions := regions.GetRegionList()

	for _, region := range regions {
		wg.Add(1)

		go func(region *ec2.Region) {
			defer wg.Done()
			err := GetRegionKeyPairs(*region.RegionName, keyList, search)
			if err != nil {
				terminal.ShowErrorMessage(fmt.Sprintf("Error gathering key pair list for region [%s]", *region.RegionName), err.Error())
				errs = append(errs, err)
			}
		}(region)
	}

	wg.Wait()

	return keyList, errs
}
コード例 #20
0
ファイル: snapshots.go プロジェクト: murdinc/awsm
// CreateSnapshot creates a new EBS Snapshot
func CreateSnapshot(search, class, name string, dryRun bool) error {

	// --dry-run flag
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}

	// Locate the Volume
	volumes, _ := GetVolumes(search, false)
	if len(*volumes) == 0 {
		return errors.New("No volumes found for your search terms.")
	}
	if len(*volumes) > 1 {
		volumes.PrintTable()
		return errors.New("Please limit your search to return only one volume.")
	}

	volume := (*volumes)[0]
	region := volume.Region

	// Class Config
	cfg, err := config.LoadSnapshotClass(class)
	if err != nil {
		return err
	}

	terminal.Information("Found Snapshot Class Configuration for [" + class + "]!")

	// Create the snapshot
	createSnapshotResp, err := createSnapshot(volume.VolumeID, region, dryRun)
	if err != nil {
		return err
	}

	terminal.Information("Created Snapshot [" + *createSnapshotResp.SnapshotId + "] named [" + name + "] in [" + region + "]!")

	// Add Tags
	err = SetEc2NameAndClassTags(createSnapshotResp.SnapshotId, name, class, region)

	if err != nil {
		return err
	}

	sourceSnapshot := Snapshot{Name: name, Class: class, SnapshotID: *createSnapshotResp.SnapshotId}

	// Check for Propagate flag
	if cfg.Propagate && cfg.PropagateRegions != nil {

		var wg sync.WaitGroup
		var errs []error

		terminal.Information("Propagate flag is set, waiting for initial snapshot to complete.")

		// Wait for the snapshot to complete.
		err = waitForSnapshot(*createSnapshotResp.SnapshotId, region, dryRun)
		if err != nil {
			return err
		}

		// Copy to other regions
		for _, propRegion := range cfg.PropagateRegions {
			wg.Add(1)

			go func(region string) {
				defer wg.Done()

				// Copy snapshot to the destination region
				copySnapResp, err := copySnapshot(sourceSnapshot, propRegion, dryRun)

				if err != nil {
					terminal.ShowErrorMessage(fmt.Sprintf("Error propagating snapshot [%s] to region [%s]", sourceSnapshot.SnapshotID, propRegion), err.Error())
					errs = append(errs, err)
				}

				// Add Tags
				err = SetEc2NameAndClassTags(copySnapResp.SnapshotId, name, class, propRegion)
				terminal.Information(fmt.Sprintf("Copied snapshot [%s] to region [%s].", sourceSnapshot.SnapshotID, propRegion))

			}(propRegion)
		}

		wg.Wait()

		if errs != nil {
			return errors.New("Error propagating snapshot to other regions!")
		}
	}

	// Rotate out older snapshots
	if cfg.Retain > 1 {
		err := rotateSnapshots(class, cfg, dryRun)
		if err != nil {
			terminal.ShowErrorMessage(fmt.Sprintf("Error rotating [%s] snapshots!", sourceSnapshot.Class), err.Error())
			return err
		}
	}

	return nil
}
コード例 #21
0
ファイル: images.go プロジェクト: murdinc/awsm
// CreateImage creates a new Amazon Machine Image from an instance matching the provided search term. It assigns the Image the class and name that was provided
func CreateImage(search, class, name string, dryRun bool) error {

	// --dry-run flag
	if dryRun {
		terminal.Information("--dry-run flag is set, not making any actual changes!")
	}

	// Class Config
	cfg, err := config.LoadImageClass(class)
	if err != nil {
		return err
	}

	terminal.Information("Found Image Class Configuration for [" + class + "]!")

	// Locate the Instance
	instances, _ := GetInstances(search, true)
	instCount := len(*instances)
	if instCount == 0 {
		return errors.New("No running instances found for your search terms.")
	}
	if instCount > 1 {
		instances.PrintTable()
		return errors.New("Please limit your search to return only one instance.")
	}

	instance := (*instances)[0]
	region := instance.Region

	createImageResp, err := createImage(instance.InstanceID, name, region, dryRun)
	if err != nil {
		return err
	}

	terminal.Information("Created Image [" + *createImageResp.ImageId + "] named [" + name + "] in [" + region + "]!")

	// Add Tags
	err = SetEc2NameAndClassTags(createImageResp.ImageId, name, class, region)

	if err != nil {
		return err
	}

	sourceImage := Image{Name: name, Class: class, ImageID: *createImageResp.ImageId, Region: region}

	// Check for Propagate flag
	if cfg.Propagate && cfg.PropagateRegions != nil {

		var wg sync.WaitGroup
		var errs []error

		terminal.Information("Propagate flag is set, waiting for initial snapshot to complete...")

		// Wait for the image to complete.
		err = waitForImage(*createImageResp.ImageId, region, dryRun)
		if err != nil {
			return err
		}

		// Copy to other regions
		for _, propRegion := range cfg.PropagateRegions {
			wg.Add(1)

			go func(region string) {
				defer wg.Done()

				// Copy image to the destination region
				copyImageResp, err := copyImage(sourceImage, propRegion, dryRun)

				if err != nil {
					terminal.ShowErrorMessage(fmt.Sprintf("Error propagating image [%s] to region [%s]", sourceImage.ImageID, propRegion), err.Error())
					errs = append(errs, err)
				}

				// Add Tags
				err = SetEc2NameAndClassTags(copyImageResp.ImageId, name, class, propRegion)

				terminal.Information(fmt.Sprintf("Copied image [%s] to region [%s].", sourceImage.ImageID, propRegion))

			}(propRegion)
		}

		wg.Wait()

		if errs != nil {
			return errors.New("Error propagating snapshot to other regions!")
		}
	}

	// Rotate out older images
	if cfg.Retain > 1 {
		err := rotateImages(class, cfg, dryRun)
		if err != nil {
			terminal.ShowErrorMessage(fmt.Sprintf("Error rotating [%s] images!", sourceImage.Class), err.Error())
			return err
		}
	}

	return nil
}
コード例 #22
0
ファイル: launchconfigurations.go プロジェクト: murdinc/awsm
// CreateLaunchConfigurations creates a new Launch Configuration of a given class
func CreateLaunchConfigurations(class string, dryRun bool) (err error) {

	// Verify the launch config class input
	cfg, err := config.LoadLaunchConfigurationClass(class)
	if err != nil {
		return err
	}

	terminal.Information("Found Launch Configuration class configuration for [" + class + "]")

	// Instance Class Config
	instanceCfg, err := config.LoadInstanceClass(cfg.InstanceClass)
	if err != nil {
		return err
	}

	terminal.Information("Found Instance class configuration for [" + cfg.InstanceClass + "]")

	// Increment the version
	terminal.Information(fmt.Sprintf("Previous version of launch configuration is [%d]", cfg.Version))
	cfg.Increment(class)
	terminal.Information(fmt.Sprintf("New version of launch configuration is [%d]", cfg.Version))

	params := &autoscaling.CreateLaunchConfigurationInput{
		LaunchConfigurationName:  aws.String(fmt.Sprintf("%s-v%d", class, cfg.Version)),
		AssociatePublicIpAddress: aws.Bool(instanceCfg.PublicIPAddress),
		InstanceMonitoring: &autoscaling.InstanceMonitoring{
			Enabled: aws.Bool(instanceCfg.Monitoring),
		},
		InstanceType: aws.String(instanceCfg.InstanceType),
		UserData:     aws.String(instanceCfg.UserData),
		//KernelId:         aws.String("XmlStringMaxLen255"),
		//PlacementTenancy: aws.String("XmlStringMaxLen64"),
		//RamdiskId:        aws.String("XmlStringMaxLen255"),
		//SpotPrice: aws.String("SpotPrice"),
		//ClassicLinkVPCId:         aws.String("XmlStringMaxLen255"),
		//ClassicLinkVPCSecurityGroups: []*string{
		//aws.String("XmlStringMaxLen255"),
		//},
	}

	// IAM Profile
	if len(instanceCfg.IAMUser) > 0 {
		iam, err := GetIAMUser(instanceCfg.IAMUser)
		if err != nil {
			return err
		}

		terminal.Information("Found IAM User [" + iam.UserName + "]")
		params.IamInstanceProfile = aws.String(iam.Arn)

	}

	for _, region := range cfg.Regions {

		if !regions.ValidRegion(region) {
			return errors.New("Region [" + region + "] is not valid!")
		}

		// EBS
		ebsVolumes := make([]*autoscaling.BlockDeviceMapping, len(instanceCfg.EBSVolumes))
		for i, ebsClass := range instanceCfg.EBSVolumes {
			volCfg, err := config.LoadVolumeClass(ebsClass)
			if err != nil {
				return err
			}

			terminal.Information("Found Volume Class Configuration for [" + ebsClass + "]")

			latestSnapshot, err := GetLatestSnapshotByTag(region, "Class", volCfg.Snapshot)
			if err != nil {
				return err
			}

			terminal.Information("Found Snapshot [" + latestSnapshot.SnapshotID + "] with class [" + latestSnapshot.Class + "] created [" + latestSnapshot.CreatedHuman + "]")

			ebsVolumes[i] = &autoscaling.BlockDeviceMapping{
				DeviceName: aws.String(volCfg.DeviceName),
				Ebs: &autoscaling.Ebs{
					DeleteOnTermination: aws.Bool(volCfg.DeleteOnTermination),
					SnapshotId:          aws.String(latestSnapshot.SnapshotID),
					VolumeSize:          aws.Int64(int64(volCfg.VolumeSize)),
					VolumeType:          aws.String(volCfg.VolumeType),
					//Encrypted:           aws.Bool(volCfg.Encrypted),
				},
				//NoDevice:    aws.String("String"),
				//VirtualName: aws.String("String"),
			}

			if volCfg.VolumeType == "io1" {
				ebsVolumes[i].Ebs.Iops = aws.Int64(int64(volCfg.Iops))
			}

		}

		// EBS Optimized
		if instanceCfg.EbsOptimized {
			terminal.Information("Launching as EBS Optimized")
			params.EbsOptimized = aws.Bool(instanceCfg.EbsOptimized)
		}

		params.BlockDeviceMappings = ebsVolumes

		// AMI
		ami, err := GetLatestImageByTag(region, "Class", instanceCfg.AMI)
		if err != nil {
			return err
		}

		terminal.Information("Found AMI [" + ami.ImageID + "] with class [" + ami.Class + "] created [" + ami.CreatedHuman + "]")
		params.ImageId = aws.String(ami.ImageID)

		// KeyPair
		keyPair, err := GetKeyPairByName(region, instanceCfg.KeyName)
		if err != nil {
			return err
		}

		terminal.Information("Found KeyPair [" + keyPair.KeyName + "] in [" + keyPair.Region + "]")
		params.KeyName = aws.String(keyPair.KeyName)

		// VPC / Subnet
		var vpc Vpc
		var subnet Subnet
		secGroupIds := make([]*string, len(instanceCfg.SecurityGroups))
		if instanceCfg.Vpc != "" && instanceCfg.Subnet != "" {
			// VPC
			vpc, err = GetVpcByTag(region, "Class", instanceCfg.Vpc)
			if err != nil {
				return err
			}

			terminal.Information("Found VPC [" + vpc.VpcID + "] in Region [" + region + "]")

			// Subnet
			subnet, err = vpc.GetVpcSubnetByTag("Class", instanceCfg.Subnet)
			if err != nil {
				return err
			}

			terminal.Information("Found Subnet [" + subnet.SubnetID + "] in VPC [" + subnet.VpcID + "]")

			// VPC Security Groups
			secGroups, err := vpc.GetVpcSecurityGroupByTagMulti("Class", instanceCfg.SecurityGroups)
			if err != nil {
				return err
			}

			for i, secGroup := range secGroups {
				terminal.Information("Found VPC Security Group [" + secGroup.GroupID + "] with name [" + secGroup.Name + "]")
				secGroupIds[i] = aws.String(secGroup.GroupID)
			}

		} else {
			terminal.Information("No VPC and/or Subnet specified for instance Class [" + class + "]")

			// EC2-Classic security groups
			secGroups, err := GetSecurityGroupByTagMulti(region, "Class", instanceCfg.SecurityGroups)
			if err != nil {
				return err
			}

			for i, secGroup := range secGroups {
				terminal.Information("Found Security Group [" + secGroup.GroupID + "] with name [" + secGroup.Name + "]")
				secGroupIds[i] = aws.String(secGroup.GroupID)
			}

		}

		params.SecurityGroups = secGroupIds

		svc := autoscaling.New(session.New(&aws.Config{Region: aws.String(region)}))

		_, err = svc.CreateLaunchConfiguration(params)

		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				return errors.New(awsErr.Message())
			}
			return err
		}

	}

	// Rotate out older launch configurations
	if cfg.Retain > 1 {
		err := RotateLaunchConfigurations(class, cfg, dryRun)
		if err != nil {
			terminal.ShowErrorMessage(fmt.Sprintf("Error rotating [%s] launch configurations!", class), err.Error())
			return err
		}
	}

	return nil
}