github.com/wata727/tflint@v0.12.2-0.20191013070026-96dd0d36f385/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 mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/ec2/ec2iface/interface.go -destination aws_ec2_mock.go -package client 30 //go:generate mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/elasticache/elasticacheiface/interface.go -destination aws_elasticache_mock.go -package client 31 //go:generate mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/elb/elbiface/interface.go -destination aws_elb_mock.go -package client 32 //go:generate mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/elbv2/elbv2iface/interface.go -destination aws_elbv2_mock.go -package client 33 //go:generate mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/iam/iamiface/interface.go -destination aws_iam_mock.go -package client 34 //go:generate mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/rds/rdsiface/interface.go -destination aws_rds_mock.go -package client 35 //go:generate mockgen -source ../vendor/github.com/aws/aws-sdk-go/service/ecs/ecsiface/interface.go -destination aws_ecs_mock.go -package client 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 }