modules/mu/clouds/aws.rb in cloud-mu-1.9.0.pre.beta vs modules/mu/clouds/aws.rb in cloud-mu-2.0.0.pre.alpha
- old
+ new
@@ -24,85 +24,116 @@
class Cloud
# Support for Amazon Web Services as a provisioning layer.
class AWS
@@myRegion_var = nil
- @@creds_loaded = false
+ @@creds_loaded = {}
# Load some credentials for using the AWS API
- def self.loadCredentials
- return if @@creds_loaded
- if $MU_CFG and $MU_CFG['aws']
- loaded = false
- if $MU_CFG['aws']['access_key'] and $MU_CFG['aws']['access_secret'] and
- # access key and secret just sitting in mu.yaml
- !$MU_CFG['aws']['access_key'].empty? and
- !$MU_CFG['aws']['access_secret'].empty?
- Aws.config = {
- access_key_id: $MU_CFG['aws']['access_key'],
- secret_access_key: $MU_CFG['aws']['access_secret'],
- region: $MU_CFG['aws']['region']
- }
- loaded = true
- elsif $MU_CFG['aws']['credentials_file'] and
- !$MU_CFG['aws']['credentials_file'].empty?
- # pull access key and secret from an awscli-style credentials file
- begin
- File.read($MU_CFG["aws"]["credentials_file"]) # make sure it's there
- credfile = IniFile.load($MU_CFG["aws"]["credentials_file"])
+ # @param name [String]: The name of the mu.yaml AWS credential set to use. If not specified, will use the default credentials, and set the global Aws.config credentials to those.
+ # @return [Aws::Credentials]
+ def self.loadCredentials(name = nil)
+ @@creds_loaded ||= {}
- if !credfile.sections or credfile.sections.size == 0
- raise ::IniFile::Error, "No AWS profiles found in #{$MU_CFG["aws"]["credentials_file"]}"
+ if name.nil?
+ return @@creds_loaded["#default"] if @@creds_loaded["#default"]
+ else
+ return @@creds_loaded[name] if @@creds_loaded[name]
+ end
+
+ cred_cfg = credConfig(name)
+ if cred_cfg.nil?
+ return nil
+ end
+
+ loaded = false
+ cred_obj = nil
+ if cred_cfg['access_key'] and cred_cfg['access_secret'] and
+ # access key and secret just sitting in mu.yaml
+ !cred_cfg['access_key'].empty? and
+ !cred_cfg['access_secret'].empty?
+ cred_obj = Aws::Credentials.new(
+ cred_cfg['access_key'], cred_cfg['access_secret']
+ )
+ if name.nil?
+# Aws.config = {
+# access_key_id: cred_cfg['access_key'],
+# secret_access_key: cred_cfg['access_secret'],
+# region: cred_cfg['region']
+# }
+ end
+ elsif cred_cfg['credentials_file'] and
+ !cred_cfg['credentials_file'].empty?
+
+ # pull access key and secret from an awscli-style credentials file
+ begin
+ File.read(cred_cfg["credentials_file"]) # make sure it's there
+ credfile = IniFile.load(cred_cfg["credentials_file"])
+
+ if !credfile.sections or credfile.sections.size == 0
+ raise ::IniFile::Error, "No AWS profiles found in #{cred_cfg["credentials_file"]}"
+ end
+ data = credfile.has_section?("default") ? credfile["default"] : credfile[credfile.sections.first]
+ if data["aws_access_key_id"] and data["aws_secret_access_key"]
+ cred_obj = Aws::Credentials.new(
+ data['aws_access_key_id'], data['aws_secret_access_key']
+ )
+ if name.nil?
+# Aws.config = {
+# access_key_id: data['aws_access_key_id'],
+# secret_access_key: data['aws_secret_access_key'],
+# region: cred_cfg['region']
+# }
end
- data = credfile.has_section?("default") ? credfile["default"] : credfile[credfile.sections.first]
- if data["aws_access_key_id"] and data["aws_secret_access_key"]
- Aws.config = {
- access_key_id: data['aws_access_key_id'],
- secret_access_key: data['aws_secret_access_key'],
- region: $MU_CFG['aws']['region']
- }
- loaded = true
- else
- MU.log "AWS credentials in #{$MU_CFG["aws"]["credentials_file"]} specified, but is missing aws_access_key_id or aws_secret_access_key elements", MU::WARN
- end
- rescue IniFile::Error, Errno::ENOENT, Errno::EACCES => e
- MU.log "AWS credentials file #{$MU_CFG["aws"]["credentials_file"]} is missing or invalid", MU::WARN, details: e.message
+ else
+ MU.log "AWS credentials in #{cred_cfg["credentials_file"]} specified, but is missing aws_access_key_id or aws_secret_access_key elements", MU::WARN
end
- elsif $MU_CFG['aws']['credentials'] and
- !$MU_CFG['aws']['credentials'].empty?
- # pull access key and secret from a vault
- begin
- vault, item = $MU_CFG["aws"]["credentials"].split(/:/)
- data = MU::Groomer::Chef.getSecret(vault: vault, item: item).to_h
- if data["access_key"] and data["access_secret"]
- Aws.config = {
- access_key_id: data['access_key'],
- secret_access_key: data['access_secret'],
- region: $MU_CFG['aws']['region']
- }
- loaded = true
- else
- MU.log "AWS credentials vault:item #{$MU_CFG["aws"]["credentials"]} specified, but is missing access_key or access_secret elements", MU::WARN
+ rescue IniFile::Error, Errno::ENOENT, Errno::EACCES => e
+ MU.log "AWS credentials file #{cred_cfg["credentials_file"]} is missing or invalid", MU::WARN, details: e.message
+ end
+ elsif cred_cfg['credentials'] and
+ !cred_cfg['credentials'].empty?
+ # pull access key and secret from a vault
+ begin
+ vault, item = cred_cfg["credentials"].split(/:/)
+ data = MU::Groomer::Chef.getSecret(vault: vault, item: item).to_h
+ if data["access_key"] and data["access_secret"]
+ cred_obj = Aws::Credentials.new(
+ cred_cfg['access_key'], cred_cfg['access_secret']
+ )
+ if name.nil?
+# Aws.config = {
+# access_key_id: data['access_key'],
+# secret_access_key: data['access_secret'],
+# region: cred_cfg['region']
+# }
end
- rescue MU::Groomer::Chef::MuNoSuchSecret
- MU.log "AWS credentials vault:item #{$MU_CFG["aws"]["credentials"]} specified, but does not exist", MU::WARN
+ else
+ MU.log "AWS credentials vault:item #{cred_cfg["credentials"]} specified, but is missing access_key or access_secret elements", MU::WARN
end
+ rescue MU::Groomer::Chef::MuNoSuchSecret
+ MU.log "AWS credentials vault:item #{cred_cfg["credentials"]} specified, but does not exist", MU::WARN
end
+ end
- if !loaded and hosted?
- # assume we've got an IAM profile and hope for the best
- ENV.delete('AWS_ACCESS_KEY_ID')
- ENV.delete('AWS_SECRET_ACCESS_KEY')
- Aws.config = {region: ENV['EC2_REGION']}
- loaded = true
- end
+ if !cred_obj and hosted?
+ # assume we've got an IAM profile and hope for the best
+ ENV.delete('AWS_ACCESS_KEY_ID')
+ ENV.delete('AWS_SECRET_ACCESS_KEY')
+ cred_obj = Aws::InstanceProfileCredentials.new
+# if name.nil?
+# Aws.config = {region: ENV['EC2_REGION']}
+# end
+ end
- @@creds_loaded = loaded
- if !@@creds_loaded
- raise MuError, "AWS layer is enabled in mu.yaml, but I couldn't find working API credentials anywhere"
- end
+ if name.nil?
+ @@creds_loaded["#default"] = cred_obj
+ else
+ @@creds_loaded[name] = cred_obj
end
+
+ cred_obj
end
# Any cloud-specific instance methods we require our resource
# implementations to have, above and beyond the ones specified by
# {MU::Cloud}
@@ -112,17 +143,21 @@
end
# If we've configured AWS as a provider, or are simply hosted in AWS,
# decide what our default region is.
def self.myRegion
+ return @@myRegion_var if @@myRegion_var
+ return nil if credConfig.nil? and !hosted?
+
if $MU_CFG and (!$MU_CFG['aws'] or !account_number) and !hosted?
return nil
end
+
if $MU_CFG and $MU_CFG['aws'] and $MU_CFG['aws']['region']
- @@myRegion_var ||= MU::Cloud::AWS.ec2($MU_CFG['aws']['region']).describe_availability_zones.availability_zones.first.region_name
+ @@myRegion_var ||= MU::Cloud::AWS.ec2(region: $MU_CFG['aws']['region']).describe_availability_zones.availability_zones.first.region_name
elsif ENV.has_key?("EC2_REGION") and !ENV['EC2_REGION'].empty?
- @@myRegion_var ||= MU::Cloud::AWS.ec2(ENV['EC2_REGION']).describe_availability_zones.availability_zones.first.region_name
+ @@myRegion_var ||= MU::Cloud::AWS.ec2(region: ENV['EC2_REGION']).describe_availability_zones.availability_zones.first.region_name
else
# hacky, but useful in a pinch
az_str = MU::Cloud::AWS.getAWSMetaData("placement/availability-zone")
@@myRegion_var = az_str.sub(/[a-z]$/i, "") if az_str
end
@@ -138,11 +173,11 @@
# @param resources [Array<String>]: The cloud provider identifier of the resource to untag
# @param key [String]: The name of the tag to remove
# @param value [String]: The value of the tag to remove
# @param region [String]: The cloud provider region
def self.removeTag(key, value, resources = [], region: myRegion)
- MU::Cloud::AWS.ec2(region).delete_tags(
+ MU::Cloud::AWS.ec2(region: region).delete_tags(
resources: resources,
tags: [
{
key: key,
value: value
@@ -156,15 +191,15 @@
# @param resources [Array<String>]: The cloud provider identifier of the resource to tag
# @param key [String]: The name of the tag to create
# @param value [String]: The value of the tag
# @param region [String]: The cloud provider region
# @return [void,<Hash>]
- def self.createTag(key, value, resources = [], region: myRegion)
+ def self.createTag(key, value, resources = [], region: myRegion, credentials: nil)
if !MU::Cloud::CloudFormation.emitCloudFormation
begin
- MU::Cloud::AWS.ec2(region).create_tags(
+ MU::Cloud::AWS.ec2(region: region, credentials: credentials).create_tags(
resources: resources,
tags: [
{
key: key,
value: value
@@ -194,19 +229,19 @@
# List the Availability Zones associated with a given Amazon Web Services
# region. If no region is given, search the one in which this MU master
# server resides.
# @param region [String]: The region to search.
# @return [Array<String>]: The Availability Zones in this region.
- def self.listAZs(region = MU.curRegion)
+ def self.listAZs(region: MU.curRegion, account: nil, credentials: nil)
if $MU_CFG and (!$MU_CFG['aws'] or !account_number)
return []
end
if !region.nil? and @@azs[region]
return @@azs[region]
end
if region
- azs = MU::Cloud::AWS.ec2(region).describe_availability_zones(
+ azs = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_availability_zones(
filters: [name: "region-name", values: [region]]
)
end
@@azs[region] ||= []
azs.data.availability_zones.each { |az|
@@ -216,25 +251,61 @@
end
# Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
# @param deploy_id [String]: The deploy for which we're writing the secret
# @param value [String]: The contents of the secret
- def self.writeDeploySecret(deploy_id, value, name = nil)
+ def self.writeDeploySecret(deploy_id, value, name = nil, credentials: nil)
name ||= deploy_id+"-secret"
begin
- MU.log "Writing #{name} to S3 bucket #{MU.adminBucketName}"
- MU::Cloud::AWS.s3(myRegion).put_object(
+ MU.log "Writing #{name} to S3 bucket #{adminBucketName(credentials)}"
+ MU::Cloud::AWS.s3(region: myRegion, credentials: credentials).put_object(
acl: "private",
- bucket: MU.adminBucketName,
+ bucket: adminBucketName(credentials),
key: name,
body: value
)
rescue Aws::S3::Errors => e
- raise MU::MommaCat::DeployInitializeError, "Got #{e.inspect} trying to write #{name} to #{MU.adminBucketName}"
+ raise MU::MommaCat::DeployInitializeError, "Got #{e.inspect} trying to write #{name} to #{adminBucketName(credentials)}"
end
end
+ # Log bucket policy for enabling CloudTrail logging to our log bucket in S3.
+ def self.cloudtrailBucketPolicy(credentials = nil)
+ cfg = credConfig(credentials)
+ policy_json = '{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "AWSCloudTrailAclCheck20131101",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':iam::<%= MU.account_number %>:root",
+ "Service": "cloudtrail.amazonaws.com"
+ },
+ "Action": "s3:GetBucketAcl",
+ "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'"
+ },
+ {
+ "Sid": "AWSCloudTrailWrite20131101",
+ "Effect": "Allow",
+ "Principal": {
+ "AWS": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':iam::'+credToAcct(credentials)+':root",
+ "Service": "cloudtrail.amazonaws.com"
+ },
+ "Action": "s3:PutObject",
+ "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(cfg['region']) ? "aws-us-gov" : "aws")+':s3:::'+MU::Cloud::AWS.adminBucketName(credentials)+'/AWSLogs/'+credToAcct(credentials)+'/*",
+ "Condition": {
+ "StringEquals": {
+ "s3:x-amz-acl": "bucket-owner-full-control"
+ }
+ }
+ }
+ ]
+ }'
+ ERB.new(policy_json).result
+ end
+
@@is_in_aws = nil
# Alias for #{MU::Cloud::AWS.hosted?}
def self.hosted
MU::Cloud::AWS.hosted?
@@ -291,55 +362,162 @@
sample["credentials_file"] = "#{Etc.getpwuid(Process.uid).dir}/.aws/credentials"
sample["log_bucket_name"] = "my-mu-s3-bucket"
sample
end
- @my_acct_num = nil
+ @@my_acct_num = nil
+ @@my_hosted_cfg = nil
+ @@acct_to_profile_map = {}
- # Fetch the AWS account number where this Mu master resides. If it's not in
- # AWS at all, or otherwise cannot be determined, return nil.
- # here.
+ # Map the name of a credential set back to an AWS account number
+ # @param name [String]
+ def self.credToAcct(name = nil)
+ creds = credConfig(name)
+
+ return creds['account_number'] if creds['account_number']
+
+ user_list = MU::Cloud::AWS.iam(credentials: name).list_users.users
+ acct_num = MU::Cloud::AWS.iam(credentials: name).list_users.users.first.arn.split(/:/)[4]
+ acct_num.to_s
+ end
+
+ # Return the name strings of all known sets of credentials for this cloud
+ # @return [Array<String>]
+ def self.listCredentials
+ if !$MU_CFG['aws']
+ return hosted? ? ["#default"] : nil
+ end
+
+ $MU_CFG['aws'].keys
+ end
+
+ def self.adminBucketName(credentials = nil)
+ #XXX find a default if this particular account doesn't have a log_bucket_name configured
+ cfg = credConfig(credentials)
+ cfg['log_bucket_name']
+ end
+
+ def self.adminBucketUrl(credentials = nil)
+ "s3://"+adminBucketName+"/"
+ end
+
+ # Return the $MU_CFG data associated with a particular profile/name/set of
+ # credentials. If no account name is specified, will return one flagged as
+ # default. Returns nil if AWS is not configured. Throws an exception if
+ # an account name is specified which does not exist.
+ # @param name [String]: The name of the key under 'aws' in mu.yaml to return
+ # @return [Hash,nil]
+ def self.credConfig(name = nil, name_only: false)
+ # If there's nothing in mu.yaml (which is wrong), but we're running
+ # on a machine hosted in AWS, *and* that machine has an IAM profile,
+ # fake it with those credentials and hope for the best.
+ if !$MU_CFG['aws'] or !$MU_CFG['aws'].is_a?(Hash) or $MU_CFG['aws'].size == 0
+ return @@my_hosted_cfg if @@my_hosted_cfg
+
+ if hosted?
+ begin
+ iam_data = JSON.parse(getAWSMetaData("iam/info"))
+ if iam_data["InstanceProfileArn"] and !iam_data["InstanceProfileArn"].empty?
+ @@my_hosted_cfg = hosted_config
+ return name_only ? "#default" : @@my_hosted_cfg
+ end
+ rescue JSON::ParserError => e
+ end
+ end
+
+ return nil
+ end
+
+ if name.nil?
+ $MU_CFG['aws'].each_pair { |name, cfg|
+ if cfg['default']
+ return name_only ? name : cfg
+ end
+ }
+ else
+ if $MU_CFG['aws'][name]
+ return name_only ? name : $MU_CFG['aws'][name]
+ elsif @@acct_to_profile_map[name.to_s]
+ return name_only ? name : @@acct_to_profile_map[name.to_s]
+ elsif name.is_a?(Integer) or name.match(/^\d+$/)
+ # Try to map backwards from an account id, if that's what we go
+ $MU_CFG['aws'].each_pair { |acctname, cfg|
+ if cfg['account_number'] and name.to_s == cfg['account_number'].to_s
+ return name_only ? acctname : $MU_CFG['aws'][acctname]
+ end
+ }
+
+ # Check each credential sets' resident account, then
+ $MU_CFG['aws'].each_pair { |acctname, cfg|
+ begin
+ user_list = MU::Cloud::AWS.iam(credentials: acctname).list_users.users
+# rescue ::Aws::IAM::Errors => e # XXX why does this NameError here?
+ rescue Exception => e
+ MU.log e.inspect, MU::WARN, details: cfg
+ next
+ end
+ acct_num = MU::Cloud::AWS.iam(credentials: acctname).list_users.users.first.arn.split(/:/)[4]
+ if acct_num.to_s == name.to_s
+ cfg['account_number'] = acct_num.to_s
+ @@acct_to_profile_map[name.to_s] = cfg
+ return name_only ? name.to_s : cfg
+ return cfg
+ end
+ }
+ end
+
+ raise MuError, "AWS credential set #{name} was requested, but I see no such working credentials in mu.yaml"
+ end
+ end
+
+ # Fetch the AWS account number where this Mu master resides. If it's not
+ # in AWS at all, or otherwise cannot be determined, return nil. here.
# XXX account for Google and non-cloud situations
- # XXX but what about multi-account uuuugh
+ # XXX this needs to be "myAccountNumber" or somesuch
+ # XXX and maybe do the IAM thing for arbitrary, non-resident accounts
def self.account_number
- return nil if !$MU_CFG['aws']
- return @my_acct_num if @my_acct_num
+ return nil if credConfig.nil?
+ return @@my_acct_num if @@my_acct_num
+ loadCredentials
+# XXX take optional credential set argument
- begin
- user_list = MU::Cloud::AWS.iam($MU_CFG['aws']['region']).list_users.users
-# rescue ::Aws::IAM::Errors => e # XXX why does this NameError here?
- rescue Exception => e
- MU.log "Got #{e.inspect} while trying to figure out our account number", MU::WARN, details: caller
- end
- if user_list.nil? or user_list.size == 0
+# begin
+# user_list = MU::Cloud::AWS.iam(region: credConfig['region']).list_users.users
+## rescue ::Aws::IAM::Errors => e # XXX why does this NameError here?
+# rescue Exception => e
+# MU.log "Got #{e.inspect} while trying to figure out our account number", MU::WARN, details: caller
+# end
+# if user_list.nil? or user_list.size == 0
mac = MU::Cloud::AWS.getAWSMetaData("network/interfaces/macs/").split(/\n/)[0]
acct_num = MU::Cloud::AWS.getAWSMetaData("network/interfaces/macs/#{mac}owner-id")
acct_num.chomp!
- else
- acct_num = MU::Cloud::AWS.iam($MU_CFG['aws']['region']).list_users.users.first.arn.split(/:/)[4]
- end
+# else
+# acct_num = MU::Cloud::AWS.iam(region: credConfig['region']).list_users.users.first.arn.split(/:/)[4]
+# end
MU.setVar("acct_num", acct_num)
- @my_acct_num ||= acct_num
+ @@my_acct_num ||= acct_num
acct_num
end
@@regions = {}
# List the Amazon Web Services region names available to this account. The
# region that is local to this Mu server will be listed first.
# @param us_only [Boolean]: Restrict results to United States only
# @return [Array<String>]
- def self.listRegions(us_only = false)
- if $MU_CFG and (!$MU_CFG['aws'] or !account_number)
- return []
- end
+ def self.listRegions(us_only = false, credentials: nil)
+
if @@regions.size == 0
- result = MU::Cloud::AWS.ec2(myRegion).describe_regions.regions
+ return [] if credConfig.nil?
+ result = MU::Cloud::AWS.ec2(region: myRegion, credentials: credentials).describe_regions.regions
regions = []
result.each { |r|
- @@regions[r.region_name] = Proc.new { listAZs(r.region_name) }
+ @@regions[r.region_name] = Proc.new {
+ listAZs(region: r.region_name, credentials: credentials)
+ }
}
end
+
regions = if us_only
@@regions.keys.delete_if { |r| !r.match(/^us\-/) }.uniq
else
@@regions.keys.uniq
end
@@ -366,18 +544,18 @@
# Generate an EC2 keypair unique to this deployment, given a regular
# OpenSSH-style public key and a name.
# @param keyname [String]: The name of the key to create.
# @param public_key [String]: The public key
# @return [Array<String>]: keypairname, ssh_private_key, ssh_public_key
- def self.createEc2SSHKey(keyname, public_key)
+ def self.createEc2SSHKey(keyname, public_key, credentials: nil)
# We replicate this key in all regions
if !MU::Cloud::CloudFormation.emitCloudFormation
MU::Cloud::AWS.listRegions.each { |region|
MU.log "Replicating #{keyname} to EC2 in #{region}", MU::DEBUG, details: @ssh_public_key
- MU::Cloud::AWS.ec2(region).import_key_pair(
- key_name: keyname,
- public_key_material: public_key
+ MU::Cloud::AWS.ec2(region: region, credentials: credentials).import_key_pair(
+ key_name: keyname,
+ public_key_material: public_key
)
}
end
end
@@ -387,24 +565,22 @@
# "translate" machine types across cloud providers.
# @param region [String]: Supported machine types can vary from region to region, so we look for the set we're interested in specifically
# @return [Hash]
def self.listInstanceTypes(region = myRegion)
return @@instance_types if @@instance_types and @@instance_types[region]
- if $MU_CFG and (!$MU_CFG['aws'] or !account_number)
- return {}
- end
+ return {} if credConfig.nil?
human_region = @@regionLookup[region]
@@instance_types ||= {}
@@instance_types[region] ||= {}
next_token = nil
begin
# Pricing API isn't widely available, so ask a region we know supports
# it
- resp = MU::Cloud::AWS.pricing("us-east-1").get_products(
+ resp = MU::Cloud::AWS.pricing(region: "us-east-1").get_products(
service_code: "AmazonEC2",
filters: [
{
field: "productFamily",
value: "Compute Instance",
@@ -454,11 +630,11 @@
raise MuError, "Can't call findSSLCertificate without specifying either a name or an id"
end
if !name.nil? and !name.empty?
matches = []
- acmcerts = MU::Cloud::AWS.acm(region).list_certificates(
+ acmcerts = MU::Cloud::AWS.acm(region: region).list_certificates(
certificate_statuses: ["ISSUED"]
)
acmcerts.certificate_summary_list.each { |cert|
matches << cert.certificate_arn if cert.domain_name == name
}
@@ -473,18 +649,18 @@
matches << iamcert.server_certificate.server_certificate_metadata.arn
end
if matches.size == 1
return matches.first
elsif matches.size == 0
- raise MuError, "No IAM or ACM certificate named #{name} was found"
+ raise MuError, "No IAM or ACM certificate named #{name} was found in #{region}"
elsif matches.size > 1
- raise MuError, "Multiple certificates named #{name} were found. Remove extras or use ssl_certificate_id to supply the exact ARN of the one you want to use."
+ raise MuError, "Multiple certificates named #{name} were found in #{region}. Remove extras or use ssl_certificate_id to supply the exact ARN of the one you want to use."
end
end
if id.match(/^arn:aws(?:-us-gov)?:acm/)
- resp = MU::Cloud::AWS.acm(region).get_certificate(
+ resp = MU::Cloud::AWS.acm(region: region).get_certificate(
certificate_arn: id
)
if resp.nil?
raise MuError, "No such ACM certificate '#{id}'"
end
@@ -508,241 +684,275 @@
id
end
# Amazon Certificate Manager API
- def self.acm(region = MU.curRegion)
+ def self.acm(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@acm_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "ACM", region: region)
- @@acm_api[region]
+ @@acm_api[credentials] ||= {}
+ @@acm_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "ACM", region: region, credentials: credentials)
+ @@acm_api[credentials][region]
end
# Amazon's IAM API
- def self.iam(region = MU.curRegion)
- region ||= myRegion
- @@iam_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "IAM", region: region)
- @@iam_api[region]
+ def self.iam(credentials: nil)
+ @@iam_api[credentials] ||= MU::Cloud::AWS::Endpoint.new(api: "IAM", credentials: credentials)
+ @@iam_api[credentials]
end
# Amazon's EC2 API
- def self.ec2(region = MU.curRegion)
+ def self.ec2(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@ec2_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "EC2", region: region)
- @@ec2_api[region]
+ @@ec2_api[credentials] ||= {}
+ @@ec2_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "EC2", region: region, credentials: credentials)
+ @@ec2_api[credentials][region]
end
# Amazon's Autoscaling API
- def self.autoscale(region = MU.curRegion)
+ def self.autoscale(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@autoscale_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "AutoScaling", region: region)
- @@autoscale_api[region]
+ @@autoscale_api[credentials] ||= {}
+ @@autoscale_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "AutoScaling", region: region, credentials: credentials)
+ @@autoscale_api[credentials][region]
end
# Amazon's ElasticLoadBalancing API
- def self.elb(region = MU.curRegion)
+ def self.elb(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@elb_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElasticLoadBalancing", region: region)
- @@elb_api[region]
+ @@elb_api[credentials] ||= {}
+ @@elb_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElasticLoadBalancing", region: region, credentials: credentials)
+ @@elb_api[credentials][region]
end
# Amazon's ElasticLoadBalancingV2 (ALB) API
- def self.elb2(region = MU.curRegion)
+ def self.elb2(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@elb2_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElasticLoadBalancingV2", region: region)
- @@elb2_api[region]
+ @@elb2_api[credentials] ||= {}
+ @@elb2_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElasticLoadBalancingV2", region: region, credentials: credentials)
+ @@elb2_api[credentials][region]
end
# Amazon's Route53 API
- def self.route53(region = MU.curRegion)
- region ||= myRegion
- @@route53_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "Route53", region: region)
- @@route53_api[region]
+ def self.route53(credentials: nil)
+ @@route53_api[credentials] ||= MU::Cloud::AWS::Endpoint.new(api: "Route53", credentials: credentials)
+ @@route53_api[credentials]
end
# Amazon's RDS API
- def self.rds(region = MU.curRegion)
+ def self.rds(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@rds_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "RDS", region: region)
- @@rds_api[region]
+ @@rds_api[credentials] ||= {}
+ @@rds_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "RDS", region: region, credentials: credentials)
+ @@rds_api[credentials][region]
end
# Amazon's CloudFormation API
- def self.cloudformation(region = MU.curRegion)
+ def self.cloudformation(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cloudformation_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudFormation", region: region)
- @@cloudformation_api[region]
+ @@cloudformation_api[credentials] ||= {}
+ @@cloudformation_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudFormation", region: region, credentials: credentials)
+ @@cloudformation_api[credentials][region]
end
# Amazon's S3 API
- def self.s3(region = MU.curRegion)
+ def self.s3(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@s3_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "S3", region: region)
- @@s3_api[region]
+ @@s3_api[credentials] ||= {}
+ @@s3_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "S3", region: region, credentials: credentials)
+ @@s3_api[credentials][region]
end
# Amazon's CloudTrail API
- def self.cloudtrail(region = MU.curRegion)
+ def self.cloudtrail(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cloudtrail_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudTrail", region: region)
- @@cloudtrail_api[region]
+ @@cloudtrail_api[credentials] ||= {}
+ @@cloudtrail_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudTrail", region: region, credentials: credentials)
+ @@cloudtrail_api[credentials][region]
end
# Amazon's CloudWatch API
- def self.cloudwatch(region = MU.curRegion)
+ def self.cloudwatch(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cloudwatch_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudWatch", region: region)
- @@cloudwatch_api[region]
+ @@cloudwatch_api[credentials] ||= {}
+ @@cloudwatch_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudWatch", region: region, credentials: credentials)
+ @@cloudwatch_api[credentials][region]
end
# Amazon's Web Application Firewall API (Global, for CloudFront et al)
- def self.wafglobal(region = MU.curRegion)
+ def self.wafglobal(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@wafglobal[region] ||= MU::Cloud::AWS::Endpoint.new(api: "WAF", region: region)
- @@wafglobal[region]
+ @@wafglobal_api[credentials] ||= {}
+ @@wafglobal[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "WAF", region: region, credentials: credentials)
+ @@wafglobal[credentials][region]
end
# Amazon's Web Application Firewall API (Regional, for ALBs et al)
- def self.waf(region = MU.curRegion)
+ def self.waf(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@waf[region] ||= MU::Cloud::AWS::Endpoint.new(api: "WAFRegional", region: region)
- @@waf[region]
+ @@waf[credentials] ||= {}
+ @@waf[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "WAFRegional", region: region, credentials: credentials)
+ @@waf[credentials][region]
end
# Amazon's CloudWatchLogs API
- def self.cloudwatchlogs(region = MU.curRegion)
+ def self.cloudwatchlogs(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cloudwatchlogs_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudWatchLogs", region: region)
- @@cloudwatchlogs_api[region]
+ @@cloudwatchlogs_api[credentials] ||= {}
+ @@cloudwatchlogs_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudWatchLogs", region: region, credentials: credentials)
+ @@cloudwatchlogs_api[credentials][region]
end
# Amazon's CloudFront API
- def self.cloudfront(region = MU.curRegion)
+ def self.cloudfront(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cloudfront_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudFront", region: region)
- @@cloudfront_api[region]
+ @@cloudfront_api[credentials] ||= {}
+ @@cloudfront_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudFront", region: region, credentials: credentials)
+ @@cloudfront_api[credentials][region]
end
# Amazon's ElastiCache API
- def self.elasticache(region = MU.curRegion)
+ def self.elasticache(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@elasticache_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElastiCache", region: region)
- @@elasticache_api[region]
+ @@elasticache_api[credentials] ||= {}
+ @@elasticache_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElastiCache", region: region, credentials: credentials)
+ @@elasticache_api[credentials][region]
end
# Amazon's SNS API
- def self.sns(region = MU.curRegion)
+ def self.sns(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@sns_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "SNS", region: region)
- @@sns_api[region]
+ @@sns_api[credentials] ||= {}
+ @@sns_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "SNS", region: region, credentials: credentials)
+ @@sns_api[credentials][region]
end
# Amazon's SQS API
- def self.sqs(region = MU.curRegion)
+ def self.sqs(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@sqs_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "SQS", region: region)
- @@sqs_api[region]
+ @@sqs_api[credentials] ||= {}
+ @@sqs_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "SQS", region: region, credentials: credentials)
+ @@sqs_api[credentials][region]
end
# Amazon's EFS API
- def self.efs(region = MU.curRegion)
+ def self.efs(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@efs_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "EFS", region: region)
- @@efs_api[region]
+ @@efs_api[credentials] ||= {}
+ @@efs_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "EFS", region: region, credentials: credentials)
+ @@efs_api[credentials][region]
end
# Amazon's Lambda API
- def self.lambda(region = MU.curRegion)
+ def self.lambda(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@lambda_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "Lambda", region: region)
- @@lambda_api[region]
+ @@lambda_api[credentials] ||= {}
+ @@lambda_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "Lambda", region: region, credentials: credentials)
+ @@lambda_api[credentials][region]
end
# Amazon's API Gateway API
- def self.apig(region = MU.curRegion)
+ def self.apig(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@apig_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "APIGateway", region: region)
- @@apig_api[region]
+ @@apig_api[credentials] ||= {}
+ @@apig_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "APIGateway", region: region, credentials: credentials)
+ @@apig_api[credentials][region]
end
# Amazon's Cloudwatch Events API
def self.cloudwatch_events(region = MU.cureRegion)
region ||= myRegion
- @@cloudwatch_events_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudWatchEvents", region: region)
+ @@cloudwatch_events_api[credentials] ||= {}
+ @@cloudwatch_events_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CloudWatchEvents", region: region, credentials: credentials)
@@cloudwatch_events_api
end
# Amazon's ECS API
- def self.ecs(region = MU.curRegion)
+ def self.ecs(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@ecs_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "ECS", region: region)
- @@ecs_api[region]
+ @@ecs_api[credentials] ||= {}
+ @@ecs_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "ECS", region: region, credentials: credentials)
+ @@ecs_api[credentials][region]
end
# Amazon's EKS API
- def self.eks(region = MU.curRegion)
+ def self.eks(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@eks_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "EKS", region: region)
- @@eks_api[region]
+ @@eks_api[credentials] ||= {}
+ @@eks_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "EKS", region: region, credentials: credentials)
+ @@eks_api[credentials][region]
end
# Amazon's Pricing API
- def self.pricing(region = MU.curRegion)
+ def self.pricing(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@pricing_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "Pricing", region: region)
- @@pricing_api[region]
+ @@pricing_api[credentials] ||= {}
+ @@pricing_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "Pricing", region: region, credentials: credentials)
+ @@pricing_api[credentials][region]
end
# Amazon's Simple Systems Manager API
- def self.ssm(region = MU.curRegion)
+ def self.ssm(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@ssm_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "SSM", region: region)
- @@ssm_api[region]
+ @@ssm_api[credentials] ||= {}
+ @@ssm_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "SSM", region: region, credentials: credentials)
+ @@ssm_api[credentials][region]
end
# Amazon's Elasticsearch API
- def self.elasticsearch(region = MU.curRegion)
+ def self.elasticsearch(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@elasticsearch_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElasticsearchService", region: region)
- @@elasticsearch_api[region]
+ @@elasticsearch_api[credentials] ||= {}
+ @@elasticsearch_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "ElasticsearchService", region: region, credentials: credentials)
+ @@elasticsearch_api[credentials][region]
end
# Amazon's Cognito Identity API
- def self.cognito_ident(region = MU.curRegion)
+ def self.cognito_ident(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cognito_ident_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CognitoIdentity", region: region)
- @@cognito_ident_api[region]
+ @@cognito_ident_api[credentials] ||= {}
+ @@cognito_ident_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CognitoIdentity", region: region, credentials: credentials)
+ @@cognito_ident_api[credentials][region]
end
# Amazon's Cognito Identity Provider API
- def self.cognito_user(region = MU.curRegion)
+ def self.cognito_user(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@cognito_user_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "CognitoIdentityProvider", region: region)
- @@cognito_user_api[region]
+ @@cognito_user_api[credentials] ||= {}
+ @@cognito_user_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "CognitoIdentityProvider", region: region, credentials: credentials)
+ @@cognito_user_api[credentials][region]
end
# Amazon's KMS API
- def self.kms(region = MU.curRegion)
+ def self.kms(region: MU.curRegion, credentials: nil)
region ||= myRegion
- @@kms_api[region] ||= MU::Cloud::AWS::Endpoint.new(api: "KMS", region: region)
- @@kms_api[region]
+ @@kms_api[credentials] ||= {}
+ @@kms_api[credentials][region] ||= MU::Cloud::AWS::Endpoint.new(api: "KMS", region: region, credentials: credentials)
+ @@kms_api[credentials][region]
end
+ # Amazon's Organizations API
+ def self.orgs(credentials: nil)
+ @@organizations_api ||= {}
+ @@organizations_api[credentials] ||= MU::Cloud::AWS::Endpoint.new(api: "Organizations", credentials: credentials)
+ @@organizations_api[credentials]
+ end
+
# Fetch an Amazon instance metadata parameter (example: public-ipv4).
# @param param [String]: The parameter name to fetch
# @return [String, nil]
def self.getAWSMetaData(param)
base_url = "http://169.254.169.254/latest/meta-data/"
begin
response = nil
- Timeout.timeout(2) do
+ Timeout.timeout(1) do
response = open("#{base_url}/#{param}").read
end
response
rescue OpenURI::HTTPError, Timeout::Error, SocketError, Errno::ENETUNREACH, Net::HTTPServerException, Errno::EHOSTUNREACH => e
- # This is fairly normal, just handle it gracefully
+ # This is normal on machines checking to see if they're AWS-hosted
logger = MU::Logger.new
logger.log "Failed metadata request #{base_url}/#{param}: #{e.inspect}", MU::DEBUG
return nil
end
end
@@ -842,11 +1052,11 @@
group.ip_permissions.each { |rule|
if rule.ip_protocol == "tcp" and
rule.from_port == port and rule.to_port == port
MU.log "Revoking old rules for port #{port.to_s} from #{sg_id}", MU::NOTICE
begin
- MU::Cloud::AWS.ec2(myRegion).revoke_security_group_ingress(
+ MU::Cloud::AWS.ec2(region: myRegion).revoke_security_group_ingress(
group_id: sg_id,
ip_permissions: [
{
ip_protocol: "tcp",
from_port: port,
@@ -869,11 +1079,11 @@
allow_ips.each { |cidr|
allow_ips_cidr << {"cidr_ip" => cidr}
}
begin
- MU::Cloud::AWS.ec2(myRegion).authorize_security_group_ingress(
+ MU::Cloud::AWS.ec2(region: myRegion).authorize_security_group_ingress(
group_id: sg_id,
ip_permissions: [
{
ip_protocol: "tcp",
from_port: 10514,
@@ -917,28 +1127,45 @@
# Wrapper class for the EC2 API, so that we can catch some common transient
# endpoint errors without having to spray rescues all over the codebase.
class Endpoint
@api = nil
@region = nil
+ @cred_obj = nil
+ attr_reader :credentials
+ attr_reader :account
# Create an AWS API client
# @param region [String]: Amazon region so we know what endpoint to use
# @param api [String]: Which API are we wrapping?
- def initialize(region: MU.curRegion, api: "EC2")
- @region = region
+ def initialize(region: MU.curRegion, api: "EC2", credentials: nil)
+ @cred_obj = MU::Cloud::AWS.loadCredentials(credentials)
+ @credentials = MU::Cloud::AWS.credConfig(credentials, name_only: true)
+
+ if !@cred_obj
+ raise MuError, "Unable to locate valid AWS credentials for #{api} API. #{credentials ? "Credentials requested were '#{credentials}'": ""}"
+ end
+
+ params = {}
+
if region
- @api = Object.const_get("Aws::#{api}::Client").new(region: region)
- else
- @api = Object.const_get("Aws::#{api}::Client").new
+ @region = region
+ params[:region] = @region
end
+
+ params[:credentials] = @cred_obj
+
+ MU.log "Initializing #{api} object with credentials #{credentials}", MU::DEBUG, details: params
+ @api = Object.const_get("Aws::#{api}::Client").new(params)
+
+ @api
end
@instance_cache = {}
# Catch-all for AWS client methods. Essentially a pass-through with some
# rescues for known silly endpoint behavior.
def method_missing(method_sym, *arguments)
- MU::Cloud::AWS.loadCredentials
+
retries = 0
begin
MU.log "Calling #{method_sym} in #{@region}", MU::DEBUG, details: arguments
retval = nil
if !arguments.nil? and arguments.size == 1
@@ -947,11 +1174,11 @@
retval = @api.method(method_sym).call(*arguments)
else
retval = @api.method(method_sym).call
end
return retval
- rescue Aws::EC2::Errors::InternalError, Aws::EC2::Errors::RequestLimitExceeded, Aws::EC2::Errors::Unavailable, Aws::Route53::Errors::Throttling, Aws::ElasticLoadBalancing::Errors::HttpFailureException, Aws::EC2::Errors::Http503Error, Aws::AutoScaling::Errors::Http503Error, Aws::AutoScaling::Errors::InternalFailure, Aws::AutoScaling::Errors::ServiceUnavailable, Aws::Route53::Errors::ServiceUnavailable, Aws::ElasticLoadBalancing::Errors::Throttling, Aws::RDS::Errors::ClientUnavailable, Aws::Waiters::Errors::UnexpectedError, Aws::ElasticLoadBalancing::Errors::ServiceUnavailable, Aws::ElasticLoadBalancingV2::Errors::Throttling, Seahorse::Client::NetworkingError, Aws::IAM::Errors::Throttling => e
+ rescue Aws::EC2::Errors::InternalError, Aws::EC2::Errors::RequestLimitExceeded, Aws::EC2::Errors::Unavailable, Aws::Route53::Errors::Throttling, Aws::ElasticLoadBalancing::Errors::HttpFailureException, Aws::EC2::Errors::Http503Error, Aws::AutoScaling::Errors::Http503Error, Aws::AutoScaling::Errors::InternalFailure, Aws::AutoScaling::Errors::ServiceUnavailable, Aws::Route53::Errors::ServiceUnavailable, Aws::ElasticLoadBalancing::Errors::Throttling, Aws::RDS::Errors::ClientUnavailable, Aws::Waiters::Errors::UnexpectedError, Aws::ElasticLoadBalancing::Errors::ServiceUnavailable, Aws::ElasticLoadBalancingV2::Errors::Throttling, Seahorse::Client::NetworkingError, Aws::IAM::Errors::Throttling, Aws::EFS::Errors::ThrottlingException, Aws::Pricing::Errors::ThrottlingException => e
if e.class.name == "Seahorse::Client::NetworkingError" and e.message.match(/Name or service not known/)
MU.log e.inspect, MU::ERR
raise e
end
retries = retries + 1
@@ -965,13 +1192,16 @@
debuglevel = MU::WARN
interval = 40 + Random.rand(15) - 5
# elsif retries > 100
# raise MuError, "Exhausted retries after #{retries} attempts while calling EC2's #{method_sym} in #{@region}. Args were: #{arguments}"
end
- MU.log "Got #{e.inspect} calling EC2's #{method_sym} in #{@region}, waiting #{interval.to_s}s and retrying. Args were: #{arguments}", debuglevel, details: caller
+ MU.log "Got #{e.inspect} calling EC2's #{method_sym} in #{@region} with credentials #{@credentials}, waiting #{interval.to_s}s and retrying. Args were: #{arguments}", debuglevel, details: caller
sleep interval
retry
+ rescue Exception => e
+ MU.log "Got #{e.inspect} calling EC2's #{method_sym} in #{@region} with credentials #{@credentials}", MU::DEBUG, details: arguments
+ raise e
end
end
end
@@iam_api = {}
@@acm_api = {}
@@ -1002,8 +1232,9 @@
@@ssm_api ={}
@@elasticsearch_api ={}
@@cognito_ident_api ={}
@@cognito_user_api ={}
@@kms_api ={}
+ @@organizataion_api ={}
end
end
end