Skip to content

Commit f8392ff

Browse files
Use inline policy
1 parent 90771e7 commit f8392ff

File tree

2 files changed

+133
-95
lines changed

2 files changed

+133
-95
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ on:
55
branches: [main]
66
push:
77
branches: [main]
8-
release:
9-
types: [created]
108

119
permissions:
1210
id-token: write
@@ -98,7 +96,7 @@ jobs:
9896
run: npx semantic-release
9997
env:
10098
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
101-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
99+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
102100
- name: Publish to npm
103101
run: npm publish
104102
env:

src/infraDeploy.ts

Lines changed: 132 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@ import {
66
PublishLayerVersionCommand,
77
UpdateFunctionConfigurationCommand,
88
GetFunctionCommand,
9+
ListLayersCommand,
910
} from "@aws-sdk/client-lambda";
1011
import {
1112
IAMClient,
12-
CreatePolicyCommand,
13-
AttachRolePolicyCommand,
14-
GetPolicyCommand,
13+
GetRolePolicyCommand,
14+
PutRolePolicyCommand,
1515
} from "@aws-sdk/client-iam";
1616
import { getDebuggerId, getVersion, moduleDirname } from "./lldebugger.js";
17-
import { Policy } from "@aws-sdk/client-iam";
18-
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
17+
import { STSClient } from "@aws-sdk/client-sts";
1918
import * as Functions from "./functions.js";
2019
import fs from "fs/promises";
2120
import * as path from "path";
@@ -24,6 +23,19 @@ const lambdaClient = new LambdaClient({});
2423
const stsClient = new STSClient({});
2524
const iamClient = new IAMClient({});
2625

