diff --git a/backend/src/services/secret-sync/aws-parameter-store/aws-parameter-store-sync-fns.ts b/backend/src/services/secret-sync/aws-parameter-store/aws-parameter-store-sync-fns.ts index b687d81dd..d0e97afa1 100644 --- a/backend/src/services/secret-sync/aws-parameter-store/aws-parameter-store-sync-fns.ts +++ b/backend/src/services/secret-sync/aws-parameter-store/aws-parameter-store-sync-fns.ts @@ -307,7 +307,6 @@ export const AwsParameterStoreSyncFns = { awsParameterStoreSecretsRecord, Boolean(syncOptions.tags?.length || syncOptions.syncSecretMetadataAsTags) ); - const syncTagsRecord = Object.fromEntries(syncOptions.tags?.map((tag) => [tag.key, tag.value]) ?? []); for await (const entry of Object.entries(secretMap)) { const [key, { value, secretMetadata }] = entry; @@ -342,13 +341,13 @@ export const AwsParameterStoreSyncFns = { } } - if (shouldManageTags) { + if ((syncOptions.tags !== undefined || syncOptions.syncSecretMetadataAsTags) && shouldManageTags) { const { tagsToAdd, tagKeysToRemove } = processParameterTags({ syncTagsRecord: { // configured sync tags take preference over secret metadata ...(syncOptions.syncSecretMetadataAsTags && Object.fromEntries(secretMetadata?.map((tag) => [tag.key, tag.value]) ?? [])), - ...syncTagsRecord + ...(syncOptions.tags && Object.fromEntries(syncOptions.tags?.map((tag) => [tag.key, tag.value]) ?? [])) }, awsTagsRecord: awsParameterStoreTagsRecord[key] ?? {} }); diff --git a/backend/src/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-fns.ts b/backend/src/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-fns.ts index df73512e5..a6415ac00 100644 --- a/backend/src/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-fns.ts +++ b/backend/src/services/secret-sync/aws-secrets-manager/aws-secrets-manager-sync-fns.ts @@ -366,37 +366,39 @@ export const AwsSecretsManagerSyncFns = { } } - const { tagsToAdd, tagKeysToRemove } = processTags({ - syncTagsRecord: { - // configured sync tags take preference over secret metadata - ...(syncOptions.syncSecretMetadataAsTags && - Object.fromEntries(secretMetadata?.map((tag) => [tag.key, tag.value]) ?? [])), - ...syncTagsRecord - }, - awsTagsRecord: Object.fromEntries( - awsDescriptionsRecord[key]?.Tags?.map((tag) => [tag.Key!, tag.Value!]) ?? [] - ) - }); + if (syncOptions.tags !== undefined || syncOptions.syncSecretMetadataAsTags) { + const { tagsToAdd, tagKeysToRemove } = processTags({ + syncTagsRecord: { + // configured sync tags take preference over secret metadata + ...(syncOptions.syncSecretMetadataAsTags && + Object.fromEntries(secretMetadata?.map((tag) => [tag.key, tag.value]) ?? [])), + ...(syncOptions.tags !== undefined && syncTagsRecord) + }, + awsTagsRecord: Object.fromEntries( + awsDescriptionsRecord[key]?.Tags?.map((tag) => [tag.Key!, tag.Value!]) ?? [] + ) + }); - if (tagsToAdd.length) { - try { - await addTags(client, key, tagsToAdd); - } catch (error) { - throw new SecretSyncError({ - error, - secretKey: key - }); + if (tagsToAdd.length) { + try { + await addTags(client, key, tagsToAdd); + } catch (error) { + throw new SecretSyncError({ + error, + secretKey: key + }); + } } - } - if (tagKeysToRemove.length) { - try { - await removeTags(client, key, tagKeysToRemove); - } catch (error) { - throw new SecretSyncError({ - error, - secretKey: key - }); + if (tagKeysToRemove.length) { + try { + await removeTags(client, key, tagKeysToRemove); + } catch (error) { + throw new SecretSyncError({ + error, + secretKey: key + }); + } } } } @@ -439,32 +441,34 @@ export const AwsSecretsManagerSyncFns = { }); } - const { tagsToAdd, tagKeysToRemove } = processTags({ - syncTagsRecord, - awsTagsRecord: Object.fromEntries( - awsDescriptionsRecord[destinationConfig.secretName]?.Tags?.map((tag) => [tag.Key!, tag.Value!]) ?? [] - ) - }); + if (syncOptions.tags !== undefined) { + const { tagsToAdd, tagKeysToRemove } = processTags({ + syncTagsRecord, + awsTagsRecord: Object.fromEntries( + awsDescriptionsRecord[destinationConfig.secretName]?.Tags?.map((tag) => [tag.Key!, tag.Value!]) ?? [] + ) + }); - if (tagsToAdd.length) { - try { - await addTags(client, destinationConfig.secretName, tagsToAdd); - } catch (error) { - throw new SecretSyncError({ - error, - secretKey: destinationConfig.secretName - }); + if (tagsToAdd.length) { + try { + await addTags(client, destinationConfig.secretName, tagsToAdd); + } catch (error) { + throw new SecretSyncError({ + error, + secretKey: destinationConfig.secretName + }); + } } - } - if (tagKeysToRemove.length) { - try { - await removeTags(client, destinationConfig.secretName, tagKeysToRemove); - } catch (error) { - throw new SecretSyncError({ - error, - secretKey: destinationConfig.secretName - }); + if (tagKeysToRemove.length) { + try { + await removeTags(client, destinationConfig.secretName, tagKeysToRemove); + } catch (error) { + throw new SecretSyncError({ + error, + secretKey: destinationConfig.secretName + }); + } } } } diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsParameterStoreSyncOptionsFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsParameterStoreSyncOptionsFields.tsx index 4b1467182..b4f7dcd81 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsParameterStoreSyncOptionsFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsParameterStoreSyncOptionsFields.tsx @@ -22,87 +22,19 @@ import { SecretSync } from "@app/hooks/api/secretSyncs"; import { TSecretSyncForm } from "../schemas"; -export const AwsParameterStoreSyncOptionsFields = () => { - const { control, watch } = useFormContext< +const AwsTagsSection = () => { + const { control } = useFormContext< TSecretSyncForm & { destination: SecretSync.AWSParameterStore } >(); - const region = watch("destinationConfig.region"); - const connectionId = useWatch({ name: "connection.id", control }); - - const { data: kmsKeys = [], isPending: isKmsKeysPending } = useListAwsConnectionKmsKeys( - { - connectionId, - region, - destination: SecretSync.AWSParameterStore - }, - { enabled: Boolean(connectionId && region) } - ); - const tagFields = useFieldArray({ control, name: "syncOptions.tags" }); return ( - <> - ( - - org.alias === value) ?? null} - onChange={(option) => - onChange((option as SingleValue)?.alias ?? null) - } - // eslint-disable-next-line react/no-unstable-nested-components - noOptionsMessage={({ inputValue }) => - inputValue ? undefined : ( -

- To configure a KMS key, ensure the following permissions are present on the - selected IAM role:{" "} - - "kms:ListAliases" - - ,{" "} - - "kms:DescribeKey" - - ,{" "} - - "kms:Encrypt" - - ,{" "} - - "kms:Decrypt" - - . -

- ) - } - options={kmsKeys} - placeholder="Leave blank to use default KMS key" - getOptionLabel={(option) => - option.alias === "alias/aws/ssm" ? `${option.alias} (Default)` : option.alias - } - getOptionValue={(option) => option.alias} - /> -
- )} - /> - -
+
+
{tagFields.fields.map(({ id: tagFieldId }, i) => (
@@ -164,12 +96,118 @@ export const AwsParameterStoreSyncOptionsFields = () => { Add Tag
+
+ ); +}; + +export const AwsParameterStoreSyncOptionsFields = () => { + const { control, watch, setValue } = useFormContext< + TSecretSyncForm & { destination: SecretSync.AWSParameterStore } + >(); + + const region = watch("destinationConfig.region"); + const connectionId = useWatch({ name: "connection.id", control }); + const watchedTags = watch("syncOptions.tags"); + + const { data: kmsKeys = [], isPending: isKmsKeysPending } = useListAwsConnectionKmsKeys( + { + connectionId, + region, + destination: SecretSync.AWSParameterStore + }, + { enabled: Boolean(connectionId && region) } + ); + + return ( + <> + ( + + org.alias === value) ?? null} + onChange={(option) => + onChange((option as SingleValue)?.alias ?? null) + } + // eslint-disable-next-line react/no-unstable-nested-components + noOptionsMessage={({ inputValue }) => + inputValue ? undefined : ( +

+ To configure a KMS key, ensure the following permissions are present on the + selected IAM role:{" "} + + "kms:ListAliases" + + ,{" "} + + "kms:DescribeKey" + + ,{" "} + + "kms:Encrypt" + + ,{" "} + + "kms:Decrypt" + + . +

+ ) + } + options={kmsKeys} + placeholder="Leave blank to use default KMS key" + getOptionLabel={(option) => + option.alias === "alias/aws/ssm" ? `${option.alias} (Default)` : option.alias + } + getOptionValue={(option) => option.alias} + /> +
+ )} + /> + { + if (isChecked) { + setValue("syncOptions.tags", []); + } else { + setValue("syncOptions.tags", undefined); + } + }} + > +

