diff --git a/README.md b/README.md index c9425bc..c603314 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,6 @@ User can load a bash script or define a single command, that will execute on all ## Usage -### Parameters (flags) -* `aws-profile` - AWS profile as defined in *aws credentials* file. Default: `default` -* `aws-zone` - AWS zone in which EC2 instances reside. Default: `eu-central-1` -* `cmd` - one-liner bash command that will be executed on EC2 instances. -* `instances` - instance IDs, separated by comma (,). This is a mandatory flag. -* `log-level` - the level of logging output (info, debug, error). Default: `info` -* `output` - a file name to write the output result of a command/script. Default: `console output` -* `script` - the location of bash script file that will run on EC2 instances. - ### AWS credentials AWS credentials can be pulled from environment variables or from aws credentials file. To define a which profile from credentials file should be used, set `aws-profile` flag. By default, it is set to `default`. @@ -27,12 +18,51 @@ Environment variables with credentials that can be set: * `AWS_SECRET_ACCESS_KEY` - the access key secret * `AWS_SESSION_TOKEN` - the session token (optional) -### Example + +### General Parameters +* `aws-profile` - AWS profile as defined in *aws credentials* file. Default: `default` +* `aws-zone` - AWS zone in which EC2 instances reside. Default: `eu-central-1` +* `instances` - instance IDs, separated by comma (,). This is a mandatory flag. +* `log-level` - the level of logging output (info, debug, error). Default: `info` +* `output` - a file name to write the output result of a command/script. Default: `console output` +* `mode` - switch between modes - Bash script or Ansible playbook. Default: `bash` + +### Running Bash scripts +* `cmd` - one-liner bash command that will be executed on EC2 instances. +* `script` - the location of bash script file that will run on EC2 instances. +* `mode` - for running bash scripts `mode` can be omitted as the default value is `bash` + +If both `cmd` and `script` flags are defined, `script` will take precedence, and `cmd` will be disregarded. + +#### Example ```bash aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -cmd "cd /tmp && ls -lah" -aws-profile test-account ``` +### Running Ansible Playbook +* `playbook` - the location of Ansible playbook that will be executed on EC2 instances. +* `dryrun` - when set to true, Ansible playbook will run and the output will be shown, but + no data will be changed. +* `mode` - for running Ansible playbook `mode` must be set to `ansible` + +#### Ansible prerequisites +Every EC2 instance, that should run Ansible playbook, must have Ansible already installed. +If Ansible is not installed, the deployment will fail. +You can use `bash` mode to simply install Ansible from your OS package manager before running the playbook. + +#### Example +```bash +## if Ansible is not installed on host - install Ansible +aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -cmd "sudo apt install -y ansible" -aws-profile test-account -aws-zone us-west-2 +## run playbook +aws-commander -instances i-0bf9c273c67f684a0,i-011c9b3e3607a63b5,i-0e53e37f7b34517f5,i-0f02ca10faf8f349e -mode ansible -playbook scripts/nodes-restart.yaml -aws-profile test-account -aws-zone us-west-2 +``` + +#### Missing features +Currently, running the Ansible playbook from a remote location via URL / S3 is not supported. +It will be supported in the future release. + ## License Copyright 2022 Trapesys diff --git a/framework/adapters/left/cmd/cmd.go b/framework/adapters/left/cmd/cmd.go index d76f143..baa2631 100644 --- a/framework/adapters/left/cmd/cmd.go +++ b/framework/adapters/left/cmd/cmd.go @@ -10,36 +10,94 @@ import ( ) type Adapter struct { - flags *cmd.Flags logger hclog.Logger buffInstanceFlag string } func NewAdapter() ports.ICmd { - return &Adapter{ - flags: &cmd.Flags{ - AwsZone: new(string), - BashScriptLocation: new(string), - LogLevel: new(string), - OutputLocation: new(string), - FreeFormCmd: new(string), - AwsProfile: new(string), - }, - } + return &Adapter{} } func (a *Adapter) GetFlags() cmd.Flags { - flag.StringVar(a.flags.AwsZone, "aws-zone", "eu-central-1", "aws zone where instances reside") - flag.StringVar(a.flags.BashScriptLocation, "script", "", "the location of the script to run") - flag.StringVar(&a.buffInstanceFlag, "instances", "", "instance IDs, separated by comma (,)") - flag.StringVar(a.flags.LogLevel, "log-level", "info", "log output level") - flag.StringVar(a.flags.OutputLocation, "output", "", "the location of file to write json output "+ - "(default: output to console)") - flag.StringVar(a.flags.FreeFormCmd, "cmd", "", "freeform command, a single line bash command to be executed") - flag.StringVar(a.flags.AwsProfile, "aws-profile", "default", "aws credentials profile") + flag.StringVar(cmd.UserFlags.AwsZone.ValueString, + cmd.UserFlags.AwsZone.Name, + cmd.UserFlags.AwsZone.DefaultString, + cmd.UserFlags.AwsZone.Usage, + ) + flag.StringVar(cmd.UserFlags.BashScriptLocation.ValueString, + cmd.UserFlags.BashScriptLocation.Name, + cmd.UserFlags.BashScriptLocation.DefaultString, + cmd.UserFlags.BashScriptLocation.Usage, + ) + flag.StringVar(&a.buffInstanceFlag, + cmd.UserFlags.InstanceIDs.Name, + cmd.UserFlags.InstanceIDs.DefaultString, + cmd.UserFlags.InstanceIDs.Usage, + ) + flag.StringVar(cmd.UserFlags.LogLevel.ValueString, + cmd.UserFlags.LogLevel.Name, + cmd.UserFlags.LogLevel.DefaultString, + cmd.UserFlags.LogLevel.Usage, + ) + flag.StringVar(cmd.UserFlags.OutputLocation.ValueString, + cmd.UserFlags.OutputLocation.Name, + cmd.UserFlags.OutputLocation.DefaultString, + cmd.UserFlags.OutputLocation.Usage, + ) + flag.StringVar(cmd.UserFlags.FreeFormCmd.ValueString, + cmd.UserFlags.FreeFormCmd.Name, + cmd.UserFlags.FreeFormCmd.DefaultString, + cmd.UserFlags.FreeFormCmd.Usage, + ) + flag.StringVar(cmd.UserFlags.AwsProfile.ValueString, + cmd.UserFlags.AwsProfile.Name, + cmd.UserFlags.AwsProfile.DefaultString, + cmd.UserFlags.AwsProfile.Usage, + ) + flag.StringVar(cmd.UserFlags.Mode.ValueString, + cmd.UserFlags.Mode.Name, + cmd.UserFlags.Mode.DefaultString, + cmd.UserFlags.Mode.Usage, + ) + flag.StringVar(cmd.UserFlags.AnsiblePlaybook.ValueString, + cmd.UserFlags.AnsiblePlaybook.Name, + cmd.UserFlags.AnsiblePlaybook.DefaultString, + cmd.UserFlags.AnsiblePlaybook.Usage, + ) + flag.BoolVar(cmd.UserFlags.AnsibleDryRun.ValueBool, + cmd.UserFlags.AnsibleDryRun.Name, + cmd.UserFlags.AnsibleDryRun.DefaultBool, + cmd.UserFlags.AnsibleDryRun.Usage, + ) flag.Parse() + a.checkFlags() + + cmd.UserFlags.InstanceIDs.ValueStringArr = append(cmd.UserFlags.InstanceIDs.ValueStringArr, + strings.Split(a.buffInstanceFlag, ",")...) + + return cmd.UserFlags +} + +func (a *Adapter) WithLogger(logger hclog.Logger) ports.ICmd { + a.logger = logger.Named("cmd") + + return a +} + +func (a Adapter) isAllowedMode() bool { + for _, mode := range cmd.UserFlags.Mode.AllowedValuesStr { + if *cmd.UserFlags.Mode.ValueString == mode { + return true + } + } + + return false +} + +func (a *Adapter) checkFlags() { + // check if Instance ID is defined if a.buffInstanceFlag == "" { a.logger.Error("instance IDs not defined") flag.PrintDefaults() @@ -47,13 +105,20 @@ func (a *Adapter) GetFlags() cmd.Flags { os.Exit(1) } - a.flags.InstanceIDs = append(a.flags.InstanceIDs, strings.Split(a.buffInstanceFlag, ",")...) + // check if modes are allowed + if !a.isAllowedMode() { + a.logger.Error("only bash script and ansible playbook modes types are supported") + flag.PrintDefaults() - return *a.flags -} + os.Exit(1) + } -func (a *Adapter) WithLogger(logger hclog.Logger) ports.ICmd { - a.logger = logger.Named("cmd") + // check if ansible playbook is defined + if *cmd.UserFlags.Mode.ValueString == "ansible" && + *cmd.UserFlags.AnsiblePlaybook.ValueString == "" { + a.logger.Error("running in Ansible mode but no Ansible Playbook file defined!") + flag.PrintDefaults() - return a + os.Exit(1) + } } diff --git a/framework/adapters/left/localfs/localfs.go b/framework/adapters/left/localfs/localfs.go index 929162c..fa74e70 100644 --- a/framework/adapters/left/localfs/localfs.go +++ b/framework/adapters/left/localfs/localfs.go @@ -33,6 +33,17 @@ func (a Adapter) ReadBashScript(bashScriptLocation string) string { return string(fileBytes) } +func (a Adapter) ReadAnsiblePlaybook(playbookLocation string) string { + // TODO: check if is yaml or yml file + fileBytes, err := os.ReadFile(playbookLocation) + if err != nil { + a.logger.Error("could not read file", "file", playbookLocation, "err", err.Error()) + os.Exit(1) + } + + return string(fileBytes) +} + func (a Adapter) WriteRunCommandOutput(cmdOutput ssm.Instances, outputLocation string) error { jsonBuff, err := json.MarshalIndent(cmdOutput, "", " ") if err != nil { diff --git a/framework/adapters/right/ssm/helpers.go b/framework/adapters/right/ssm/helpers.go new file mode 100644 index 0000000..875b1c1 --- /dev/null +++ b/framework/adapters/right/ssm/helpers.go @@ -0,0 +1,73 @@ +package ssm + +import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ssm" +) + +type commandType string + +var generateCommand map[commandType]func() *ssm.SendCommandInput + +// these should reflect flag options for -mode flag - bash by default +const ( + bashScript commandType = "bash" + ansiblePlaybook commandType = "ansible" +) + +// prepareCommand sets the function which initializes command based on the flag input +func (a *Adapter) prepareCommand() map[commandType]func() *ssm.SendCommandInput { + generateCommand = map[commandType]func() *ssm.SendCommandInput{ + bashScript: a.runBashScript, + ansiblePlaybook: a.runAnsiblePlaybook, + } + + return generateCommand +} + +// runBashScript initializes AWS-RunShellScript document which will run a bash script on nodes +func (a *Adapter) runBashScript() *ssm.SendCommandInput { + // if we have instance IDs, else if we have tags + if a.instanceIDs != nil { + return &ssm.SendCommandInput{ + DocumentName: aws.String("AWS-RunShellScript"), + DocumentVersion: aws.String("$LATEST"), + InstanceIds: a.instanceIDs, + Parameters: a.commands, + TimeoutSeconds: aws.Int64(300), + } + } else if a.targets != nil { + return &ssm.SendCommandInput{ + DocumentName: aws.String("AWS-RunShellScript"), + DocumentVersion: aws.String("$LATEST"), + Targets: a.targets, + Parameters: a.commands, + TimeoutSeconds: aws.Int64(300), + } + } + + return nil +} + +func (a *Adapter) runAnsiblePlaybook() *ssm.SendCommandInput { + // if we have instance IDs, else if we have tags + if a.instanceIDs != nil { + return &ssm.SendCommandInput{ + DocumentName: aws.String("AWS-RunAnsiblePlaybook"), + DocumentVersion: aws.String("$LATEST"), + InstanceIds: a.instanceIDs, + Parameters: a.commands, + TimeoutSeconds: aws.Int64(300), + } + } else if a.targets != nil { + return &ssm.SendCommandInput{ + DocumentName: aws.String("AWS-RunAnsiblePlaybook"), + DocumentVersion: aws.String("$LATEST"), + Targets: a.targets, + Parameters: a.commands, + TimeoutSeconds: aws.Int64(300), + } + } + + return nil +} diff --git a/framework/adapters/right/ssm/ssm.go b/framework/adapters/right/ssm/ssm.go index 558f8d4..6546df7 100644 --- a/framework/adapters/right/ssm/ssm.go +++ b/framework/adapters/right/ssm/ssm.go @@ -5,6 +5,7 @@ import ( ssm2 "github.com/Trapesys/aws-commander/framework/adapters/types/ssm" "github.com/Trapesys/aws-commander/framework/ports" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ssm" @@ -15,10 +16,12 @@ import ( // Adapter is the adapter for SSM port type Adapter struct { - ssmSession *ssm.SSM - logger hclog.Logger - instanceIDs []*string - targets []*ssm.Target + ssmSession *ssm.SSM + logger hclog.Logger + instanceIDs []*string + targets []*ssm.Target + mode commandType + ansiblePlaybookOpts *ports.AnsiblePlaybookOpts commands } @@ -86,38 +89,20 @@ func (a *Adapter) WithInstanceTags(tagName string, tagValues []string) ports.ISS func (a *Adapter) RunCommand() ssm2.Instances { var command *ssm.SendCommandInput - responceData := ssm2.Instances{} - // Prepare command - if a.instanceIDs != nil { - command = &ssm.SendCommandInput{ - DocumentName: aws.String("AWS-RunShellScript"), - DocumentVersion: aws.String("$LATEST"), - InstanceIds: a.instanceIDs, - Parameters: a.commands, - TimeoutSeconds: aws.Int64(300), - } + responseData := ssm2.Instances{} + prepareCommandFunc := a.prepareCommand() - a.logger.Debug("commands set", "commands", fmt.Sprintf("%+v\n", a.instanceIDs)) - } else if a.targets != nil { - command = &ssm.SendCommandInput{ - DocumentName: aws.String("AWS-RunShellScript"), - DocumentVersion: aws.String("$LATEST"), - Targets: a.targets, - Parameters: a.commands, - TimeoutSeconds: aws.Int64(300), - } - a.logger.Debug("targets set", "targets", fmt.Sprintf("%+v\n", a.targets)) - } else { + if command = prepareCommandFunc[a.mode](); command == nil { a.logger.Error("could not find instance or tag to run the commands on") - return responceData + return responseData } // Send Command out, err := a.ssmSession.SendCommand(command) if err != nil { a.logger.Error("could not run SSM command", "err", err.Error()) - return responceData + return responseData } a.logger.Debug("send command call response", "response", out.String()) @@ -138,16 +123,24 @@ func (a *Adapter) RunCommand() ssm2.Instances { waiter.MaxAttempts = 60 }, ); cmdTimeoutErr != nil { - a.logger.Error( - "timeout reached while waiting for command to finish", - "instanceID", instance, - "commandID", out.Command.CommandId, - ) - - return responceData + //nolint + if awsErr, ok := cmdTimeoutErr.(awserr.Error); ok { + if awsErr.Code() == request.WaiterResourceNotReadyErrorCode { + a.logger.Error( + "error running ansible playbook", + "instanceID", *instance, + "commandID", *out.Command.CommandId, + ) + } else if awsErr.Code() == request.ErrCodeResponseTimeout { + a.logger.Error( + "timeout reached while waiting for command to finish", + "instanceID", *instance, + "commandID", *out.Command.CommandId, + ) + } + } } } - // and parse and return output data for _, instanceID := range out.Command.InstanceIds { var output, errorOutput string @@ -158,10 +151,10 @@ func (a *Adapter) RunCommand() ssm2.Instances { ErrorOutput: errorOutput, } - responceData.Instance = append(responceData.Instance, inst) + responseData.Instance = append(responseData.Instance, inst) } - return responceData + return responseData } func (a *Adapter) GetCommandOutput(cmdID, instanceID *string) (string, string) { @@ -198,3 +191,18 @@ func (a *Adapter) WithFreeFormCommand(cmd string) ports.ISSMPort { return a } + +func (a *Adapter) WithMode(mode string) { + a.mode = commandType(mode) +} + +func (a *Adapter) WithAnsiblePlaybook(opts *ports.AnsiblePlaybookOpts) ports.ISSMPort { + a.ansiblePlaybookOpts = opts + + a.commands["playbook"] = []*string{&opts.Playbook} + a.commands["playbookurl"] = []*string{&opts.PlaybookURL} + a.commands["extravars"] = []*string{&opts.ExtraVars} + a.commands["check"] = []*string{&opts.Check} + + return a +} diff --git a/framework/adapters/types/cmd/types.go b/framework/adapters/types/cmd/types.go index b399fb3..2966a8d 100644 --- a/framework/adapters/types/cmd/types.go +++ b/framework/adapters/types/cmd/types.go @@ -1,11 +1,94 @@ package cmd type Flags struct { - AwsZone *string - InstanceIDs []string - BashScriptLocation *string - LogLevel *string - OutputLocation *string - FreeFormCmd *string - AwsProfile *string + AwsZone FlagDetails + InstanceIDs FlagDetails + BashScriptLocation FlagDetails + LogLevel FlagDetails + OutputLocation FlagDetails + FreeFormCmd FlagDetails + AwsProfile FlagDetails + Mode FlagDetails + AnsiblePlaybook FlagDetails + AnsibleDryRun FlagDetails +} + +type FlagDetails struct { + Name string + Usage string + + DefaultString string + DefaultInt int + DefaultBool bool + + ValueString *string + ValueStringArr []string + ValueInt *int + ValueBool *bool + + AllowedValuesStr []string +} + +var UserFlags = Flags{ + AwsZone: FlagDetails{ + Name: "aws-zone", + Usage: "aws zone where instances reside", + DefaultString: "eu-central-1", + ValueString: new(string), + }, + InstanceIDs: FlagDetails{ + Name: "instances", + Usage: "instance IDs, separated by comma (,)", + DefaultString: "", + ValueStringArr: make([]string, 0), + }, + BashScriptLocation: FlagDetails{ + Name: "script", + Usage: "the location of the script to run", + DefaultString: "", + ValueString: new(string), + }, + LogLevel: FlagDetails{ + Name: "log-level", + Usage: "log output level", + DefaultString: "info", + ValueString: new(string), + }, + OutputLocation: FlagDetails{ + Name: "output", + Usage: "the location of file to write json output (default: output to console)", + DefaultString: "", + ValueString: new(string), + }, + FreeFormCmd: FlagDetails{ + Name: "cmd", + Usage: "freeform command, a single line bash command to be executed", + DefaultString: "", + ValueString: new(string), + }, + AwsProfile: FlagDetails{ + Name: "aws-profile", + Usage: "aws credentials profile", + DefaultString: "default", + ValueString: new(string), + }, + Mode: FlagDetails{ + Name: "mode", + Usage: "set command mode - bash script or ansible playbook", + DefaultString: "bash", + ValueString: new(string), + AllowedValuesStr: []string{"bash", "ansible"}, + }, + AnsiblePlaybook: FlagDetails{ + Name: "playbook", + Usage: "the location of Ansible playbook file", + DefaultString: "", + ValueString: new(string), + }, + AnsibleDryRun: FlagDetails{ + Name: "dryrun", + Usage: "run Ansible script without changing any actual data", + DefaultBool: false, + ValueBool: new(bool), + }, } diff --git a/framework/ports/localfs_port.go b/framework/ports/localfs_port.go index 9480aef..1f8c689 100644 --- a/framework/ports/localfs_port.go +++ b/framework/ports/localfs_port.go @@ -8,6 +8,8 @@ import ( type ILocalFS interface { // ReadBashScript is a file reader intended for reading bash scripts. ReadBashScript(bashScriptLocation string) string + // ReadAnsiblePlaybook is a file reader intended for reading ansible playbook yaml/yml files + ReadAnsiblePlaybook(playbookLocation string) string // WithLogger injects a logger instance WithLogger(logger hclog.Logger) ILocalFS // WriteRunCommandOutput writes output result from ssm.RunCommand diff --git a/framework/ports/ssm_port.go b/framework/ports/ssm_port.go index 96dd6c9..74f695b 100644 --- a/framework/ports/ssm_port.go +++ b/framework/ports/ssm_port.go @@ -10,6 +10,9 @@ type ISSMPort interface { // RunCommand runs the commands RunCommand() ssm.Instances + // WithMode sets the mode to run command. This defines if Ansible or Bash script will be run. + WithMode(mode string) + // GetCommandOutput outputs the command result GetCommandOutput(commandID, instanceID *string) (string, string) @@ -24,6 +27,9 @@ type ISSMPort interface { // WithFreeFormCommand takes in a single bash command to be executed WithFreeFormCommand(cmd string) ISSMPort + // WithAnsiblePlaybook takes in the location of Ansible playbook + WithAnsiblePlaybook(opts *AnsiblePlaybookOpts) ISSMPort + // WithInstances builder sets the instance IDs to run the command on // // Can be omitted if WithInstanceTags is set @@ -37,3 +43,10 @@ type ISSMPort interface { // WithAWSSession builder builds the SSM adapter with AWS session WithAWSSession(awsSession *session.Session) ISSMPort } + +type AnsiblePlaybookOpts struct { + Playbook string + PlaybookURL string + ExtraVars string + Check string +} diff --git a/internal/adapters/app/app.go b/internal/adapters/app/app.go index ef87050..095969c 100644 --- a/internal/adapters/app/app.go +++ b/internal/adapters/app/app.go @@ -35,18 +35,18 @@ func (a Adapter) getAWSOptions() session.Options { os.Getenv("AWS_SECRET_ACCESS_KEY"), os.Getenv("AWS_SESSION_TOKEN"), ), - Region: a.flags.AwsZone, + Region: a.flags.AwsZone.ValueString, }, } } - a.baseLogger.Info("reading aws credentials from", "profile", *a.flags.AwsProfile) + a.baseLogger.Info("reading aws credentials from", "profile", *a.flags.AwsProfile.ValueString) // otherwise, use profile from aws credential file return session.Options{ Config: aws.Config{ - Region: a.flags.AwsZone, + Region: a.flags.AwsZone.ValueString, }, - Profile: *a.flags.AwsProfile, + Profile: *a.flags.AwsProfile.ValueString, } } @@ -55,7 +55,7 @@ func (a *Adapter) Init() ports.IApp { a.flags = a.cmdAdapter.WithLogger(a.baseLogger).GetFlags() ////////// // set log level - a.baseLogger.SetLevel(hclog.LevelFromString(*a.flags.LogLevel)) + a.baseLogger.SetLevel(hclog.LevelFromString(*a.flags.LogLevel.ValueString)) // CORE // // init core @@ -66,18 +66,38 @@ func (a *Adapter) Init() ports.IApp { // init ssm session a.ssmAdapter.WithAWSSession(a.coreAdapter.GetAWSSession()).WithLogger(a.baseLogger) + // MODE // + // init command mode ( bash or ansible ) + a.ssmAdapter.WithMode(*a.flags.Mode.ValueString) + // init ssm instances - a.ssmAdapter.WithInstances(a.flags.InstanceIDs) + a.ssmAdapter.WithInstances(a.flags.InstanceIDs.ValueStringArr) + //init ssm commands - if *a.flags.BashScriptLocation != "" { + if *a.flags.Mode.ValueString == "bash" && *a.flags.BashScriptLocation.ValueString != "" { a.ssmAdapter.WithCommands( a.localFSAdapter. WithLogger(a.baseLogger). - ReadBashScript(*a.flags.BashScriptLocation), + ReadBashScript(*a.flags.BashScriptLocation.ValueString), ) - } else if *a.flags.FreeFormCmd != "" { - a.baseLogger.Debug("freeform command found", "cmd", *a.flags.FreeFormCmd) - a.ssmAdapter.WithFreeFormCommand(*a.flags.FreeFormCmd) + } else if *a.flags.FreeFormCmd.ValueString != "" { + a.baseLogger.Debug("freeform command found", "cmd", *a.flags.FreeFormCmd.ValueString) + a.ssmAdapter.WithFreeFormCommand(*a.flags.FreeFormCmd.ValueString) + } else if *a.flags.Mode.ValueString == "ansible" { + a.baseLogger.Debug("mode set to Ansible") + + // TODO: implement PlaybookURL and ExtraVars + isDryRun := "False" + if *a.flags.AnsibleDryRun.ValueBool { + isDryRun = "True" + } + + a.ssmAdapter.WithAnsiblePlaybook(&fports.AnsiblePlaybookOpts{ + Playbook: a.localFSAdapter.ReadAnsiblePlaybook(*a.flags.AnsiblePlaybook.ValueString), + PlaybookURL: "", + ExtraVars: "", + Check: isDryRun, + }) } else { a.baseLogger.Error("could not find a command to execute, " + "script location or cmd flag must be defined") @@ -109,12 +129,12 @@ func (a *Adapter) WithLogger(logger hclog.Logger) ports.IApp { func (a Adapter) RunCommand() error { cmdResult := a.ssmAdapter.RunCommand() - if *a.flags.OutputLocation != "" { - if err := a.localFSAdapter.WriteRunCommandOutput(cmdResult, *a.flags.OutputLocation); err != nil { + if *a.flags.OutputLocation.ValueString != "" { + if err := a.localFSAdapter.WriteRunCommandOutput(cmdResult, *a.flags.OutputLocation.ValueString); err != nil { return fmt.Errorf("could not write command result to file: %w", err) } - a.baseLogger.Info("command output written to file", "filename", *a.flags.OutputLocation) + a.baseLogger.Info("command output written to file", "filename", *a.flags.OutputLocation.ValueString) os.Exit(0) }