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.
Files changed (2) hide show
  1. package/commands/new_app.js +63 -11
  2. package/package.json +1 -1
@@ -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
- return existing.Role.Arn;
143
+ roleArn = existing.Role.Arn;
141
144
  } catch (e){
142
145
  if (!/NoSuchEntity/.test(e)){ throw e }
143
146
  }
144
147
 
145
- // Trust: only GitHub Actions workflows from this repo
146
- const trustPolicy = exports.githubTrustPolicy(oidcArn, repo);
147
- const created = await iam.send(new CreateRoleCommand({
148
- RoleName: roleName,
149
- AssumeRolePolicyDocument: JSON.stringify(trustPolicy),
150
- Description: `Mason CI role for ${appName} (github.com/${repo})`
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(`Created role ${roleName} (${created.Role.Arn})`);
211
- console.log(`Use in GitHub Actions with: role-to-assume: ${created.Role.Arn}`);
212
- return created.Role.Arn;
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){
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmason2",
3
- "version": "1.6.0",
3
+ "version": "1.7.9",
4
4
  "description": "",
5
5
  "main": "main.js",
6
6
  "files": [