2
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 1 year has passed since last update.

gasからAWSのV4署名を作成して、APIでSESからメールを送信する

Last updated at Posted at 2021-09-30

Google SpreadSheetのgasからAWSのV4署名を作成して、APIでSESから別ドメインのメールを送信する。
少しニッチなニーズ(死語)ですがドキュメントオーナーのgmailアドレスに依存しない形になるので、可用性は高まります。
セキュリティ、メールに関する知識が足りないので、足りない部分はコメント頂けると幸いです。

https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/send-email-api.html
(参考:公式 Amazon SES API を使用して E メールを送信する)

直接 HTTPS リクエストを作成する - これは、リクエストの認証と署名を手動で処理し、リクエストを手動で作成する必要があるため、最も高度な方法です。リクエストの実行方法の詳細については、「Amazon SES API の使用」を参照してください。

最も高度な方法……
本稿では、gas内でAWSのV4署名を作成して、その署名を使用して、APIを叩いて、メールを送信するまで、です。GETのみ、POSTもできるようですが、検証までの時間が足りませんでした。

1.V4 署名の作成

https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4-signed-request-examples.html
(AWS公式:完全な署名バージョン 4 署名プロセスの例 (Python))
公式にPythonのサンプルコードがあるので、そのままgasに置換して作成します。

以下、実装です。

/**
 * AWSのV4署名を作成する 
 * 
 * @param method GET/POST
 * @param host 使用するAPIのエンドポイントのHOST
 * @param region 使用するAPIのリージョン
 * @param service 使用するAPIのサービス EC2とかmailとか
 * @param dateStamp 現在日付 new Date(), "UTC", "yyyyMMdd"
 * @param amzdate aws形式の日付 new Date(), "UTC", "yyyyMMdd'T'HHmmss'Z'"
 * @param requestParameters リクエストパラメータ Action=CreateUser&UserName=NewUser&Version=2010-05-08
 * @param signedHeaders host;x-amz-date 固定 サンプルがそうだったから
 * @param secretAccessKey アクセスする秘密鍵
 */
function getSignature(method, host, region, service, dateStamp, amzdate, requestParameters, signedHeaders, secretAccessKey) {

// ************* TASK 1: CREATE A CANONICAL REQUEST *************
// http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
// Step 1 is to define the verb (GET, POST, etc.)--already done.
// Step 2: Create canonical URI--the part of the URI from domain to query 
// string (use '/' if no path)
  let canonicalUri = '/';

// Step 3: Create the canonical query string. In this example (a GET request),
// request parameters are in the query string. Query string values must
// be URL-encoded (space=%20). The parameters must be sorted by name.
// For this example, the query string is pre-formatted in the request_parameters variable.
  let canonicalQuerystring = requestParameters;

// Step 4: Create the canonical headers and signed headers. Header names
// must be trimmed and lowercase, and sorted in code point order from
// low to high. Note that there is a trailing \n.
  let canonicalHeaders = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n';

// Step 5: Create the list of signed headers. This lists the headers
// in the canonical_headers list, delimited with ";" and in alpha order.
// Note: The request can include any headers; canonical_headers and
// signed_headers lists those that you want to be included in the 
// hash of the request. "Host" and "x-amz-date" are always required.
// let signedHeaders = 'host;x-amz-date';

// Step 6: Create payload hash (hash of the request body content). For GET
// requests, the payload is an empty string ("").
  let payloadHash = getSHA256('');

// Step 7: Combine elements to create canonical request
  let canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQuerystring + '\n' + canonicalHeaders + '\n' + signedHeaders + '\n' + payloadHash;

// ************* TASK 2: CREATE THE STRING TO SIGN*************
// Match the algorithm to the hashing algorithm you use, either SHA-1 or
// SHA-256 (recommended)

  let algorithm = 'AWS4-HMAC-SHA256'
  let credentialScope = dateStamp + '/' + region + '/' + service + '/' + 'aws4_request';
  let stringToSign = algorithm + '\n' +  amzdate + '\n' +  credentialScope + '\n' + getSHA256(canonicalRequest);

  let signaturekey = getSignatureKey(secretAccessKey, dateStamp, region, service);
  let signature = getHmacSha256Signature(signaturekey, stringToSign, 'HEX');

  return signature
}

