package cmd import ( "fmt" "strings" cli "github.com/urfave/cli/v2" "aws-mgmt/pkg/client" ) const updateADesc = `Update a DNS A record for an instance. The only required argument is the FQDN: other information required to update the appropriate Route53 record is determined by querying Route53 and IMDS (or EC2) APIs. If run on an EC2 instance with an appropriate IAM EC2 role, an A record will be created for that instance. Required IAM permissions include: - route53:ListHostedZonesByName - route53:ChangeResourceRecordSets Alternatively, an instance ID must be specified: an A record will be created for that instance. Required IAM permissions include: - route53:ListHostedZonesByName - route53:ChangeResourceRecordSets - ec2:DescribeInstances` var UpdateAApp = &cli.App{ Name: "update-a", Usage: "Create / update an A record for an instance with a public IP", UsageText: "update-a [options] ", HideHelpCommand: true, Args: true, ArgsUsage: "", Action: updateAFunc, Description: updateADesc, Flags: []cli.Flag{ &cli.StringFlag{ Name: "instance-id", Value: "", Usage: "set the instance id, defaults to the instance running this command", }, &cli.StringFlag{ Name: "loglevel", Value: "error", Usage: "set the loglevel: debug, info, error", }, }, } var UpdateACommand = &cli.Command{ Name: "update-a", Usage: "Create / update an A record for an instance with a public IP", UsageText: "update-a [options] ", HideHelpCommand: true, Args: true, ArgsUsage: "", Action: updateAFunc, Description: updateADesc, Flags: []cli.Flag{ &cli.StringFlag{ Name: "instance-id", Value: "", Usage: "set the instance id, defaults to the instance running this command", }, &cli.StringFlag{ Name: "loglevel", Value: "error", Usage: "set the loglevel: debug, info, error", }, }, } func updateAFunc(ctx *cli.Context) error { // create logger log, err := createLogger(ctx.String("loglevel")) if err != nil { return fmt.Errorf("unable to create a logger, %w", err) } // create aws config cfg, err := client.CreateAWSConfig() if err != nil { return err } awsClient := client.NewAWSClient( client.SetConfig(cfg), ) // determine instance id instance_id := ctx.String("instance-id") // get hostname & domain hostname := ctx.Args().Get(0) domain, err := parseHostname(hostname) if err != nil { return err } log.Debug("input received", "instance-id", instance_id, "hostname", hostname, "domain", domain) zoneID, err := awsClient.GetZoneID(domain) if err != nil { return err } log.Debug("zone id set", "zone-id", zoneID) var publicIP string if instance_id != "" { log.Debug("attempting to retrieve public ip from the EC2 API") publicIP, err = awsClient.GetEC2PublicIP(instance_id) } else { log.Debug("attempting to retrieve public ip from IMDS") publicIP, err = awsClient.GetMetadata("public-ipv4") } if err != nil { return err } log.Debug("public ip retrieved", "public_ip", publicIP) err = awsClient.UpdateA(hostname, publicIP, zoneID) if err != nil { return err } log.Info("dns record updated", "hostname", hostname, "zone-id", zoneID, "public-ipv4", publicIP) return nil } func parseHostname(h string) (string, error) { var domain string if idx := strings.IndexByte(h, '.'); idx >= 0 { domain = h[idx+1:] } else { return "", fmt.Errorf("hostname (%s) must be a FQDN", h) } return domain, nil }