Skip to content

Commit

Permalink
Allow to pass a credentials file location
Browse files Browse the repository at this point in the history
  • Loading branch information
wata727 committed Jul 14, 2019
1 parent 31a29c5 commit 2712e34
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 47 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ Application Options:
--aws-access-key=ACCESS_KEY AWS access key used in deep check mode
--aws-secret-key=SECRET_KEY AWS secret key used in deep check mode
--aws-profile=PROFILE AWS shared credential profile name used in deep check mode
--aws-creds-file=FILE Aws shared credentials file used in deep checking
--aws-region=REGION AWS region used in deep check mode
--force Return zero exit status even if issues found
-q, --quiet Do not output any message when no issues are found (default format only)
Expand Down
35 changes: 24 additions & 11 deletions client/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,18 @@ type AwsCredentials struct {
AccessKey string
SecretKey string
Profile string
CredsFile string
Region string
}

// NewAwsClient returns new AwsClient with configured session
func NewAwsClient(creds AwsCredentials) *AwsClient {
func NewAwsClient(creds AwsCredentials) (*AwsClient, error) {
log.Print("[INFO] Initialize AWS Client")

s := newAwsSession(creds)
s, err := newAwsSession(creds)
if err != nil {
return nil, err
}

return &AwsClient{
IAM: iam.New(s),
Expand All @@ -65,11 +69,11 @@ func NewAwsClient(creds AwsCredentials) *AwsClient {
ELB: elb.New(s),
ELBV2: elbv2.New(s),
ECS: ecs.New(s),
}
}, nil
}

// newAwsSession returns a session necessary for initialization of the AWS SDK
func newAwsSession(creds AwsCredentials) *session.Session {
func newAwsSession(creds AwsCredentials) (*session.Session, error) {
s := session.New()

if creds.Region != "" {
Expand All @@ -79,14 +83,23 @@ func newAwsSession(creds AwsCredentials) *session.Session {
})
}
if creds.Profile != "" && creds.Region != "" {
log.Printf("[INFO] Set AWS shared credentials")
path, err := homedir.Expand("~/.aws/credentials")
if err != nil {
// Maybe this is bug
panic(err)
var credsFile string
var err error
if creds.CredsFile != "" {
credsFile, err = homedir.Expand(creds.CredsFile)
if err != nil {
return nil, err
}
} else {
credsFile, err = homedir.Expand("~/.aws/credentials")
if err != nil {
// Maybe this is bug
panic(err)
}
}
log.Printf("[INFO] Set AWS shared credentials: %s", credsFile)
s = session.New(&aws.Config{
Credentials: credentials.NewSharedCredentials(path, creds.Profile),
Credentials: credentials.NewSharedCredentials(credsFile, creds.Profile),
Region: aws.String(creds.Region),
})
}
Expand All @@ -98,5 +111,5 @@ func newAwsSession(creds AwsCredentials) *session.Session {
})
}

return s
return s, nil
}
18 changes: 17 additions & 1 deletion client/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func Test_newAwsSession(t *testing.T) {
Region *string
}
path, _ := homedir.Expand("~/.aws/credentials")
credPath, _ := homedir.Expand("~/.aws/creds")

cases := []struct {
Name string
Expand Down Expand Up @@ -44,10 +45,25 @@ func Test_newAwsSession(t *testing.T) {
Region: aws.String("us-east-1"),
},
},
{
Name: "shared credentials path",
Creds: AwsCredentials{
Profile: "default",
CredsFile: "~/.aws/creds",
Region: "us-east-1",
},
Expected: Result{
Credentials: credentials.NewSharedCredentials(credPath, "default"),
Region: aws.String("us-east-1"),
},
},
}

