1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS Auto ScalingのAPI Requestの書き方

Posted at

はじめに

諸事情により、CLIもSDKも使わないでAWS Auto Scalingに対してAPI操作しなきゃならなくなった。
(EC2 AutoScalingでもApplication AutoScalingでもないやつ。コンソールではAWS Auto ScalingだしCLIとかではauto-scaling-plansとかでややこしい。)
普通に(自分が普通だと思う方法で)書いたらなかなか上手く行かなかったのでめも。

完成形のコード

基本はこれのコピペ。
https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4-signed-request-examples.html#sig-v4-examples-post

DescribeScalingPlansを叩く。

auto_scaling_plans.py
import sys, os, base64, datetime, hashlib, hmac
import requests
from aws_requests_auth.aws_auth import AWSRequestsAuth
from boto3.session import Session

profile_name = 'default'
method = 'POST'
service = 'autoscaling'
service_alt = 'autoscaling-plans'
region = 'ap-northeast-1'
host = service + '.' + region + '.amazonaws.com'
endpoint = 'https://' + host + '/'

content_type = 'application/x-amz-json-1.1'
amz_target = 'AnyScaleScalingPlannerFrontendService.DescribeScalingPlans'

request_parameters = '{}'


def sign(key, msg):
    return hmac.new(key, msg.encode("utf-8"), hashlib.sha256).digest()


def getSignatureKey(key, date_stamp, regionName, serviceName):
    kDate = sign(('AWS4' + key).encode('utf-8'), date_stamp)
    kRegion = sign(kDate, regionName)
    kService = sign(kRegion, serviceName)
    kSigning = sign(kService, 'aws4_request')
    return kSigning


credentials = Session(profile_name=profile_name).get_credentials()

access_key = credentials.access_key
secret_key = credentials.secret_key
session_token = credentials.token
if access_key is None or secret_key is None:
    print('No access key is available.')
    sys.exit()

t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
date_stamp = t.strftime('%Y%m%d')

canonical_uri = '/'

canonical_querystring = ''

canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' + 'x-amz-security-token:' + session_token + '\n' + 'x-amz-target:' + amz_target + '\n'

signed_headers = 'content-type;host;x-amz-date;x-amz-security-token;x-amz-target'

payload_hash = hashlib.sha256(request_parameters.encode('utf-8')).hexdigest()

canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp + '/' + region + '/' + service_alt + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
    canonical_request.encode('utf-8')).hexdigest()

signing_key = getSignatureKey(secret_key, date_stamp, region, service_alt)

signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'),
                     hashlib.sha256).hexdigest()

authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature

headers = {
    'Content-Type': content_type,
    'X-Amz-Date': amz_date,
    'X-Amz-Security-Token': session_token,
    'X-Amz-Target': amz_target,
    'Authorization': authorization_header,
}

print('Request:\n\t' + request_parameters)

response = requests.post(endpoint, data=request_parameters, headers=headers)
print('Response:\n\t' + response.text)

困ったところ

x-amz-targetになんて書いたらいいかわからん

DescribeScalingPlansを叩くから
amz_target = 'DescribeScalingPlans'って書いたら、↓レスポンスがこんな感じだった。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html class="no-js aws-lng-en_US" lang="en-US" data-static-assets="https://a0.awsstatic.com" data-js-version="1.0.299" data-css-version="1.0.302">
 <head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 
  <link rel="dns-prefetch" href="https://a0.awsstatic.com" />
  <link rel="dns-prefetch" href="//d0.awsstatic.com" />
  <link rel="dns-prefetch" href="//d1.awsstatic.com" />
  <title>AWS Auto Scaling</title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="description" content="Learn how AWS Auto Scaling monitors your applications and automatically adjusts capacity to maintain steady, predictable performance at the lowest possible cost. " />
~以下略~

x-amz-targetが正しくないと、AWS Auto ScalingのWebページ (https://autoscaling.ap-northeast-1.amazonaws.com) を取得するようす。

じゃあなにを入れればいいのかと。どこを見ればいいのかと。
ドキュメントを読んでもよくわからなかったので、AWS CLIでAutoScalingPlansのコマンドをdebugモードで叩いて、ログから拾った。

$ aws autoscaling-plans describe-scaling-plans --debug
~中略~
2019-09-12 18:52:40,665 - MainThread - botocore.endpoint - DEBUG - Making request for OperationModel(name=DescribeScalingPlans) with params: {'url_path': '/', 'query_string': '', 'method': 'POST', 'headers': {'X-Amz-Target': 'AnyScaleScalingPlannerFrontendService.DescribeScalingPlans', 'Content-Type': 'application/x-amz-json-1.1', 'User-Agent': 'aws-cli/1.16.237 Python/3.7.4 Linux/4.4.0-18362-Microsoft botocore/1.12.227'}, 'body': b'{}', 'url': 'https://autoscaling.ap-northeast-1.amazonaws.com/', 'context': {'client_region': 'ap-northeast-1', 'client_config': <botocore.config.Config object at 0x7f5c9b8fe610>, 'has_streaming_input': False, 'auth_type': None}}
~中略~
'X-Amz-Target': 'AnyScaleScalingPlannerFrontendService.DescribeScalingPlans',

AnyScaleScalingPlannerFrontendServiceだって。見たことないよ。

というわけで、こうしたら動いた。
amz_target = 'AnyScaleScalingPlannerFrontendService.DescribeScalingPlans'

InvalidSignatureExceptionが発生する

ここは特にいじらずそのまま書いてたんだけど、

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
    canonical_request.encode('utf-8')).hexdigest()

signing_key = getSignatureKey(secret_key, date_stamp, region, service)

↓これがでた。

エラーメッセージ
{"__type":"InvalidSignatureException","message":"Credential should be scoped to correct service: 'autoscaling-plans'. "}

スコープがautoscaling-plansになってないって。

↓ここでホスト名を生成するためにserviceautoscalingにしていたが、

service = 'autoscaling'
region = 'ap-northeast-1'
host = service + '.' + region + '.amazonaws.com'
endpoint = 'https://' + host + '/'

credential_scopeとsigning_keyのとこはautoscalingじゃなくてautoscaling-plansじゃないとだめだった。

service_alt = 'autoscaling-plans'

algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp + '/' + region + '/' + service_alt + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(
    canonical_request.encode('utf-8')).hexdigest()

signing_key = getSignatureKey(secret_key, date_stamp, region, service_alt)

おわりに

この辺のややこしいのを全部やってくれるCLIさんが好き。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?