26+
const inlinePolicyName = "LambdaLiveDebuggerPolicy";
27+
28+
const policyDocument = {
29+
Version: "2012-10-17",
30+
Statement: [
31+
{
32+
Action: "iot:*",
33+
Resource: "*",
34+
Effect: "Allow",
35+
},
36+
],
37+
};
38+
2739
async function findExistingLayer(layerName: string) {
2840
const listLayerVersionsCommand = new ListLayerVersionsCommand({
2941
LayerName: layerName,
@@ -37,7 +49,7 @@ async function findExistingLayer(layerName: string) {
3749
return undefined;
3850
}
3951

40-
async function deployLayer(layerName: string) {
52+
async function deployLayer() {
4153
const layerDescription = `Lambda Live Debugger Layer version ${await getVersion()}`;
4254

4355
let layerZipPathFullPath = path.join(
@@ -103,6 +115,68 @@ async function deployLayer(layerName: string) {
103115
return response.LayerVersionArn;
104116
}
105117

118+
async function deleteLayer() {
119+
let nextMarker: string | undefined;
120+
do {
121+
const layers = await lambdaClient.send(
122+
new ListLayersCommand({
123+
Marker: nextMarker,
124+
MaxItems: 10,
125+
})
126+
);
127+
128+
// Filter layers by name
129+
const targetLayers =
130+
layers.Layers?.filter((layer) => layer.LayerName === layerName) || [];
131+
132+
for (const layer of targetLayers) {
133+
await deleteAllVersionsOfLayer(layer.LayerArn!);
134+
}
135+
136+
nextMarker = layers.NextMarker;
137+
} while (nextMarker);
138+
}
139+
140+
async function deleteAllVersionsOfLayer(layerArn: string): Promise<void> {
141+
let nextMarker: string | undefined;
142+
do {
143+
const versions = await lambdaClient.send(
144+
new ListLayerVersionsCommand({
145+
LayerName: layerArn,
146+
Marker: nextMarker,
147+
//MaxItems: 5,
148+
})
149+
);
150+
151+
for (const version of versions.LayerVersions || []) {
152+
await deleteLayerVersion(layerArn, version.Version!);
153+
}
154+
155+
nextMarker = versions.NextMarker;
156+
} while (nextMarker);
157+
}
158+
159+
async function deleteLayerVersion(
160+
layerArn: string,
161+
versionNumber: number
162+
): Promise<void> {
163+
try {
164+
await lambdaClient.send(
165+
new DeleteLayerVersionCommand({
166+
LayerName: layerArn,
167+
VersionNumber: versionNumber,
168+
})
169+
);
170+
console.log(`Deleted version ${versionNumber} of layer ${layerArn}`);
171+
} catch (error) {
172+
console.error(
173+
`Error deleting version ${versionNumber} of layer ${layerArn}:`,
174+
error
175+
);
176+
throw error;
177+
}
178+
}
179+
106180
async function attachLayerToFunction(
107181
functionName: string,
108182
functionId: string,
@@ -143,75 +217,7 @@ async function attachLayerToFunction(
143217
console.log("Function configuration updated");
144218
}
145219

146-
async function findOrCreatePolicy(): Promise<string> {
147-
const version = await getVersion();
148-
const policyName = `lambda-live-debugger-policy-v${version.replace(
149-
/\./g,
150-
"-"
151-
)}`;
152-
153-
let existingPolicy: Policy | undefined;
154-
155-
try {
156-
// get account number
157-
const { Account: account } = await stsClient.send(
158-
new GetCallerIdentityCommand({})
159-
);
160-
161-
const response = await iamClient.send(
162-
new GetPolicyCommand({
163-
PolicyArn: `arn:aws:iam::${account}:policy/${policyName}`,
164-
})
165-
);
166-
existingPolicy = response.Policy;
167-
} catch (e: any) {
168-
if (e.name === "NoSuchEntityException") {
169-
// did not find the policy
170-
} else {
171-
throw e;
172-
}
173-
}
174-
175-
if (existingPolicy) {
176-
if (!existingPolicy.Arn) {
177-
throw new Error(
178-
"Failed to retrieve the policy ARN of the existing policy"
179-
);
180-
}
181-
return existingPolicy.Arn;
182-
}
183-
184-
const policyDocument = {
185-
Version: "2012-10-17",
186-
Statement: [
187-
{
188-
Action: "iot:*",
189-
Resource: "*",
190-
Effect: "Allow",
191-
},
192-
],
193-
};
194-
195-
// If not found, create a new policy
196-
const createPolicyResponse = await iamClient.send(
197-
new CreatePolicyCommand({
198-
PolicyName: policyName,
199-
PolicyDocument: JSON.stringify(policyDocument),
200-
})
201-
);
202-
console.log(`Policy ${policyName} created`);
203-
204-
if (!createPolicyResponse.Policy?.Arn) {
205-
throw new Error("Failed to retrieve the policy ARN of the new policy");
206-
}
207-
208-
return createPolicyResponse.Policy?.Arn;
209-
}
210-
211-
async function attachPolicyToLambdaRole(
212-
functionName: string,
213-
policyArn: string
214-
) {
220+
async function addPolicyToLambdaRole(functionName: string) {
215221
// Retrieve the Lambda function's execution role ARN
216222
const getFunctionResponse = await lambdaClient.send(
217223
new GetFunctionCommand({
@@ -220,44 +226,78 @@ async function attachPolicyToLambdaRole(
220226
);
221227
const roleArn = getFunctionResponse.Configuration?.Role;
222228
if (!roleArn) {
223-
console.error("Failed to retrieve function role ARN");
224-
return;
229+
throw new Error(
230+
`Failed to retrieve the role ARN for function ${functionName}`
231+
);
225232
}
226233

227234
// Extract the role name from the role ARN
228235
const roleName = roleArn.split("/").pop();
229236

230-
if (!policyArn || !roleName) {
231-
console.error("Failed to proceed without policy ARN or role name");
232-
return;
237+
if (!roleName) {
238+
throw new Error(
239+
`Failed to extract role name from role ARN: ${roleArn} for function ${functionName}`
240+
);
233241
}
234242

235-
// Attach the policy to the role
236-
await iamClient.send(
237-
new AttachRolePolicyCommand({
238-
RoleName: roleName,
239-
PolicyArn: policyArn,
240-
})
241-
);
242-
console.log(`Policy attached to Lambda role`);
243+
const existingPolicy = getPolicyDocument(roleName);
244+
245+
let addPolicy: boolean = true;
246+
247+
// compare existing policy with the new one
248+
if (existingPolicy) {
249+
if (JSON.stringify(existingPolicy) === JSON.stringify(policyDocument)) {
250+
console.log(
251+
`Correct policy already attached to the role ${roleName} for function ${functionName}`
252+
);
253+
addPolicy = false;
254+
}
255+
}
256+
257+
if (addPolicy) {
258+
// add inline policy to the role using PutRolePolicyCommand
259+
await iamClient.send(
260+
new PutRolePolicyCommand({
261+
RoleName: roleName,
262+
PolicyName: inlinePolicyName,
263+
PolicyDocument: JSON.stringify(policyDocument),
264+
})
265+
);
266+
}
267+
}
268+
269+
async function getPolicyDocument(roleName: string) {
270+
try {
271+
const policy = await iamClient.send(
272+
new GetRolePolicyCommand({
273+
RoleName: roleName,
274+
PolicyName: inlinePolicyName,
275+
})
276+
);
277+
278+
return policy.PolicyDocument;
279+
} catch (error) {
280+
// console.error(
281+
// `Error retrieving policy document for ${inlinePolicyName}:`,
282+
// error
283+
// );
284+
}
243285
}
244286

245287
const layerName = "LambdaLiveDebugger"; // Replace with your layer name
246288

247289
export async function deployInfrastructure() {
248-
const layerVersionArnPromise = deployLayer(layerName);
249-
const policyArnPromise = findOrCreatePolicy();
290+
const layerVersionArnPromise = deployLayer();
250291

251292
const layerVersionArn = await layerVersionArnPromise;
252-
const policyArn = await policyArnPromise;
253293

254294
const promises: Promise<void>[] = [];
255295

256296
for (const func of Functions.getFunctions()) {
257297
promises.push(
258298
attachLayerToFunction(func.functionName, func.functionId, layerVersionArn)
259299
);
260-
promises.push(attachPolicyToLambdaRole(func.functionName, policyArn));
300+
promises.push(addPolicyToLambdaRole(func.functionName));
261301
}
262302

263303
await Promise.all(promises);

0 commit comments

Comments
 (0)