// CreateAddress creates a new Elastic IP Address in the given region and domain func CreateAddress(region, domain string, dryRun bool) error { // --dry-run flag if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // Validate the region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // Validate the domain if !(domain == "vpc" || domain != "classic") { return errors.New("Domain should be either [vpc] or [classic].") } svc := ec2.New(session.New(&aws.Config{Region: aws.String(region)})) // Create the address params := &ec2.AllocateAddressInput{ Domain: aws.String(domain), DryRun: aws.Bool(dryRun), } _, err := svc.AllocateAddress(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } return nil }
// CreateKeyPair creates a KeyPair of a specified class in the specified region func CreateKeyPair(class, region string, dryRun bool) error { if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // KeyPair Class Config keypairCfg, err := config.LoadKeyPairClass(class) if err != nil { return err } terminal.Information("Found KeyPair class configuration for [" + class + "]!") // Validate the region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // Import the KeyPair to the requested region err = importKeyPair(region, class, []byte(keypairCfg.PublicKey), dryRun) if err != nil { return err } return nil }
// CreateVpc creates a new VPC func CreateVpc(class, name, ip, region 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.LoadVpcClass(class) if err != nil { return err } terminal.Information("Found VPC Class Configuration for [" + class + "]!") // Validate the region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // TODO limit to one VPC of a class per region, so that we can target VPCs by class instead of name. svc := ec2.New(session.New(&aws.Config{Region: aws.String(region)})) // Create the VPC vpcParams := &ec2.CreateVpcInput{ CidrBlock: aws.String(ip + cfg.CIDR), DryRun: aws.Bool(dryRun), InstanceTenancy: aws.String(cfg.Tenancy), } createVpcResp, err := svc.CreateVpc(vpcParams) if err != nil { return err } terminal.Information("Created VPC [" + *createVpcResp.Vpc.VpcId + "] named [" + name + "] in [" + region + "]!") // Add Tags err = SetEc2NameAndClassTags(createVpcResp.Vpc.VpcId, name, class, region) if err != nil { return err } return nil }
// CopySnapshot copies a Snapshot to another region func CopySnapshot(search, region string, dryRun bool) error { // --dry-run flag if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // Validate the destination region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // Get the source snapshot snapshots, _ := GetSnapshots(search, true) snapCount := len(*snapshots) if snapCount == 0 { return errors.New("No available snapshots found for your search terms.") } if snapCount > 1 { snapshots.PrintTable() return errors.New("Please limit your search to return only one snapshot.") } snapshot := (*snapshots)[0] copySnapResp, err := copySnapshot(snapshot, region, dryRun) if err != nil { return err } terminal.Information("Created Snapshot [" + *copySnapResp.SnapshotId + "] named [" + snapshot.Name + "] to [" + region + "]!") // Add Tags err = SetEc2NameAndClassTags(copySnapResp.SnapshotId, snapshot.Name, snapshot.Class, region) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } return nil }
// CreateSecurityGroup creates a new Security Group based on the provided class, region, and VPC ID func CreateSecurityGroup(class, region, vpcID string, dryRun bool) error { // --dry-run flag if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // Validate the region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // Verify the security group class input cfg, err := config.LoadSecurityGroupClass(class) if err != nil { return err } terminal.Information("Found Security Group class configuration for [" + class + "]") svc := ec2.New(session.New(&aws.Config{Region: aws.String(region)})) // Create the security group params := &ec2.CreateSecurityGroupInput{ Description: aws.String(cfg.Description), GroupName: aws.String(class), DryRun: aws.Bool(dryRun), VpcId: aws.String(vpcID), } _, err = svc.CreateSecurityGroup(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } return nil }
// CreateSimpleDBDomain creates a new SimpleDB Domain func CreateSimpleDBDomain(domain, region string) error { // Validate the region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } svc := simpledb.New(session.New(&aws.Config{Region: aws.String(region)})) params := &simpledb.CreateDomainInput{ DomainName: aws.String(domain), } terminal.Information("Creating SimpleDB Domain [" + domain + "] in [" + region + "]...") _, err := svc.CreateDomain(params) if err == nil { terminal.Information("Done!") } return err }
// CopyImage copies an existing AMI to another region func CopyImage(search, region string, dryRun bool) error { // --dry-run flag if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // Validate the destination region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // Get the source image images, _ := GetImages(search, true) imgCount := len(*images) if imgCount == 0 { return errors.New("No available images found for your search terms.") } if imgCount > 1 { images.PrintTable() return errors.New("Please limit your search to return only one image.") } image := (*images)[0] // Copy image to the destination region copyImageResp, err := copyImage(image, region, dryRun) if err != nil { return err } terminal.Information("Created Image [" + *copyImageResp.ImageId + "] named [" + image.Name + "] to [" + region + "]!") // Add Tags return SetEc2NameAndClassTags(copyImageResp.ImageId, image.Name, image.Class, region) }
// CreateAlarm creates a new CloudWatch Alarm given the provided class, region, and dimensions of that Alarm func CreateAlarm(class, region string, dimensions map[string]string, dryRun bool) error { // --dry-run flag if dryRun { terminal.Information("--dry-run flag is set, not making any actual changes!") } // Validate the region if !regions.ValidRegion(region) { return errors.New("Region [" + region + "] is Invalid!") } // Verify the alarm class input cfg, err := config.LoadAlarmClass(class) if err != nil { return err } terminal.Information("Found CloudWatch Alarm class configuration for [" + class + "]") svc := cloudwatch.New(session.New(&aws.Config{Region: aws.String(region)})) // Create the alarm params := &cloudwatch.PutMetricAlarmInput{ AlarmName: aws.String(class), ComparisonOperator: aws.String(cfg.ComparisonOperator), EvaluationPeriods: aws.Int64(int64(cfg.EvaluationPeriods)), MetricName: aws.String(cfg.MetricName), Namespace: aws.String(cfg.Namespace), Period: aws.Int64(int64(cfg.Period)), Statistic: aws.String(cfg.Statistic), Threshold: aws.Float64(cfg.Threshold), ActionsEnabled: aws.Bool(cfg.ActionsEnabled), AlarmDescription: aws.String(cfg.AlarmDescription), Unit: aws.String(cfg.Unit), } // Set the Alarm Actions for _, action := range cfg.AlarmActions { params.AlarmActions = append(params.AlarmActions, aws.String(action)) } // Set the Alarm Dimensions for name, value := range dimensions { params.Dimensions = append(params.Dimensions, &cloudwatch.Dimension{ Name: aws.String(name), Value: aws.String(value), }) } // Set the Alarm InsufficientDataActions for _, action := range cfg.InsufficientDataActions { params.InsufficientDataActions = append(params.InsufficientDataActions, aws.String(action)) } // Set the Alarm OKActions for _, action := range cfg.OKActions { params.OKActions = append(params.OKActions, aws.String(action)) } if !dryRun { _, err = svc.PutMetricAlarm(params) if err != nil { if awsErr, ok := err.(awserr.Error); ok { return errors.New(awsErr.Message()) } return err } } return nil }
// 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 }