+ Configure Resource Tags{" "} + + If enabled, AWS resource tags will be overwritten using static values defined below. +

+ } + > + + +

+
+ + {Array.isArray(watchedTags) && } + ( diff --git a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsSecretsManagerSyncOptionsFields.tsx b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsSecretsManagerSyncOptionsFields.tsx index 5e4768dda..6a13c5205 100644 --- a/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsSecretsManagerSyncOptionsFields.tsx +++ b/frontend/src/components/secret-syncs/forms/SecretSyncOptionsFields/AwsSecretsManagerSyncOptionsFields.tsx @@ -23,14 +23,93 @@ import { AwsSecretsManagerSyncMappingBehavior } from "@app/hooks/api/secretSyncs import { TSecretSyncForm } from "../schemas"; +const AwsTagsSection = () => { + const { control } = useFormContext< + TSecretSyncForm & { destination: SecretSync.AWSSecretsManager } + >(); + + const tagFields = useFieldArray({ + control, + name: "syncOptions.tags" + }); + + return ( +
+
+ {tagFields.fields.map(({ id: tagFieldId }, i) => ( + +
+ {i === 0 && Key} + ( + + + + )} + /> +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+ + tagFields.remove(i)} + > + + + +
+ ))} +
+
+ +
+
+ ); +}; + export const AwsSecretsManagerSyncOptionsFields = () => { - const { control, watch } = useFormContext< + const { control, watch, setValue } = useFormContext< TSecretSyncForm & { destination: SecretSync.AWSSecretsManager } >(); const region = watch("destinationConfig.region"); const connectionId = useWatch({ name: "connection.id", control }); const mappingBehavior = watch("destinationConfig.mappingBehavior"); + const watchedTags = watch("syncOptions.tags"); const { data: kmsKeys = [], isPending: isKmsKeysPending } = useListAwsConnectionKmsKeys( { @@ -41,11 +120,6 @@ export const AwsSecretsManagerSyncOptionsFields = () => { { enabled: Boolean(connectionId && region) } ); - const tagFields = useFieldArray({ - control, - name: "syncOptions.tags" - }); - return ( <> {
)} /> - -
- {tagFields.fields.map(({ id: tagFieldId }, i) => ( - -
- {i === 0 && Key} - ( - - - - )} - /> -
-
- {i === 0 && ( - - )} - ( - - - - )} - /> -
- - tagFields.remove(i)} - > - - - -
- ))} -
-
- -
+ + { + if (isChecked) { + setValue("syncOptions.tags", []); + } else { + setValue("syncOptions.tags", undefined); + } + }} + > +

+ Configure Secret Tags{" "} + + If enabled, AWS secret tags will be overwritten using static values defined below. +

+ } + > + + +

+
+ + {Array.isArray(watchedTags) && } + {mappingBehavior === AwsSecretsManagerSyncMappingBehavior.OneToOne && ( ( - +