From 0a69307b1b449d274446ec0d5678dd65edd5b832 Mon Sep 17 00:00:00 2001 From: ZeljkoBenovic Date: Sun, 19 Nov 2023 10:50:48 +0100 Subject: [PATCH] implemented tags --- README.md | 27 ++++++++++++++++++--------- aws/ssm/ansible.go | 1 + aws/ssm/bash.go | 1 + aws/ssm/ssm.go | 41 +++++++++++++++++++++++++++++++++++++++++ conf/conf.go | 2 +- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f5ec040..9c44898 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ Supported scripts: * Ansible playbook loaded from a local filesystem The command/script/playbook will run across all EC2 instances simultaneously. -EC2 instances, for now, can be selected only by their IDs. Support for selection by tags will be -added in future versions. +EC2 instances can be targeted by instance IDs or tags. ## Prerequisites @@ -27,6 +26,7 @@ AWS access must be authenticated via `aws cli`. * `profile` - AWS profile as defined in *aws credentials* file. * `region` - AWS region in which EC2 instances reside. * `ids` - instance IDs, separated by comma (`,`). This is a mandatory flag. +* `tags` - instance tags. Tags are semicolon delimited key - multiple value pairs (example: `Name=bar,baz;Role=foo,faz`) * `max-wait` - maximum wait time in seconds to run the command Default: `30` * `max-exec` - maximum wait time in seconds to get command result Default: `300` @@ -41,11 +41,17 @@ AWS access must be authenticated via `aws cli`. # AWS authentication aws sso login --profile test-account -# oneliner +# oneliner using instance ids aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -cmd "cd /tmp && ls -lah" -aws-profile test-account -# or bash script +# or bash script using instance ids aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -script ./script.sh -aws-profile test-account + +# or oneliner using tags +aws-commander -tags "Name=Test,Test2,Test3;Role=test" -cmd "cd /tmp && ls -lah" -aws-profile test-account + +# or bash script using tags +aws-commander -tags "Name=Test,Test2,Test3;Role=test" -script ./script.sh -aws-profile test-account ``` ### Running Ansible Playbook @@ -61,12 +67,15 @@ aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b345 # AWS authentication aws sso login -# run local playbook +# run local playbook using instance ids aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -mode ansible -playbook scripts/init.yaml -extra-vars foo=bar,faz=baz -# or from url +# or from url using instance ids aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -mode ansible -ansible-url https://example.com/init.yaml -extra-vars foo=bar,faz=baz -``` -#### Missing features -* Select EC2 instances using instance tags +# run local playbook using tags +aws-commander -tags "Name=Test,Test2,Test3;Role=test" -mode ansible -playbook scripts/init.yaml -extra-vars foo=bar,faz=baz + +# or from url using tags +aws-commander -tags "Name=Test,Test2,Test3;Role=test" -mode ansible -ansible-url https://example.com/init.yaml -extra-vars foo=bar,faz=baz +``` \ No newline at end of file diff --git a/aws/ssm/ansible.go b/aws/ssm/ansible.go index b721a14..4f2a5fb 100644 --- a/aws/ssm/ansible.go +++ b/aws/ssm/ansible.go @@ -16,6 +16,7 @@ func (s ssm) RunAnsible() error { DocumentName: aws.String("AWS-RunAnsiblePlaybook"), DocumentVersion: aws.String("$LATEST"), InstanceIds: s.provideInstanceIDs(), + Targets: s.provideTags(), Parameters: s.provideAnsibleCommands(), TimeoutSeconds: &s.conf.CommandExecMaxWait, }) diff --git a/aws/ssm/bash.go b/aws/ssm/bash.go index 0cece03..ab9548d 100644 --- a/aws/ssm/bash.go +++ b/aws/ssm/bash.go @@ -16,6 +16,7 @@ func (s ssm) RunBash() error { DocumentName: aws.String("AWS-RunShellScript"), DocumentVersion: aws.String("$LATEST"), InstanceIds: s.provideInstanceIDs(), + Targets: s.provideTags(), Parameters: s.provideBashCommands(), TimeoutSeconds: &s.conf.CommandExecMaxWait, }) diff --git a/aws/ssm/ssm.go b/aws/ssm/ssm.go index a15eddd..27cee79 100644 --- a/aws/ssm/ssm.go +++ b/aws/ssm/ssm.go @@ -31,7 +31,40 @@ func New(log logger.Logger, conf conf.Config, session *session.Session) *ssm { } } +func (s ssm) provideTags() []*assm.Target { + if s.conf.AWSInstanceTags == "" { + return nil + } + + var resp = make([]*assm.Target, 0) + + for _, rawTags := range strings.Split(s.conf.AWSInstanceTags, ";") { + tags := make([]*string, 0) + + key := "tag:" + strings.TrimSpace(strings.Split(rawTags, "=")[0]) + vals := strings.Split(strings.Split(rawTags, "=")[1], ",") + + for _, v := range vals { + v := v + tags = append(tags, stringRef(strings.TrimSpace(v))) + } + + resp = append(resp, &assm.Target{ + Key: &key, + Values: tags, + }) + } + + s.log.Debug("Provided tags", "tags", spew.Sdump(resp)) + + return resp +} + func (s ssm) provideInstanceIDs() []*string { + if s.conf.AWSInstanceIDs == "" { + return nil + } + var instIDs = make([]*string, 0) ids := strings.Split(strings.TrimSpace(s.conf.AWSInstanceIDs), ",") @@ -57,6 +90,8 @@ func (s ssm) waitForCmdExecutionComplete(cmdID *string, instID *string) error { func (s ssm) waitForCmdExecAndDisplayCmdOutput(command *assm.SendCommandOutput) { var instIdsSuccess = make([]*string, 0) + s.log.Debug("Command", "command", spew.Sdump(command)) + //TODO: get results when only tags are provided for _, instID := range command.Command.InstanceIds { if err := s.waitForCmdExecutionComplete(command.Command.CommandId, instID); err != nil { s.log.Error("Error waiting for command execution", "err", err.Error(), "instance_id", *instID) @@ -101,3 +136,9 @@ func displayResults(instanceID *string, data *assm.GetCommandInvocationOutput) { fmt.Print(buff.String()) } + +func stringRef(str string) *string { + s := str + + return &s +} diff --git a/conf/conf.go b/conf/conf.go index 84d4c04..4a34b2e 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -95,7 +95,7 @@ func (c *Config) processFlags() { "comma delimited list of aws ec2 ids", ) flag.StringVar(&c.AWSInstanceTags, "tags", c.AWSInstanceTags, - "comma delimited list of ec2 tags", + "semi-column delimited list of ec2 tags (\"foo=bar,baz;faz=baz,bar\")", ) flag.IntVar(&c.CommandResultMaxWait, "max-wait", c.CommandResultMaxWait, "maximum wait time in seconds for command execution",