github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/client/aws.go (about)

     1  package client
     2  
     3  import (
     4  	"errors"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/aws/aws-sdk-go/service/ec2"
     9  	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
    10  	"github.com/aws/aws-sdk-go/service/ecs"
    11  	"github.com/aws/aws-sdk-go/service/ecs/ecsiface"
    12  	"github.com/aws/aws-sdk-go/service/elasticache"
    13  	"github.com/aws/aws-sdk-go/service/elasticache/elasticacheiface"
    14  	"github.com/aws/aws-sdk-go/service/elb"
    15  	"github.com/aws/aws-sdk-go/service/elb/elbiface"
    16  	"github.com/aws/aws-sdk-go/service/elbv2"
    17  	"github.com/aws/aws-sdk-go/service/elbv2/elbv2iface"
    18  	"github.com/aws/aws-sdk-go/service/iam"
    19  	"github.com/aws/aws-sdk-go/service/iam/iamiface"
    20  	"github.com/aws/aws-sdk-go/service/rds"
    21  	"github.com/aws/aws-sdk-go/service/rds/rdsiface"
    22  	awsbase "github.com/hashicorp/aws-sdk-go-base"
    23  	hcl "github.com/hashicorp/hcl/v2"
    24  	"github.com/hashicorp/terraform/configs/configschema"
    25  	homedir "github.com/mitchellh/go-homedir"
    26  	"github.com/zclconf/go-cty/cty"
    27  )
    28  
    29  //go:generate go run github.com/golang/mock/mockgen -destination aws_ec2_mock.go -package client github.com/aws/aws-sdk-go/service/ec2/ec2iface EC2API
    30  //go:generate go run github.com/golang/mock/mockgen -destination aws_elasticache_mock.go -package client github.com/aws/aws-sdk-go/service/elasticache/elasticacheiface ElastiCacheAPI
    31  //go:generate go run github.com/golang/mock/mockgen -destination aws_elb_mock.go -package client github.com/aws/aws-sdk-go/service/elb/elbiface ELBAPI
    32  //go:generate go run github.com/golang/mock/mockgen -destination aws_elbv2_mock.go -package client github.com/aws/aws-sdk-go/service/elbv2/elbv2iface ELBV2API
    33  //go:generate go run github.com/golang/mock/mockgen -destination aws_iam_mock.go -package client github.com/aws/aws-sdk-go/service/iam/iamiface IAMAPI
    34  //go:generate go run github.com/golang/mock/mockgen -destination aws_rds_mock.go -package client github.com/aws/aws-sdk-go/service/rds/rdsiface RDSAPI
    35  //go:generate go run github.com/golang/mock/mockgen -destination aws_ecs_mock.go -package client github.com/aws/aws-sdk-go/service/ecs/ecsiface ECSAPI
    36  
    37  // AwsClient is a wrapper of the AWS SDK client
    38  // It has interfaces for each services to make testing easier
    39  type AwsClient struct {
    40  	IAM         iamiface.IAMAPI
    41  	EC2         ec2iface.EC2API
    42  	RDS         rdsiface.RDSAPI
    43  	ElastiCache elasticacheiface.ElastiCacheAPI
    44  	ELB         elbiface.ELBAPI
    45  	ELBV2       elbv2iface.ELBV2API
    46  	ECS         ecsiface.ECSAPI
    47  }
    48  
    49  // AwsCredentials is credentials for AWS used in deep check mode
    50  type AwsCredentials struct {
    51  	AccessKey             string
    52  	SecretKey             string
    53  	Profile               string
    54  	CredsFile             string
    55  	AssumeRoleARN         string
    56  	AssumeRoleExternalID  string
    57  	AssumeRolePolicy      string
    58  	AssumeRoleSessionName string
    59  	Region                string
    60  }
    61  
    62  // AwsProviderBlockSchema is a schema of `aws` provider block
    63  var AwsProviderBlockSchema = &hcl.BodySchema{
    64  	Attributes: []hcl.AttributeSchema{
    65  		{Name: "access_key"},
    66  		{Name: "secret_key"},
    67  		{Name: "profile"},
    68  		{Name: "shared_credentials_file"},
    69  		{Name: "region"},
    70  	},
    71  	Blocks: []hcl.BlockHeaderSchema{
    72  		{Type: "assume_role"},
    73  	},
    74  }
    75  
    76  type providerResource interface {
    77  	Get(key string) (string, bool, error)
    78  	GetBlock(key string, schema *configschema.Block) (map[string]string, bool, error)
    79  }
    80  
    81  // NewAwsClient returns new AwsClient with configured session
    82  func NewAwsClient(creds AwsCredentials) (*AwsClient, error) {
    83  	log.Print("[INFO] Initialize AWS Client")
    84  
    85  	config, err := getBaseConfig(creds)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	s, err := awsbase.GetSession(config)
    91  	if err != nil {
    92  		return nil, formatBaseConfigError(err)
    93  	}
    94  
    95  	return &AwsClient{
    96  		IAM:         iam.New(s),
    97  		EC2:         ec2.New(s),
    98  		RDS:         rds.New(s),
    99  		ElastiCache: elasticache.New(s),
   100  		ELB:         elb.New(s),
   101  		ELBV2:       elbv2.New(s),
   102  		ECS:         ecs.New(s),
   103  	}, nil
   104  }
   105  
   106  // ConvertToCredentials converts to credentials from the given provider config
   107  func ConvertToCredentials(providerConfig providerResource) (AwsCredentials, error) {
   108  	ret := AwsCredentials{}
   109  
   110  	accessKey, exists, err := providerConfig.Get("access_key")
   111  	if err != nil {
   112  		return ret, err
   113  	}
   114  	if exists {
   115  		ret.AccessKey = accessKey
   116  	}
   117  
   118  	secretKey, exists, err := providerConfig.Get("secret_key")
   119  	if err != nil {
   120  		return ret, err
   121  	}
   122  	if exists {
   123  		ret.SecretKey = secretKey
   124  	}
   125  
   126  	profile, exists, err := providerConfig.Get("profile")
   127  	if err != nil {
   128  		return ret, err
   129  	}
   130  	if exists {
   131  		ret.Profile = profile
   132  	}
   133  
   134  	credsFile, exists, err := providerConfig.Get("shared_credentials_file")
   135  	if err != nil {
   136  		return ret, err
   137  	}
   138  	if exists {
   139  		ret.CredsFile = credsFile
   140  	}
   141  
   142  	region, exists, err := providerConfig.Get("region")
   143  	if err != nil {
   144  		return ret, err
   145  	}
   146  	if exists {
   147  		ret.Region = region
   148  	}
   149  
   150  	assumeRole, exists, err := providerConfig.GetBlock("assume_role", &configschema.Block{
   151  		Attributes: map[string]*configschema.Attribute{
   152  			"role_arn":     {Type: cty.String, Required: true},
   153  			"session_name": {Type: cty.String},
   154  			"external_id":  {Type: cty.String},
   155  			"policy":       {Type: cty.String},
   156  		},
   157  	})
   158  	if err != nil {
   159  		return ret, err
   160  	}
   161  	if exists {
   162  		ret.AssumeRoleARN = assumeRole["role_arn"]
   163  		ret.AssumeRoleSessionName = assumeRole["session_name"]
   164  		ret.AssumeRoleExternalID = assumeRole["external_id"]
   165  		ret.AssumeRolePolicy = assumeRole["policy"]
   166  	}
   167  
   168  	return ret, nil
   169  }
   170  
   171  // Merge returns a merged credentials
   172  func (c AwsCredentials) Merge(other AwsCredentials) AwsCredentials {
   173  	if other.AccessKey != "" {
   174  		c.AccessKey = other.AccessKey
   175  	}
   176  	if other.SecretKey != "" {
   177  		c.SecretKey = other.SecretKey
   178  	}
   179  	if other.Profile != "" {
   180  		c.Profile = other.Profile
   181  	}
   182  	if other.CredsFile != "" {
   183  		c.CredsFile = other.CredsFile
   184  	}
   185  	if other.Region != "" {
   186  		c.Region = other.Region
   187  	}
   188  	if other.AssumeRoleARN != "" {
   189  		c.AssumeRoleARN = other.AssumeRoleARN
   190  	}
   191  	if other.AssumeRoleSessionName != "" {
   192  		c.AssumeRoleSessionName = other.AssumeRoleSessionName
   193  	}
   194  	if other.AssumeRoleExternalID != "" {
   195  		c.AssumeRoleExternalID = other.AssumeRoleExternalID
   196  	}
   197  	if other.AssumeRolePolicy != "" {
   198  		c.AssumeRolePolicy = other.AssumeRolePolicy
   199  	}
   200  	return c
   201  }
   202  
   203  func getBaseConfig(creds AwsCredentials) (*awsbase.Config, error) {
   204  	expandedCredsFile, err := homedir.Expand(creds.CredsFile)
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  
   209  	return &awsbase.Config{
   210  		AccessKey:             creds.AccessKey,
   211  		AssumeRoleARN:         creds.AssumeRoleARN,
   212  		AssumeRoleExternalID:  creds.AssumeRoleExternalID,
   213  		AssumeRolePolicy:      creds.AssumeRolePolicy,
   214  		AssumeRoleSessionName: creds.AssumeRoleSessionName,
   215  		SecretKey:             creds.SecretKey,
   216  		Profile:               creds.Profile,
   217  		CredsFilename:         expandedCredsFile,
   218  		Region:                creds.Region,
   219  	}, nil
   220  }
   221  
   222  // @see https://github.com/hashicorp/aws-sdk-go-base/blob/v0.3.0/session.go#L87
   223  func formatBaseConfigError(err error) error {
   224  	if strings.Contains(err.Error(), "No valid credential sources found for AWS Provider") {
   225  		return errors.New("No valid credential sources found")
   226  	}
   227  	return err
   228  }