2.署名キーの作成

署名を作成するために、署名キーを作成する必要があります。
前段の署名を作成するソースコードでは、下記関数があたります。
getSignatureKey

https://docs.aws.amazon.com/ja_jp/general/latest/gr/signature-v4-examples.html
(AWS公式:署名バージョン 4 の署名キーを取得する方法の例)
署名キーもサンプルがあるので、そのままgasにします。
HmacSHA256を作成するために、最初は、gasのutilities.computeHmacSha256Signatureを使用したところ取得できる値がサンプルと違うため、確認したところ、バイナリ形式で鍵を送らないといけないみたいでした。

https://creators-note.chatwork.com/entry/2017/12/20/163128
(参考:Google Apps ScriptでChatWorkのWebhook署名を検証する方法)
Chatworkの中の人が、バイナリ形式で送る方法書いてくれていたので、参考にしました。

コードを見て気づいた方もいらっしゃるかもしれませんが、今回GASで標準で提供されるUtilities.computeHmacSha256Signature)ではなく、jsSHA - SHA Hashes in JavaScriptを直接埋め込んでいます。

これはUtilities.computeHmacSignatureがバイナリ形式の入力に対応しておらず、署名の検証に使用することができないためです。

同じくコードを貼り付け、HEXの形から、鍵を作れるようになりました。

https://stackoverflow.com/questions/41232615/how-to-get-hex-value-from-computehmacsha256signature-method-of-google-apps-scrip
(参考)
stackoverflowでも、私と同じようにサンプルと同じようにならない、間違っているのではないか、と書いている人がいました。
ちゃんと公式に書いているんですけどね

これらはバイナリデータの 16 進エンコード表現ですが、キー自体と中間値はバイナリ形式となることに注意してください。

サンプルの値の鍵がページにあるので、途中経過の計算があっているかは確認できます。

kSecret  = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate    = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion  = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'

getでも、空のpayloadをダイジェスト値にして送らないといけないため、ダイジェスト値の作成は以下を参考にしました。
https://qiita.com/SogoK/items/cc0d514ffe74009e5fd5

以下、実装です。

function getSignatureKey(key, dateStamp, region, service) {
  let keyDateStamp = getHmacSha256Signature('AWS4' + key, dateStamp, 'TEXT');
  let keyRegion = getHmacSha256Signature(keyDateStamp, region, 'HEX');
  let keyService = getHmacSha256Signature(keyRegion, service, 'HEX');
  let keySinging = getHmacSha256Signature(keyService, 'aws4_request', 'HEX');
  return keySinging;
}
function getHmacSha256Signature(key, value, keyFormat = 'TEXT')
{
  let shaObj = new jsSHA("SHA-256", "TEXT");
  shaObj.setHMACKey(key, keyFormat);
  shaObj.update(value);
  let signature = shaObj.getHMAC("HEX");
  Logger.log(`key: ${key} value:${value} signature:${signature}`)
  return signature 
}

//https://qiita.com/SogoK/items/cc0d514ffe74009e5fd5
function getSHA256(input) {
  var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, input, Utilities.Charset.UTF_8);
  var txtHash = '';
  for (i = 0; i < rawHash.length; i++) {
    var hashVal = rawHash[i];
    if (hashVal < 0) {
      hashVal += 256;
    }
    if (hashVal.toString(16).length == 1) {
      txtHash += '0';
    }
    txtHash += hashVal.toString(16);
  }
  return txtHash;
}

