cloudmason2 1.6.0 → 1.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/new_app.js +63 -11
- package/package.json +1 -1
package/commands/new_app.js
CHANGED
|
@@ -134,21 +134,27 @@ async function ensureGithubRole(repo, bucket, appPrefix, appName, productId){
|
|
|
134
134
|
|
|
135
135
|
const oidcArn = await ensureOidcProvider(iam, accountId);
|
|
136
136
|
|
|
137
|
+
// Reuse the role if it already exists - the permissions policy below is
|
|
138
|
+
// still re-applied, so re-running new-app refreshes an existing role
|
|
139
|
+
let roleArn = null;
|
|
137
140
|
try {
|
|
138
141
|
const existing = await iam.send(new GetRoleCommand({ RoleName: roleName }));
|
|
139
142
|
console.log(`Role ${roleName} already exists (${existing.Role.Arn})`);
|
|
140
|
-
|
|
143
|
+
roleArn = existing.Role.Arn;
|
|
141
144
|
} catch (e){
|
|
142
145
|
if (!/NoSuchEntity/.test(e)){ throw e }
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
if (!roleArn){
|
|
149
|
+
// Trust: only GitHub Actions workflows from this repo
|
|
150
|
+
const trustPolicy = exports.githubTrustPolicy(oidcArn, repo);
|
|
151
|
+
const created = await iam.send(new CreateRoleCommand({
|
|
152
|
+
RoleName: roleName,
|
|
153
|
+
AssumeRolePolicyDocument: JSON.stringify(trustPolicy),
|
|
154
|
+
Description: `Mason CI role for ${appName} (github.com/${repo})`
|
|
155
|
+
}));
|
|
156
|
+
roleArn = created.Role.Arn;
|
|
157
|
+
}
|
|
152
158
|
|
|
153
159
|
// Scoped to the app prefix, product id, and app stack names where the
|
|
154
160
|
// services support it. EC2 build permissions cannot be scoped
|
|
@@ -187,6 +193,52 @@ async function ensureGithubRole(repo, bucket, appPrefix, appName, productId){
|
|
|
187
193
|
Action: ["cloudformation:CreateStack", "cloudformation:UpdateStack", "cloudformation:DescribeStacks"],
|
|
188
194
|
Resource: `arn:aws:cloudformation:*:${accountId}:stack/${appName}-*/*`
|
|
189
195
|
},
|
|
196
|
+
{
|
|
197
|
+
Sid: "ReadTemplateSummary",
|
|
198
|
+
Effect: "Allow",
|
|
199
|
+
// launch calls GetTemplateSummary with a TemplateURL, which IAM
|
|
200
|
+
// evaluates against * rather than a stack ARN
|
|
201
|
+
Action: "cloudformation:GetTemplateSummary",
|
|
202
|
+
Resource: "*"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
Sid: "LaunchStackServices",
|
|
206
|
+
Effect: "Allow",
|
|
207
|
+
// CloudFormation provisions stack resources with the caller's
|
|
208
|
+
// credentials. Certs, DNS records, and user pools get
|
|
209
|
+
// unpredictable ARNs, so these cannot be resource-scoped
|
|
210
|
+
Action: ["acm:*", "route53:*", "cognito-idp:*"],
|
|
211
|
+
Resource: "*"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
Sid: "AppStackIam",
|
|
215
|
+
Effect: "Allow",
|
|
216
|
+
Action: [
|
|
217
|
+
"iam:CreateRole", "iam:DeleteRole", "iam:GetRole", "iam:UpdateRole", "iam:TagRole", "iam:UntagRole",
|
|
218
|
+
"iam:PutRolePolicy", "iam:DeleteRolePolicy", "iam:GetRolePolicy", "iam:ListRolePolicies",
|
|
219
|
+
"iam:AttachRolePolicy", "iam:DetachRolePolicy", "iam:ListAttachedRolePolicies", "iam:PassRole",
|
|
220
|
+
"iam:CreateInstanceProfile", "iam:DeleteInstanceProfile", "iam:GetInstanceProfile",
|
|
221
|
+
"iam:AddRoleToInstanceProfile", "iam:RemoveRoleFromInstanceProfile", "iam:TagInstanceProfile"
|
|
222
|
+
],
|
|
223
|
+
// CFN-generated role/profile names start with the stack name,
|
|
224
|
+
// and launch names every stack <app>-<title>
|
|
225
|
+
Resource: [
|
|
226
|
+
`arn:aws:iam::${accountId}:role/${appName}-*`,
|
|
227
|
+
`arn:aws:iam::${accountId}:instance-profile/${appName}-*`
|
|
228
|
+
]
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
Sid: "AppStackBuckets",
|
|
232
|
+
Effect: "Allow",
|
|
233
|
+
Action: "s3:*",
|
|
234
|
+
Resource: [`arn:aws:s3:::${appName}-*`, `arn:aws:s3:::${appName}-*/*`]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
Sid: "AppStackTables",
|
|
238
|
+
Effect: "Allow",
|
|
239
|
+
Action: "dynamodb:*",
|
|
240
|
+
Resource: `arn:aws:dynamodb:*:${accountId}:table/${appName}-*`
|
|
241
|
+
},
|
|
190
242
|
{
|
|
191
243
|
Sid: "ReadRoleParam",
|
|
192
244
|
Effect: "Allow",
|
|
@@ -207,9 +259,9 @@ async function ensureGithubRole(repo, bucket, appPrefix, appName, productId){
|
|
|
207
259
|
PolicyDocument: JSON.stringify(policy)
|
|
208
260
|
}));
|
|
209
261
|
|
|
210
|
-
console.log(`
|
|
211
|
-
console.log(`Use in GitHub Actions with: role-to-assume: ${
|
|
212
|
-
return
|
|
262
|
+
console.log(`Configured role ${roleName} (${roleArn})`);
|
|
263
|
+
console.log(`Use in GitHub Actions with: role-to-assume: ${roleArn}`);
|
|
264
|
+
return roleArn;
|
|
213
265
|
}
|
|
214
266
|
|
|
215
267
|
async function ensureOidcProvider(iam, accountId){
|