javascript SDKで、次のような流れの処理を書こうとしていたのですが、EC2の起動時にRoleが渡せなくて週末の夜が潰れてしまったので、似たようなことをする方向けにメモしておきます。
- 一時的にRoleを作成
- EC2インスタンスを起動
- UserDataで渡したスクリプト内でゴニョゴニョする
- Roleを削除
コードは次のような感じになります。
PolicyArnには起動するEC2に許可したいポリシーのArnが入っていて、ec2Paramにはインスタンス起動時に必要な他のパラメータが全部入っていると考えてください。
const AssumeRolePolicyDocument = JSON.stringify({
Version: "2012-10-17",
Statement: {
Effect: "Allow",
Principal: { Service: "ec2.amazonaws.com" },
Action: "sts:AssumeRole"
}
});
const iam = new AWS.IAM()
await iam.createRole({ RoleName, AssumeRolePolicyDocument }).promise();
await iam.attachRolePolicy({ RoleName, PolicyArn}).promise();
const InstanceProfileName = RoleName;
await iam.createInstanceProfile({ InstanceProfileName }).promise();
await iam.addRoleToInstanceProfile({ InstanceProfileName, RoleName }).promise();
const ec2 = new AWS.EC2();
ec2Param.IamInstanceProfile={ Name: InstanceProfileName };
await ec2.runInstances(ec2Param).promise();
特に問題の無いコードに見えますが、これだとrunInstancesで InvalidParameterValue: Value (****) for parameter iamInstanceProfile.name is invalid. Invalid IAM Instance Profile name
と言われて起動しません。
waitForでinstanceProfileExistsを待ってからrunInstancesを呼んでも状況は変わらず、どうしたもんかと悩んでいたんですがAWS forumの古い記事に答えが書いてありました(この方はRuby sdkでやっていたようです)
Many of the AWS APIs are eventually consistent. While the API call responds right away, it can take a variable amount of time before that resource is ready. In this case, the IAM instance profile is not available for use outside IAM yet.
つまり、IAM側で有効になっててもEC2側からは未だ見えない可能性があるよ!!ってことですね。
このフォーラムでは、ちょっと待ったらOKだったよ!みたいな話になってますが、こっちを使ってrunInstancesに成功するまで頑張ってもらった方が良さそうです。
色々ハードコーディングしていますが、こんな感じのコードで、Instance Profile name絡みでエラーが出たら、3000ミリ秒の間隔を空けて、10回リトライします。
let retryCount = 0;
const result = await ec2.runInstances(ec2Param)
.on("retry", (res)=>{
if (retryCount < 10 && res.error.code === "InvalidParameterValue" && res.error.message.endsWith("Invalid IAM Instance Profile name")) {
res.error.retryable = true;
res.error.retryDelay = 3000;
++retryCount;
}
})
.promise();
2019/2/13追記
上のコード例で、callbackルーチン内でリトライ回数の判定をしていましたが、AWSConfig側にmaxRetriesというプロパティがあって、EC2の場合は3に設定されているようです。したがって、このコードのようにリトライ回数のチェックをrunInstancesの呼び出し毎に行なう場合は、EC2のコンストラクタに渡すオプションか、AWS.Config()を事前に呼び出してこちらで設定した回数より大きい値を設定しておく必要があります。
また、複数回runInstancesの呼び出しがあってリトライ回数は同じで良いような場合は、callback内でのカウントを止めて、Config側で設定してしまえば十分です。
この記事は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。