//https://creators-note.chatwork.com/entry/2017/12/20/163128
'use strict';(function(I){function w(c,a,d){var l=0,b=[],g=0,f,n,k,e,h,q,y,p,m=!1,t=[],r=[],u,z=!1;d=d||{};f=d.encoding||"UTF8";u=d.numRounds||1;if(u!==parseInt(u,10)||1>u)throw Error("numRounds must a integer >= 1");if(0===c.lastIndexOf("SHA-",0))if(q=function(b,a){return A(b,a,c)},y=function(b,a,l,f){var g,e;if("SHA-224"===c||"SHA-256"===c)g=(a+65>>>9<<4)+15,e=16;else throw Error("Unexpected error in SHA-2 implementation");for(;b.length<=g;)b.push(0);b[a>>>5]|=128<<24-a%32;a=a+l;b[g]=a&4294967295;
b[g-1]=a/4294967296|0;l=b.length;for(a=0;a<l;a+=e)f=A(b.slice(a,a+e),f,c);if("SHA-224"===c)b=[f[0],f[1],f[2],f[3],f[4],f[5],f[6]];else if("SHA-256"===c)b=f;else throw Error("Unexpected error in SHA-2 implementation");return b},p=function(b){return b.slice()},"SHA-224"===c)h=512,e=224;else if("SHA-256"===c)h=512,e=256;else throw Error("Chosen SHA variant is not supported");else throw Error("Chosen SHA variant is not supported");k=B(a,f);n=x(c);this.setHMACKey=function(b,a,g){var e;if(!0===m)throw Error("HMAC key already set");
if(!0===z)throw Error("Cannot set HMAC key after calling update");f=(g||{}).encoding||"UTF8";a=B(a,f)(b);b=a.binLen;a=a.value;e=h>>>3;g=e/4-1;if(e<b/8){for(a=y(a,b,0,x(c));a.length<=g;)a.push(0);a[g]&=4294967040}else if(e>b/8){for(;a.length<=g;)a.push(0);a[g]&=4294967040}for(b=0;b<=g;b+=1)t[b]=a[b]^909522486,r[b]=a[b]^1549556828;n=q(t,n);l=h;m=!0};this.update=function(a){var c,f,e,d=0,p=h>>>5;c=k(a,b,g);a=c.binLen;f=c.value;c=a>>>5;for(e=0;e<c;e+=p)d+h<=a&&(n=q(f.slice(e,e+p),n),d+=h);l+=d;b=f.slice(d>>>
5);g=a%h;z=!0};this.getHash=function(a,f){var d,h,k,q;if(!0===m)throw Error("Cannot call getHash after setting HMAC key");k=C(f);switch(a){case "HEX":d=function(a){return D(a,e,k)};break;case "B64":d=function(a){return E(a,e,k)};break;case "BYTES":d=function(a){return F(a,e)};break;case "ARRAYBUFFER":try{h=new ArrayBuffer(0)}catch(v){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a){return G(a,e)};break;default:throw Error("format must be HEX, B64, BYTES, or ARRAYBUFFER");
}q=y(b.slice(),g,l,p(n));for(h=1;h<u;h+=1)q=y(q,e,0,x(c));return d(q)};this.getHMAC=function(a,f){var d,k,t,u;if(!1===m)throw Error("Cannot call getHMAC without first setting HMAC key");t=C(f);switch(a){case "HEX":d=function(a){return D(a,e,t)};break;case "B64":d=function(a){return E(a,e,t)};break;case "BYTES":d=function(a){return F(a,e)};break;case "ARRAYBUFFER":try{d=new ArrayBuffer(0)}catch(v){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a){return G(a,e)};break;default:throw Error("outputFormat must be HEX, B64, BYTES, or ARRAYBUFFER");
}k=y(b.slice(),g,l,p(n));u=q(r,x(c));u=y(k,e,h,u);return d(u)}}function m(){}function D(c,a,d){var l="";a/=8;var b,g;for(b=0;b<a;b+=1)g=c[b>>>2]>>>8*(3+b%4*-1),l+="0123456789abcdef".charAt(g>>>4&15)+"0123456789abcdef".charAt(g&15);return d.outputUpper?l.toUpperCase():l}function E(c,a,d){var l="",b=a/8,g,f,n;for(g=0;g<b;g+=3)for(f=g+1<b?c[g+1>>>2]:0,n=g+2<b?c[g+2>>>2]:0,n=(c[g>>>2]>>>8*(3+g%4*-1)&255)<<16|(f>>>8*(3+(g+1)%4*-1)&255)<<8|n>>>8*(3+(g+2)%4*-1)&255,f=0;4>f;f+=1)8*g+6*f<=a?l+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(n>>>
6*(3-f)&63):l+=d.b64Pad;return l}function F(c,a){var d="",l=a/8,b,g;for(b=0;b<l;b+=1)g=c[b>>>2]>>>8*(3+b%4*-1)&255,d+=String.fromCharCode(g);return d}function G(c,a){var d=a/8,l,b=new ArrayBuffer(d),g;g=new Uint8Array(b);for(l=0;l<d;l+=1)g[l]=c[l>>>2]>>>8*(3+l%4*-1)&255;return b}function C(c){var a={outputUpper:!1,b64Pad:"=",shakeLen:-1};c=c||{};a.outputUpper=c.outputUpper||!1;!0===c.hasOwnProperty("b64Pad")&&(a.b64Pad=c.b64Pad);if("boolean"!==typeof a.outputUpper)throw Error("Invalid outputUpper formatting option");
if("string"!==typeof a.b64Pad)throw Error("Invalid b64Pad formatting option");return a}function B(c,a){var d;switch(a){case "UTF8":case "UTF16BE":case "UTF16LE":break;default:throw Error("encoding must be UTF8, UTF16BE, or UTF16LE");}switch(c){case "HEX":d=function(a,b,c){var f=a.length,d,k,e,h,q;if(0!==f%2)throw Error("String of HEX type must be in byte increments");b=b||[0];c=c||0;q=c>>>3;for(d=0;d<f;d+=2){k=parseInt(a.substr(d,2),16);if(isNaN(k))throw Error("String of HEX type contains invalid characters");
h=(d>>>1)+q;for(e=h>>>2;b.length<=e;)b.push(0);b[e]|=k<<8*(3+h%4*-1)}return{value:b,binLen:4*f+c}};break;case "TEXT":d=function(c,b,d){var f,n,k=0,e,h,q,m,p,r;b=b||[0];d=d||0;q=d>>>3;if("UTF8"===a)for(r=3,e=0;e<c.length;e+=1)for(f=c.charCodeAt(e),n=[],128>f?n.push(f):2048>f?(n.push(192|f>>>6),n.push(128|f&63)):55296>f||57344<=f?n.push(224|f>>>12,128|f>>>6&63,128|f&63):(e+=1,f=65536+((f&1023)<<10|c.charCodeAt(e)&1023),n.push(240|f>>>18,128|f>>>12&63,128|f>>>6&63,128|f&63)),h=0;h<n.length;h+=1){p=k+
q;for(m=p>>>2;b.length<=m;)b.push(0);b[m]|=n[h]<<8*(r+p%4*-1);k+=1}else if("UTF16BE"===a||"UTF16LE"===a)for(r=2,n="UTF16LE"===a&&!0||"UTF16LE"!==a&&!1,e=0;e<c.length;e+=1){f=c.charCodeAt(e);!0===n&&(h=f&255,f=h<<8|f>>>8);p=k+q;for(m=p>>>2;b.length<=m;)b.push(0);b[m]|=f<<8*(r+p%4*-1);k+=2}return{value:b,binLen:8*k+d}};break;case "B64":d=function(a,b,c){var f=0,d,k,e,h,q,m,p;if(-1===a.search(/^[a-zA-Z0-9=+\/]+$/))throw Error("Invalid character in base-64 string");k=a.indexOf("=");a=a.replace(/\=/g,
"");if(-1!==k&&k<a.length)throw Error("Invalid '=' found in base-64 string");b=b||[0];c=c||0;m=c>>>3;for(k=0;k<a.length;k+=4){q=a.substr(k,4);for(e=h=0;e<q.length;e+=1)d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(q[e]),h|=d<<18-6*e;for(e=0;e<q.length-1;e+=1){p=f+m;for(d=p>>>2;b.length<=d;)b.push(0);b[d]|=(h>>>16-8*e&255)<<8*(3+p%4*-1);f+=1}}return{value:b,binLen:8*f+c}};break;case "BYTES":d=function(a,b,c){var d,n,k,e,h;b=b||[0];c=c||0;k=c>>>3;for(n=0;n<a.length;n+=
1)d=a.charCodeAt(n),h=n+k,e=h>>>2,b.length<=e&&b.push(0),b[e]|=d<<8*(3+h%4*-1);return{value:b,binLen:8*a.length+c}};break;case "ARRAYBUFFER":try{d=new ArrayBuffer(0)}catch(l){throw Error("ARRAYBUFFER not supported by this environment");}d=function(a,b,c){var d,n,k,e,h;b=b||[0];c=c||0;n=c>>>3;h=new Uint8Array(a);for(d=0;d<a.byteLength;d+=1)e=d+n,k=e>>>2,b.length<=k&&b.push(0),b[k]|=h[d]<<8*(3+e%4*-1);return{value:b,binLen:8*a.byteLength+c}};break;default:throw Error("format must be HEX, TEXT, B64, BYTES, or ARRAYBUFFER");
}return d}function r(c,a){return c>>>a|c<<32-a}function J(c,a,d){return c&a^~c&d}function K(c,a,d){return c&a^c&d^a&d}function L(c){return r(c,2)^r(c,13)^r(c,22)}function M(c){return r(c,6)^r(c,11)^r(c,25)}function N(c){return r(c,7)^r(c,18)^c>>>3}function O(c){return r(c,17)^r(c,19)^c>>>10}function P(c,a){var d=(c&65535)+(a&65535);return((c>>>16)+(a>>>16)+(d>>>16)&65535)<<16|d&65535}function Q(c,a,d,l){var b=(c&65535)+(a&65535)+(d&65535)+(l&65535);return((c>>>16)+(a>>>16)+(d>>>16)+(l>>>16)+(b>>>
16)&65535)<<16|b&65535}function R(c,a,d,l,b){var g=(c&65535)+(a&65535)+(d&65535)+(l&65535)+(b&65535);return((c>>>16)+(a>>>16)+(d>>>16)+(l>>>16)+(b>>>16)+(g>>>16)&65535)<<16|g&65535}function x(c){var a=[],d;if(0===c.lastIndexOf("SHA-",0))switch(a=[3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428],d=[1779033703,3144134277,1013904242,2773480762,1359893119,2600822924,528734635,1541459225],c){case "SHA-224":break;case "SHA-256":a=d;break;case "SHA-384":a=[new m,new m,
new m,new m,new m,new m,new m,new m];break;case "SHA-512":a=[new m,new m,new m,new m,new m,new m,new m,new m];break;default:throw Error("Unknown SHA variant");}else throw Error("No SHA variants supported");return a}function A(c,a,d){var l,b,g,f,n,k,e,h,m,r,p,w,t,x,u,z,A,B,C,D,E,F,v=[],G;if("SHA-224"===d||"SHA-256"===d)r=64,w=1,F=Number,t=P,x=Q,u=R,z=N,A=O,B=L,C=M,E=K,D=J,G=H;else throw Error("Unexpected error in SHA-2 implementation");d=a[0];l=a[1];b=a[2];g=a[3];f=a[4];n=a[5];k=a[6];e=a[7];for(p=
0;p<r;p+=1)16>p?(m=p*w,h=c.length<=m?0:c[m],m=c.length<=m+1?0:c[m+1],v[p]=new F(h,m)):v[p]=x(A(v[p-2]),v[p-7],z(v[p-15]),v[p-16]),h=u(e,C(f),D(f,n,k),G[p],v[p]),m=t(B(d),E(d,l,b)),e=k,k=n,n=f,f=t(g,h),g=b,b=l,l=d,d=t(h,m);a[0]=t(d,a[0]);a[1]=t(l,a[1]);a[2]=t(b,a[2]);a[3]=t(g,a[3]);a[4]=t(f,a[4]);a[5]=t(n,a[5]);a[6]=t(k,a[6]);a[7]=t(e,a[7]);return a}var H;H=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,
2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,
2756734187,3204031479,3329325298];"function"===typeof define&&define.amd?define(function(){return w}):"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(module.exports=w),exports=w):I.jsSHA=w})(this);

3.APIのリクエスト

作成した署名を使用してリクエストを送信します。
サンプルでは、リクエストのヘッダーにHostを付与しろと書いてありますが、gasのUrlFetchApp.fetchでは付与すると使用できないと、エラーになりました。付与しなくても、リクエストは成功するようです。
リクエストのパラメータは、ASCIIコード順に並べないと、署名に失敗します。

https://pfs.nifcloud.com/api/signature_v4.htm
(参考:ニフクラAPI(シグネチャーバージョン 4 生成方法))

パラメーター名でASCIIコード順にソートします。

最初サンプル通り、
https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/using-ses-api-examples.html
(参考:公式 Amazon SES API の GET と POST の例)

https://email.us-west-2.amazonaws.com/
?Action=SendEmail
&Source=user%40example.com
&Destination.ToAddresses.member.1=allan%40example.com
&Message.Subject.Data=This%20is%20the%20subject%20line.
&Message.Body.Text.Data=Hello.%20I%20hope%20you%20are%20having%20a%20good%20day.

としていたら署名に失敗しました。パラメータがASCII順に並んでいないためです。

https://email.us-west-2.amazonaws.com/
?Action=SendEmail
&Destination.ToAddresses.member.1=allan%40example.com
&Message.Body.Text.Data=Hello.%20I%20hope%20you%20are%20having%20a%20good%20day.
&Message.Subject.Data=This%20is%20the%20subject%20line.
&Source=user%40example.com

Destination→Message.Body→Message.Subject→Source ASCII順にして、署名を作成する必要がある

以下、実装です。


// AWS SESにpostする
function postMail(fromAddress = 'no-reply@sample.com', toAddress = 'rei-ta@sample.jp',title = 'test', body = 'body', bccAddress = '') {

  const accesskeyId = 'AKIsssssssZWH';
  const secretAccessKey = '6oddddddddddddddddZLiEKGM+KO';

  const method = 'GET';
  const host = 'email.ap-northeast-1.amazonaws.com';
  const region = 'ap-northeast-1';
  const service = 'ses';

  const encodeToAddress =  encodeURIComponent(toAddress);
  const base64Body=  encodeURIComponent(Utilities.base64Encode(`From:${fromAddress}
To: ${toAddress}
Bcc: ${bccAddress}
Subject: ${title}

${body}`,Utilities.Charset.UTF_8));
  let request_parameters = `Action=SendRawEmail&RawMessage.Data=${base64Body}`;

  requestAwsApi(method, host, region, service, accesskeyId, secretAccessKey, request_parameters);

} 

function requestAwsApi(method, host, region, service, accesskeyId, secretAccessKey, requestParameters) {

  let dateStamp = Utilities.formatDate(new Date(), "UTC", "yyyyMMdd");
  let amzdate = Utilities.formatDate(new Date(), "UTC", "yyyyMMdd'T'HHmmss'Z'");
  let signedHeaders = 'host;x-amz-date';
  let signature = getSignature(method, host, region, service, dateStamp, amzdate, requestParameters, signedHeaders, secretAccessKey);

  var headers = {
    'Content-Type': 'application/x-www-form-urlencoded',
    'X-Amz-Date': amzdate,
    'Authorization': `AWS4-HMAC-SHA256 Credential=${accesskeyId}/${dateStamp}/${region}/${service}/aws4_request, SignedHeaders=${signedHeaders}, Signature=${signature}`
  }
  Logger.log(headers)

  var options = {
    'method' : method,
    'headers' : headers,
    'muteHttpExceptions' : true,
  }
 try {
    var url = 'https://' + host + '?' + requestParameters;
    console.log(`url: ${url}`);

    var response = UrlFetchApp.fetch(url, options);
    console.log(response.getContentText());

  } catch(e) {
    // 例外エラー処理
    Logger.log('Error:')
    Logger.log(e)
  }
  return response;

}

ちょっと煩雑になっているので足りない部分があると思いますが、最低限伝えたいのはgasで、AWSのV4署名が作成できて、APIのリクエストできるか、ですが、答えは「頑張ればできる」です。
(ググっても、できるかどうかが、出てこなかったので不安になりながら作業していました。)

やるかどうかは別として、gasからAPI経由でec2のシャットダウンとかもできるはずです。

2
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
2
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?