mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Update AWS SM integration to allow updating tags
This commit is contained in:
@@ -154,7 +154,33 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
|
|||||||
.describe(INTEGRATION.UPDATE.secretPath),
|
.describe(INTEGRATION.UPDATE.secretPath),
|
||||||
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
|
targetEnvironment: z.string().trim().describe(INTEGRATION.UPDATE.targetEnvironment),
|
||||||
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
|
owner: z.string().trim().describe(INTEGRATION.UPDATE.owner),
|
||||||
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment)
|
environment: z.string().trim().describe(INTEGRATION.UPDATE.environment),
|
||||||
|
metadata: z
|
||||||
|
.object({
|
||||||
|
secretPrefix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretPrefix),
|
||||||
|
secretSuffix: z.string().optional().describe(INTEGRATION.CREATE.metadata.secretSuffix),
|
||||||
|
initialSyncBehavior: z.string().optional().describe(INTEGRATION.CREATE.metadata.initialSyncBehavoir),
|
||||||
|
shouldAutoRedeploy: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldAutoRedeploy),
|
||||||
|
secretGCPLabel: z
|
||||||
|
.object({
|
||||||
|
labelName: z.string(),
|
||||||
|
labelValue: z.string()
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.secretGCPLabel),
|
||||||
|
secretAWSTag: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
|
||||||
|
kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId),
|
||||||
|
shouldDisableDelete: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.shouldDisableDelete)
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
|||||||
@@ -9,9 +9,12 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
CreateSecretCommand,
|
CreateSecretCommand,
|
||||||
|
DescribeSecretCommand,
|
||||||
GetSecretValueCommand,
|
GetSecretValueCommand,
|
||||||
ResourceNotFoundException,
|
ResourceNotFoundException,
|
||||||
SecretsManagerClient,
|
SecretsManagerClient,
|
||||||
|
TagResourceCommand,
|
||||||
|
UntagResourceCommand,
|
||||||
UpdateSecretCommand
|
UpdateSecretCommand
|
||||||
} from "@aws-sdk/client-secrets-manager";
|
} from "@aws-sdk/client-secrets-manager";
|
||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
@@ -574,6 +577,7 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
if (awsSecretManagerSecret?.SecretString) {
|
if (awsSecretManagerSecret?.SecretString) {
|
||||||
awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString);
|
awsSecretManagerSecretObj = JSON.parse(awsSecretManagerSecret.SecretString);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEqual(awsSecretManagerSecretObj, secKeyVal)) {
|
if (!isEqual(awsSecretManagerSecretObj, secKeyVal)) {
|
||||||
await secretsManager.send(
|
await secretsManager.send(
|
||||||
new UpdateSecretCommand({
|
new UpdateSecretCommand({
|
||||||
@@ -582,7 +586,88 @@ const syncSecretsAWSSecretManager = async ({
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const secretAWSTag = metadata.secretAWSTag as { key: string; value: string }[] | undefined;
|
||||||
|
|
||||||
|
if (secretAWSTag && secretAWSTag.length) {
|
||||||
|
const describedSecret = await secretsManager.send(
|
||||||
|
// requires secretsmanager:DescribeSecret policy
|
||||||
|
new DescribeSecretCommand({
|
||||||
|
SecretId: integration.app as string
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!describedSecret.Tags) return;
|
||||||
|
|
||||||
|
const integrationTagObj = secretAWSTag.reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
acc[item.key] = item.value;
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
|
||||||
|
const awsTagObj = (describedSecret.Tags || []).reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
if (item.Key && item.Value) {
|
||||||
|
acc[item.Key] = item.Value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
|
||||||
|
const tagsToUpdate: { Key: string; Value: string }[] = [];
|
||||||
|
const tagsToDelete: { Key: string; Value: string }[] = [];
|
||||||
|
|
||||||
|
describedSecret.Tags?.forEach((tag) => {
|
||||||
|
if (tag.Key && tag.Value) {
|
||||||
|
if (!(tag.Key in integrationTagObj)) {
|
||||||
|
// delete tag from AWS secret manager
|
||||||
|
tagsToDelete.push({
|
||||||
|
Key: tag.Key,
|
||||||
|
Value: tag.Value
|
||||||
|
});
|
||||||
|
} else if (tag.Value !== integrationTagObj[tag.Key]) {
|
||||||
|
// update tag in AWS secret manager
|
||||||
|
tagsToUpdate.push({
|
||||||
|
Key: tag.Key,
|
||||||
|
Value: integrationTagObj[tag.Key]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
secretAWSTag?.forEach((tag) => {
|
||||||
|
if (!(tag.key in awsTagObj)) {
|
||||||
|
// create tag in AWS secret manager
|
||||||
|
tagsToUpdate.push({
|
||||||
|
Key: tag.key,
|
||||||
|
Value: tag.value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tagsToUpdate.length) {
|
||||||
|
await secretsManager.send(
|
||||||
|
new TagResourceCommand({
|
||||||
|
SecretId: integration.app as string,
|
||||||
|
Tags: tagsToUpdate
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagsToDelete.length) {
|
||||||
|
await secretsManager.send(
|
||||||
|
new UntagResourceCommand({
|
||||||
|
SecretId: integration.app as string,
|
||||||
|
TagKeys: tagsToDelete.map((tag) => tag.Key)
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
// case when AWS manager can't find the specified secret
|
||||||
if (err instanceof ResourceNotFoundException && secretsManager) {
|
if (err instanceof ResourceNotFoundException && secretsManager) {
|
||||||
await secretsManager.send(
|
await secretsManager.send(
|
||||||
new CreateSecretCommand({
|
new CreateSecretCommand({
|
||||||
|
|||||||
@@ -103,7 +103,8 @@ export const integrationServiceFactory = ({
|
|||||||
owner,
|
owner,
|
||||||
isActive,
|
isActive,
|
||||||
environment,
|
environment,
|
||||||
secretPath
|
secretPath,
|
||||||
|
metadata
|
||||||
}: TUpdateIntegrationDTO) => {
|
}: TUpdateIntegrationDTO) => {
|
||||||
const integration = await integrationDAL.findById(id);
|
const integration = await integrationDAL.findById(id);
|
||||||
if (!integration) throw new BadRequestError({ message: "Integration auth not found" });
|
if (!integration) throw new BadRequestError({ message: "Integration auth not found" });
|
||||||
@@ -127,7 +128,17 @@ export const integrationServiceFactory = ({
|
|||||||
appId,
|
appId,
|
||||||
targetEnvironment,
|
targetEnvironment,
|
||||||
owner,
|
owner,
|
||||||
secretPath
|
secretPath,
|
||||||
|
metadata: {
|
||||||
|
...(integration.metadata as object),
|
||||||
|
...metadata
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await secretQueueService.syncIntegrations({
|
||||||
|
environment: folder.environment.slug,
|
||||||
|
secretPath,
|
||||||
|
projectId: folder.projectId
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedIntegration;
|
return updatedIntegration;
|
||||||
|
|||||||
@@ -40,6 +40,20 @@ export type TUpdateIntegrationDTO = {
|
|||||||
targetEnvironment: string;
|
targetEnvironment: string;
|
||||||
owner: string;
|
owner: string;
|
||||||
environment: string;
|
environment: string;
|
||||||
|
metadata?: {
|
||||||
|
secretPrefix?: string;
|
||||||
|
secretSuffix?: string;
|
||||||
|
secretGCPLabel?: {
|
||||||
|
labelName: string;
|
||||||
|
labelValue: string;
|
||||||
|
};
|
||||||
|
secretAWSTag?: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
kmsKeyId?: string;
|
||||||
|
shouldDisableDelete?: boolean;
|
||||||
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeleteIntegrationDTO = {
|
export type TDeleteIntegrationDTO = {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ Prerequisites:
|
|||||||
"secretsmanager:GetSecretValue",
|
"secretsmanager:GetSecretValue",
|
||||||
"secretsmanager:CreateSecret",
|
"secretsmanager:CreateSecret",
|
||||||
"secretsmanager:UpdateSecret",
|
"secretsmanager:UpdateSecret",
|
||||||
|
"secretsmanager:DescribeSecret", // if you need to add tags to secrets
|
||||||
"secretsmanager:TagResource", // if you need to add tags to secrets
|
"secretsmanager:TagResource", // if you need to add tags to secrets
|
||||||
"kms:ListKeys", // if you need to specify the KMS key
|
"kms:ListKeys", // if you need to specify the KMS key
|
||||||
"kms:ListAliases", // if you need to specify the KMS key
|
"kms:ListAliases", // if you need to specify the KMS key
|
||||||
|
|||||||
Reference in New Issue
Block a user