for _, tc := range cases {
s := newAwsSession(tc.Creds)
s, err := newAwsSession(tc.Creds)
if err != nil {
t.Fatalf("Failed `%s` test: Unexpected error occurred: %s", tc.Name, err)
}
if !reflect.DeepEqual(tc.Expected.Credentials, s.Config.Credentials) {
t.Fatalf("Failed `%s` test: expected credentials are `%#v`, but get `%#v`", tc.Name, tc.Expected.Credentials, s.Config.Credentials)
}
Expand Down
6 changes: 5 additions & 1 deletion cmd/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ func (cli *CLI) Run(args []string) int {
variables = append(variables, cliVars)

// Check configurations via Runner
runner := tflint.NewRunner(cfg, annotations, configs, variables...)
runner, err := tflint.NewRunner(cfg, annotations, configs, variables...)
if err != nil {
cli.printError(fmt.Errorf("Failed to initialize a runner: %s", err))
return ExitCodeError
}
runners, err := tflint.NewModuleRunners(runner)
if err != nil {
cli.printError(fmt.Errorf("Failed to prepare rule checking: %s", err))
Expand Down
2 changes: 2 additions & 0 deletions cmd/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Options struct {
AwsAccessKey string `long:"aws-access-key" description:"AWS access key used in deep check mode" value-name:"ACCESS_KEY"`
AwsSecretKey string `long:"aws-secret-key" description:"AWS secret key used in deep check mode" value-name:"SECRET_KEY"`
AwsProfile string `long:"aws-profile" description:"AWS shared credential profile name used in deep check mode" value-name:"PROFILE"`
AwsCredsFile string `long:"aws-creds-file" description:"Aws shared credentials file used in deep checking" value-name:"FILE"`
AwsRegion string `long:"aws-region" description:"AWS region used in deep check mode" value-name:"REGION"`
Force bool `long:"force" description:"Return zero exit status even if issues found"`
Quiet bool `short:"q" long:"quiet" description:"Do not output any message when no issues are found (default format only)"`
Expand Down Expand Up @@ -67,6 +68,7 @@ func (opts *Options) toConfig() *tflint.Config {
AccessKey: opts.AwsAccessKey,
SecretKey: opts.AwsSecretKey,
Profile: opts.AwsProfile,
CredsFile: opts.AwsCredsFile,
Region: opts.AwsRegion,
},
IgnoreModule: ignoreModule,
Expand Down
19 changes: 19 additions & 0 deletions cmd/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ func Test_toConfig(t *testing.T) {
Rules: map[string]*tflint.RuleConfig{},
},
},
{
Name: "AWS shared credentials in another file",
Command: "./tflint --aws-creds-file ~/.aws/myapp --aws-profile production --aws-region us-east-1",
Expected: &tflint.Config{
Module: false,
DeepCheck: false,
Force: false,
AwsCredentials: client.AwsCredentials{
CredsFile: "~/.aws/myapp",
Profile: "production",
Region: "us-east-1",
},
IgnoreModule: map[string]bool{},
IgnoreRule: map[string]bool{},
Varfile: []string{},
Variables: []string{},
Rules: map[string]*tflint.RuleConfig{},
},
},
{
Name: "--ignore-module",
Command: "./tflint --ignore-module module1,module2",
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Return zero exit status even if issues found. TFLint returns non-zero exit statu

## `aws_credentials`

CLI flag: `--aws-access-key`, `--aws-secret-key`, `--aws-profile` and `--aws-region`
CLI flag: `--aws-access-key`, `--aws-secret-key`, `--aws-profile`, `--aws-creds-file` and `--aws-region`

Configure AWS service crendetials. See [Credentials](credentials.md).

Expand Down
9 changes: 5 additions & 4 deletions docs/guides/credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,18 @@ config {

## Shared Credentials

If you have [shared credentials](https://aws.amazon.com/jp/blogs/security/a-new-and-standardized-way-to-manage-credentials-in-the-aws-sdks/), you can pass the profile name. However, only `~/.aws/credentials` is supported as a credential location.
If you have [shared credentials](https://aws.amazon.com/jp/blogs/security/a-new-and-standardized-way-to-manage-credentials-in-the-aws-sdks/), you can pass a profile name and credentials file location. If omitted, these will be `default` and `~/.aws/credentials`.

```
$ tflint --aws-profile AWS_PROFILE --aws-region us-east-1
$ tflint --aws-profile AWS_PROFILE --aws-region us-east-1 --aws-creds-file ~/.aws/myapp
```

```hcl
config {
aws_credentials = {
profile = "AWS_PROFILE"
region = "us-east-1"
profile = "AWS_PROFILE"
region = "us-east-1"
shared_credentials_file = "~/.aws/myapp"
}
}
```
Expand Down
4 changes: 4 additions & 0 deletions tflint/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ func (c *Config) Merge(other *Config) *Config {
if other.AwsCredentials.Profile != "" {
ret.AwsCredentials.Profile = other.AwsCredentials.Profile
}
if other.AwsCredentials.CredsFile != "" {
ret.AwsCredentials.CredsFile = other.AwsCredentials.CredsFile
}
if other.AwsCredentials.Region != "" {
ret.AwsCredentials.Region = other.AwsCredentials.Region
}
Expand Down Expand Up @@ -251,6 +254,7 @@ func (raw *rawConfig) toConfig() *Config {
ret.AwsCredentials.AccessKey = credentials["access_key"]
ret.AwsCredentials.SecretKey = credentials["secret_key"]
ret.AwsCredentials.Profile = credentials["profile"]
ret.AwsCredentials.CredsFile = credentials["shared_credentials_file"]
ret.AwsCredentials.Region = credentials["region"]
}
if rc.IgnoreModule != nil {
Expand Down
4 changes: 4 additions & 0 deletions tflint/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func Test_LoadConfig(t *testing.T) {
AccessKey: "AWS_ACCESS_KEY",
SecretKey: "AWS_SECRET_KEY",
Region: "us-east-1",
Profile: "production",
CredsFile: "~/.aws/myapp",
},
IgnoreRule: map[string]bool{
"aws_instance_invalid_type": true,
Expand Down Expand Up @@ -259,6 +261,7 @@ func Test_Merge(t *testing.T) {
AccessKey: "ACCESS_KEY",
SecretKey: "SECRET_KEY",
Region: "ap-northeast-1",
CredsFile: "~/.aws/myapp",
},
IgnoreModule: map[string]bool{
"github.com/wata727/example-2": true,
Expand Down Expand Up @@ -290,6 +293,7 @@ func Test_Merge(t *testing.T) {
SecretKey: "SECRET_KEY",
Profile: "production",
Region: "ap-northeast-1",
CredsFile: "~/.aws/myapp",
},
IgnoreModule: map[string]bool{
"github.com/wata727/example-1": true,
Expand Down
16 changes: 12 additions & 4 deletions tflint/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,22 @@ type Rule interface {
// NewRunner returns new TFLint runner
// It prepares built-in context (workpace metadata, variables) from
// received `configs.Config` and `terraform.InputValues`
func NewRunner(c *Config, ants map[string]Annotations, cfg *configs.Config, variables ...terraform.InputValues) *Runner {
func NewRunner(c *Config, ants map[string]Annotations, cfg *configs.Config, variables ...terraform.InputValues) (*Runner, error) {
path := "root"
if !cfg.Path.IsRoot() {
path = cfg.Path.String()
}
log.Printf("[INFO] Initialize new runner for %s", path)

awsClient, err := client.NewAwsClient(c.AwsCredentials)
if err != nil {
return nil, err
}

return &Runner{
TFConfig: cfg,
Issues: []*issue.Issue{},
AwsClient: client.NewAwsClient(c.AwsCredentials),
AwsClient: awsClient,

ctx: terraform.BuiltinEvalContext{
Evaluator: &terraform.Evaluator{
Expand All @@ -67,7 +72,7 @@ func NewRunner(c *Config, ants map[string]Annotations, cfg *configs.Config, vari
},
annotations: ants,
config: c,
}
}, nil
}

// NewModuleRunners returns new TFLint runners for child modules
Expand Down Expand Up @@ -139,7 +144,10 @@ func NewModuleRunners(parent *Runner) ([]*Runner, error) {
}

// Annotation does not work with children modules
runner := NewRunner(parent.config, map[string]Annotations{}, cfg)
runner, err := NewRunner(parent.config, map[string]Annotations{}, cfg)
if err != nil {
return runners, err
}
runners = append(runners, runner)
moudleRunners, err := NewModuleRunners(runner)
if err != nil {
Expand Down
Loading

0 comments on commit 2712e34

Please sign in to comment.