diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index c83234c1f..d890fcc35 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -585,12 +585,13 @@ export const INTEGRATION = { region: "AWS region to sync secrets to.", scope: "Scope of the provider. Used by Github, Qovery", metadata: { - secretPrefix: "The prefix for the saved secret. Used by GCP", - secretSuffix: "The suffix for the saved secret. Used by GCP", - initialSyncBehavoir: "Type of syncing behavoir with the integration", - shouldAutoRedeploy: "Used by Render to trigger auto deploy", - secretGCPLabel: "The label for the GCP secrets", - secretAWSTag: "The tag for the AWS secrets" + secretPrefix: "The prefix for the saved secret. Used by GCP.", + secretSuffix: "The suffix for the saved secret. Used by GCP.", + initialSyncBehavoir: "Type of syncing behavoir with the integration.", + shouldAutoRedeploy: "Used by Render to trigger auto deploy.", + secretGCPLabel: "The label for GCP secrets.", + secretAWSTag: "The tags for AWS secrets.", + kmsKeyId: "The ID of the encryption key from AWS KMS." } }, UPDATE: { diff --git a/backend/src/server/routes/v1/integration-auth-router.ts b/backend/src/server/routes/v1/integration-auth-router.ts index 604ac0bc2..d9db7404e 100644 --- a/backend/src/server/routes/v1/integration-auth-router.ts +++ b/backend/src/server/routes/v1/integration-auth-router.ts @@ -511,6 +511,39 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider) } }); + server.route({ + method: "GET", + url: "/:integrationAuthId/aws-secrets-manager/kms-keys", + config: { + rateLimit: readLimit + }, + onRequest: verifyAuth([AuthMode.JWT]), + schema: { + params: z.object({ + integrationAuthId: z.string().trim() + }), + querystring: z.object({ + region: z.string().trim() + }), + response: { + 200: z.object({ + kmsKeys: z.object({ id: z.string(), alias: z.string() }).array() + }) + } + }, + handler: async (req) => { + const kmsKeys = await server.services.integrationAuth.getAwsKmsKeys({ + actorId: req.permission.id, + actor: req.permission.type, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + id: req.params.integrationAuthId, + region: req.query.region + }); + return { kmsKeys }; + } + }); + server.route({ method: "GET", url: "/:integrationAuthId/qovery/projects", diff --git a/backend/src/server/routes/v1/integration-router.ts b/backend/src/server/routes/v1/integration-router.ts index 58a8917c5..f908aa1fc 100644 --- a/backend/src/server/routes/v1/integration-router.ts +++ b/backend/src/server/routes/v1/integration-router.ts @@ -58,14 +58,17 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => { .optional() .describe(INTEGRATION.CREATE.metadata.secretGCPLabel), secretAWSTag: z - .object({ - key: z.string(), - value: z.string() - }) + .array( + z.object({ + key: z.string(), + value: z.string() + }) + ) .optional() - .describe(INTEGRATION.CREATE.metadata.secretAWSTag) + .describe(INTEGRATION.CREATE.metadata.secretAWSTag), + kmsKeyId: z.string().optional().describe(INTEGRATION.CREATE.metadata.kmsKeyId) }) - .optional() + .default({}) }), response: { 200: z.object({ diff --git a/backend/src/services/integration-auth/integration-auth-service.ts b/backend/src/services/integration-auth/integration-auth-service.ts index c8551df7d..3d42943a6 100644 --- a/backend/src/services/integration-auth/integration-auth-service.ts +++ b/backend/src/services/integration-auth/integration-auth-service.ts @@ -1,5 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import { Octokit } from "@octokit/rest"; +import AWS from "aws-sdk"; import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; @@ -23,6 +24,7 @@ import { TGetIntegrationAuthTeamCityBuildConfigDTO, THerokuPipelineCoupling, TIntegrationAuthAppsDTO, + TIntegrationAuthAwsKmsKeyDTO, TIntegrationAuthBitbucketWorkspaceDTO, TIntegrationAuthChecklyGroupsDTO, TIntegrationAuthGithubEnvsDTO, @@ -534,6 +536,52 @@ export const integrationAuthServiceFactory = ({ return data.results.map(({ name, id: orgId }) => ({ name, orgId })); }; + const getAwsKmsKeys = async ({ + actorId, + actor, + actorOrgId, + actorAuthMethod, + id, + region + }: TIntegrationAuthAwsKmsKeyDTO) => { + const integrationAuth = await integrationAuthDAL.findById(id); + if (!integrationAuth) throw new BadRequestError({ message: "Failed to find integration" }); + + const { permission } = await permissionService.getProjectPermission( + actor, + actorId, + integrationAuth.projectId, + actorAuthMethod, + actorOrgId + ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations); + const botKey = await projectBotService.getBotKey(integrationAuth.projectId); + const { accessId, accessToken } = await getIntegrationAccessToken(integrationAuth, botKey); + + AWS.config.update({ + region, + credentials: { + accessKeyId: String(accessId), + secretAccessKey: accessToken + } + }); + const kms = new AWS.KMS(); + + const aliases = await kms.listAliases({}).promise(); + const keys = await kms.listKeys({}).promise(); + const response = keys + .Keys!.map((key) => { + const keyAlias = aliases.Aliases!.find((alias) => key.KeyId === alias.TargetKeyId); + if (!keyAlias?.AliasName?.includes("alias/aws/") || keyAlias?.AliasName?.includes("alias/aws/secretsmanager")) { + return { id: String(key.KeyId), alias: String(keyAlias?.AliasName || key.KeyId) }; + } + return { id: "null", alias: "null" }; + }) + .filter((elem) => elem.id !== "null"); + + return response; + }; + const getQoveryProjects = async ({ actorId, actor, @@ -1133,6 +1181,7 @@ export const integrationAuthServiceFactory = ({ getIntegrationApps, getVercelBranches, getApps, + getAwsKmsKeys, getGithubOrgs, getGithubEnvs, getChecklyGroups, diff --git a/backend/src/services/integration-auth/integration-auth-types.ts b/backend/src/services/integration-auth/integration-auth-types.ts index 0c8671fbf..0a816035c 100644 --- a/backend/src/services/integration-auth/integration-auth-types.ts +++ b/backend/src/services/integration-auth/integration-auth-types.ts @@ -63,6 +63,11 @@ export type TIntegrationAuthQoveryProjectDTO = { orgId: string; } & Omit; +export type TIntegrationAuthAwsKmsKeyDTO = { + id: string; + region: string; +} & Omit; + export type TIntegrationAuthQoveryEnvironmentsDTO = { id: string; } & TProjectPermission; diff --git a/backend/src/services/integration-auth/integration-sync-secret.ts b/backend/src/services/integration-auth/integration-sync-secret.ts index 48226bb59..bc880c8c4 100644 --- a/backend/src/services/integration-auth/integration-sync-secret.ts +++ b/backend/src/services/integration-auth/integration-sync-secret.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ @@ -489,7 +490,9 @@ const syncSecretsAWSParameterStore = async ({ Type: "SecureString", Value: secrets[key].value, // Overwrite: true, - Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : [] + Tags: metadata.secretAWSTag + ? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value })) + : [] }) .promise(); // case: secret exists in AWS parameter store @@ -579,7 +582,10 @@ const syncSecretsAWSSecretManager = async ({ new CreateSecretCommand({ Name: integration.app as string, SecretString: JSON.stringify(secKeyVal), - Tags: metadata.secretAWSTag ? [{ Key: metadata.secretAWSTag.key, Value: metadata.secretAWSTag.value }] : [] + KmsKeyId: metadata.kmsKeyId ? metadata.kmsKeyId : null, + Tags: metadata.secretAWSTag + ? metadata.secretAWSTag.map((tag: { key: string; value: string }) => ({ Key: tag.key, Value: tag.value })) + : [] }) ); } @@ -2151,16 +2157,29 @@ const syncSecretsQovery = async ({ * @param {String} obj.accessToken - access token for Terraform Cloud API */ const syncSecretsTerraformCloud = async ({ + createManySecretsRawFn, + updateManySecretsRawFn, integration, secrets, - accessToken + accessToken, + integrationDAL }: { - integration: TIntegrations; - secrets: Record; + createManySecretsRawFn: (params: TCreateManySecretsRawFn) => Promise>; + updateManySecretsRawFn: (params: TUpdateManySecretsRawFn) => Promise>; + integration: TIntegrations & { + projectId: string; + environment: { + id: string; + name: string; + slug: string; + }; + }; + secrets: Record; accessToken: string; + integrationDAL: Pick; }) => { // get secrets from Terraform Cloud - const getSecretsRes = ( + const terraformSecrets = ( await request.get<{ data: { attributes: { key: string; value: string }; id: string }[] }>( `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars`, { @@ -2178,9 +2197,74 @@ const syncSecretsTerraformCloud = async ({ {} as Record ); + const secretsToAdd: { [key: string]: string } = {}; + const secretsToUpdate: { [key: string]: string } = {}; + + const metadata = z.record(z.any()).parse(integration.metadata); + + Object.keys(terraformSecrets).forEach((key) => { + if (!integration.lastUsed) { + // first time using integration + // -> apply initial sync behavior + switch (metadata.initialSyncBehavior) { + case IntegrationInitialSyncBehavior.PREFER_TARGET: { + if (!(key in secrets)) { + secretsToAdd[key] = terraformSecrets[key].attributes.value; + } else if (secrets[key]?.value !== terraformSecrets[key].attributes.value) { + secretsToUpdate[key] = terraformSecrets[key].attributes.value; + } + secrets[key] = { + value: terraformSecrets[key].attributes.value + }; + break; + } + case IntegrationInitialSyncBehavior.PREFER_SOURCE: { + if (!(key in secrets)) { + secrets[key] = { + value: terraformSecrets[key].attributes.value + }; + secretsToAdd[key] = terraformSecrets[key].attributes.value; + } + break; + } + default: { + break; + } + } + } else if (!(key in secrets)) secrets[key] = null; + }); + + if (Object.keys(secretsToAdd).length) { + await createManySecretsRawFn({ + projectId: integration.projectId, + environment: integration.environment.slug, + path: integration.secretPath, + secrets: Object.keys(secretsToAdd).map((key) => ({ + secretName: key, + secretValue: secretsToAdd[key], + type: SecretType.Shared, + secretComment: "" + })) + }); + } + + if (Object.keys(secretsToUpdate).length) { + await updateManySecretsRawFn({ + projectId: integration.projectId, + environment: integration.environment.slug, + path: integration.secretPath, + secrets: Object.keys(secretsToUpdate).map((key) => ({ + secretName: key, + secretValue: secretsToUpdate[key], + type: SecretType.Shared, + secretComment: "" + })) + }); + } + // create or update secrets on Terraform Cloud for await (const key of Object.keys(secrets)) { - if (!(key in getSecretsRes)) { + if (!(key in terraformSecrets)) { // case: secret does not exist in Terraform Cloud // -> add secret await request.post( @@ -2190,7 +2274,7 @@ const syncSecretsTerraformCloud = async ({ type: "vars", attributes: { key, - value: secrets[key].value, + value: secrets[key]?.value, category: integration.targetService } } @@ -2204,17 +2288,17 @@ const syncSecretsTerraformCloud = async ({ } ); // case: secret exists in Terraform Cloud - } else if (secrets[key].value !== getSecretsRes[key].attributes.value) { + } else if (secrets[key]?.value !== terraformSecrets[key].attributes.value) { // -> update secret await request.patch( - `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${getSecretsRes[key].id}`, + `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${terraformSecrets[key].id}`, { data: { type: "vars", - id: getSecretsRes[key].id, + id: terraformSecrets[key].id, attributes: { - ...getSecretsRes[key], - value: secrets[key].value + ...terraformSecrets[key], + value: secrets[key]?.value } } }, @@ -2229,11 +2313,11 @@ const syncSecretsTerraformCloud = async ({ } } - for await (const key of Object.keys(getSecretsRes)) { + for await (const key of Object.keys(terraformSecrets)) { if (!(key in secrets)) { // case: delete secret await request.delete( - `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${getSecretsRes[key].id}`, + `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${integration.appId}/vars/${terraformSecrets[key].id}`, { headers: { Authorization: `Bearer ${accessToken}`, @@ -2244,6 +2328,10 @@ const syncSecretsTerraformCloud = async ({ ); } } + + await integrationDAL.updateById(integration.id, { + lastUsed: new Date() + }); }; /** @@ -3285,9 +3373,12 @@ export const syncIntegrationSecrets = async ({ break; case Integrations.TERRAFORM_CLOUD: await syncSecretsTerraformCloud({ + createManySecretsRawFn, + updateManySecretsRawFn, integration, secrets, - accessToken + accessToken, + integrationDAL }); break; case Integrations.HASHICORP_VAULT: diff --git a/backend/src/services/integration/integration-types.ts b/backend/src/services/integration/integration-types.ts index 93c36cd8e..56ea46350 100644 --- a/backend/src/services/integration/integration-types.ts +++ b/backend/src/services/integration/integration-types.ts @@ -25,7 +25,8 @@ export type TCreateIntegrationDTO = { secretAWSTag?: { key: string; value: string; - }; + }[]; + kmsKeyId?: string; }; } & Omit; diff --git a/docs/documentation/platform/identities/user-identities.mdx b/docs/documentation/platform/identities/user-identities.mdx index e88752d84..2d4791127 100644 --- a/docs/documentation/platform/identities/user-identities.mdx +++ b/docs/documentation/platform/identities/user-identities.mdx @@ -9,9 +9,9 @@ A **user identity** (also known as **user**) represents a developer, admin, or a Users can be added manually (through Web UI) or programmatically (e.g., API) to [organizations](../organization) and [projects](../projects). -Upon being added to an organizaztion and projects, users assume a certain set of roles and permissions that represents their identity. +Upon being added to an organization and projects, users assume a certain set of roles and permissions that represents their identity. -![organization members](../../images/platform/organization/organization-members.png) +![organization members](../../../images/platform/organization/organization-members.png) ## Authentication methods diff --git a/docs/images/integrations/aws/integrations-aws-secret-manager-create.png b/docs/images/integrations/aws/integrations-aws-secret-manager-create.png index 619ec0a8e..21f2213ef 100644 Binary files a/docs/images/integrations/aws/integrations-aws-secret-manager-create.png and b/docs/images/integrations/aws/integrations-aws-secret-manager-create.png differ diff --git a/docs/images/integrations/aws/integrations-aws-secret-manager-options.png b/docs/images/integrations/aws/integrations-aws-secret-manager-options.png new file mode 100644 index 000000000..f8492cdfa Binary files /dev/null and b/docs/images/integrations/aws/integrations-aws-secret-manager-options.png differ diff --git a/docs/integrations/cloud/aws-parameter-store.mdx b/docs/integrations/cloud/aws-parameter-store.mdx index 547387996..c872e39f0 100644 --- a/docs/integrations/cloud/aws-parameter-store.mdx +++ b/docs/integrations/cloud/aws-parameter-store.mdx @@ -30,7 +30,7 @@ Prerequisites: "ssm:DeleteParameter", "ssm:GetParametersByPath", "ssm:DeleteParameters", - "ssm:AddTagsToResource" + "ssm:AddTagsToResource" // if you need to add tags to secrets ], "Resource": "*" } diff --git a/docs/integrations/cloud/aws-secret-manager.mdx b/docs/integrations/cloud/aws-secret-manager.mdx index a0187644d..2ab45c620 100644 --- a/docs/integrations/cloud/aws-secret-manager.mdx +++ b/docs/integrations/cloud/aws-secret-manager.mdx @@ -29,13 +29,16 @@ Prerequisites: "secretsmanager:GetSecretValue", "secretsmanager:CreateSecret", "secretsmanager:UpdateSecret", - "secretsmanager:TagResource" + "secretsmanager:TagResource", // if you need to add tags to secrets + "kms:ListKeys", // if you need to specify the KMS key + "kms:ListAliases" // if you need to specify the KMS key ], "Resource": "*" } ] } ``` + Obtain a AWS access key ID and secret access key for your IAM user in IAM > Users > User > Security credentials > Access keys @@ -43,7 +46,7 @@ Prerequisites: ![access key 1](../../images/integrations/aws/integrations-aws-access-key-1.png) ![access key 2](../../images/integrations/aws/integrations-aws-access-key-2.png) ![access key 3](../../images/integrations/aws/integrations-aws-access-key-3.png) - + Navigate to your project's integrations tab in Infisical. ![integrations](../../images/integrations.png) @@ -52,12 +55,6 @@ Prerequisites: ![integration auth](../../images/integrations/aws/integrations-aws-secret-manager-auth.png) - - If this is your project's first cloud integration, then you'll have to grant - Infisical access to your project's environment variables. Although this step - breaks E2EE, it's necessary for Infisical to sync the environment variables to - the cloud platform. - Select how you want to integration to work by specifying a number of parameters: @@ -72,13 +69,23 @@ Prerequisites: The region that you want to integrate with in AWS Secrets Manager. - The secret name/path in AWS into which you want to sync the secrets from Infisical. + The secret name/path in AWS into which you want to sync the secrets from Infisical. - Then, press `Create Integration` to start syncing secrets to AWS Secrets Manager. - ![integration create](../../images/integrations/aws/integrations-aws-secret-manager-create.png) + Optionally, you can add tags or specify the encryption key of all the secrets created via this integration: + + + The Key/Value of a tag that will be added to secrets in AWS. Please note that it is possible to add multiple tags via API. + + + The alias/ID of the AWS KMS key used for encryption. Please note that key should be enabled in order to work and the IAM user should have access to it. + + ![integration options](../../images/integrations/aws/integrations-aws-secret-manager-options.png) + + Then, press `Create Integration` to start syncing secrets to AWS Secrets Manager. + Infisical currently syncs environment variables to AWS Secrets Manager as key-value pairs under one secret. We're actively exploring ways to help users @@ -88,5 +95,6 @@ Prerequisites: Please note that upon deleting secrets in Infisical, AWS Secrets Manager immediately makes the secrets inaccessible but only schedules them for deletion after at least 7 days. + diff --git a/frontend/src/hooks/api/integrationAuth/queries.tsx b/frontend/src/hooks/api/integrationAuth/queries.tsx index 073fde34b..e66dd5700 100644 --- a/frontend/src/hooks/api/integrationAuth/queries.tsx +++ b/frontend/src/hooks/api/integrationAuth/queries.tsx @@ -10,6 +10,7 @@ import { Environment, HerokuPipelineCoupling, IntegrationAuth, + KmsKey, NorthflankSecretGroup, Org, Project, @@ -43,6 +44,14 @@ const integrationAuthKeys = { [{ integrationAuthId }, "integrationAuthGithubOrgs"] as const, getIntegrationAuthGithubEnvs: (integrationAuthId: string, repoName: string, repoOwner: string) => [{ integrationAuthId, repoName, repoOwner }, "integrationAuthGithubOrgs"] as const, + getIntegrationAuthAwsKmsKeys: ({ + integrationAuthId, + region + }: { + integrationAuthId: string, + region: string + }) => + [{ integrationAuthId, region }, "integrationAuthAwsKmsKeyIds"] as const, getIntegrationAuthQoveryOrgs: (integrationAuthId: string) => [{ integrationAuthId }, "integrationAuthQoveryOrgs"] as const, getIntegrationAuthQoveryProjects: ({ @@ -217,6 +226,27 @@ const fetchIntegrationAuthQoveryOrgs = async (integrationAuthId: string) => { return orgs; }; +const fetchIntegrationAuthAwsKmsKeys = async ({ + integrationAuthId, + region +}: { + integrationAuthId: string; + region: string; +}) => { + const { + data: { kmsKeys } + } = await apiRequest.get<{ kmsKeys: KmsKey[] }>( + `/api/v1/integration-auth/${integrationAuthId}/aws-secrets-manager/kms-keys`, + { + params: { + region + } + } + ); + + return kmsKeys; +}; + const fetchIntegrationAuthQoveryProjects = async ({ integrationAuthId, orgId @@ -544,6 +574,27 @@ export const useGetIntegrationAuthQoveryOrgs = (integrationAuthId: string) => { }); }; +export const useGetIntegrationAuthAwsKmsKeys = ({ + integrationAuthId, + region +}: { + integrationAuthId: string; + region: string; +}) => { + return useQuery({ + queryKey: integrationAuthKeys.getIntegrationAuthAwsKmsKeys({ + integrationAuthId, + region + }), + queryFn: () => + fetchIntegrationAuthAwsKmsKeys({ + integrationAuthId, + region + }), + enabled: true + }); +}; + export const useGetIntegrationAuthQoveryProjects = ({ integrationAuthId, orgId diff --git a/frontend/src/hooks/api/integrationAuth/types.ts b/frontend/src/hooks/api/integrationAuth/types.ts index cfd25df00..b0e1dd9f5 100644 --- a/frontend/src/hooks/api/integrationAuth/types.ts +++ b/frontend/src/hooks/api/integrationAuth/types.ts @@ -58,6 +58,11 @@ export type Project = { projectId: string; }; +export type KmsKey = { + id: string; + alias: string; +}; + export type Service = { name: string; serviceId: string; diff --git a/frontend/src/hooks/api/integrations/queries.tsx b/frontend/src/hooks/api/integrations/queries.tsx index 7804f0951..b855dbf73 100644 --- a/frontend/src/hooks/api/integrations/queries.tsx +++ b/frontend/src/hooks/api/integrations/queries.tsx @@ -66,7 +66,8 @@ export const useCreateIntegration = () => { secretAWSTag?: { key: string; value: string; - }; + }[]; + kmsKeyId?: string; }; }) => { const { diff --git a/frontend/src/pages/integrations/aws-parameter-store/create.tsx b/frontend/src/pages/integrations/aws-parameter-store/create.tsx index dd643023c..92e4662a9 100644 --- a/frontend/src/pages/integrations/aws-parameter-store/create.tsx +++ b/frontend/src/pages/integrations/aws-parameter-store/create.tsx @@ -128,10 +128,10 @@ export default function AWSParameterStoreCreateIntegrationPage() { metadata: { ...(shouldTag ? { - secretAWSTag: { + secretAWSTag: [{ key: tagKey, value: tagValue - } + }] } : {}) } @@ -279,7 +279,7 @@ export default function AWSParameterStoreCreateIntegrationPage() { label="Tag Value" > setTagValue(e.target.value)} /> diff --git a/frontend/src/pages/integrations/aws-secret-manager/create.tsx b/frontend/src/pages/integrations/aws-secret-manager/create.tsx index ee06fe6c1..b48296259 100644 --- a/frontend/src/pages/integrations/aws-secret-manager/create.tsx +++ b/frontend/src/pages/integrations/aws-secret-manager/create.tsx @@ -14,6 +14,7 @@ import { motion } from "framer-motion"; import queryString from "query-string"; import { useCreateIntegration } from "@app/hooks/api"; +import { useGetIntegrationAuthAwsKmsKeys } from "@app/hooks/api/integrationAuth/queries"; import { Button, @@ -87,6 +88,7 @@ export default function AWSSecretManagerCreateIntegrationPage() { const [targetSecretNameErrorText, setTargetSecretNameErrorText] = useState(""); const [tagKey, setTagKey] = useState(""); const [tagValue, setTagValue] = useState(""); + const [kmsKeyId, setKmsKeyId] = useState(""); // const [path, setPath] = useState(''); // const [pathErrorText, setPathErrorText] = useState(''); @@ -94,6 +96,19 @@ export default function AWSSecretManagerCreateIntegrationPage() { const [isLoading, setIsLoading] = useState(false); const [shouldTag, setShouldTag] = useState(false); + + const { data: integrationAuthAwsKmsKeys, isLoading: isIntegrationAuthAwsKmsKeysLoading } = + useGetIntegrationAuthAwsKmsKeys({ + integrationAuthId: String(integrationAuthId), + region: selectedAWSRegion + }); + + useEffect(() => { + if (integrationAuthAwsKmsKeys) { + setKmsKeyId(String(integrationAuthAwsKmsKeys?.filter(key => key.alias === "alias/aws/secretsmanager")[0]?.id)) + } + }, [integrationAuthAwsKmsKeys]) + useEffect(() => { if (workspace) { setSelectedSourceEnvironment(workspace.environments[0].slug); @@ -127,12 +142,16 @@ export default function AWSSecretManagerCreateIntegrationPage() { metadata: { ...(shouldTag ? { - secretAWSTag: { + secretAWSTag: [{ key: tagKey, value: tagValue - } + }] } - : {}) + : {}), + ...((kmsKeyId && integrationAuthAwsKmsKeys?.filter(key => key.id === kmsKeyId)[0]?.alias !== "alias/aws/secretsmanager") ? + { + kmsKeyId + }: {}) } }); @@ -145,7 +164,7 @@ export default function AWSSecretManagerCreateIntegrationPage() { } }; - return integrationAuth && workspace && selectedSourceEnvironment ? ( + return (integrationAuth && workspace && selectedSourceEnvironment && !isIntegrationAuthAwsKmsKeysLoading) ? (
Set Up AWS Secrets Manager Integration @@ -278,13 +297,38 @@ export default function AWSSecretManagerCreateIntegrationPage() { label="Tag Value" > setTagValue(e.target.value)} />
)} + + + @@ -317,7 +361,7 @@ export default function AWSSecretManagerCreateIntegrationPage() { Set Up AWS Secrets Manager Integration - {isintegrationAuthLoading ? ( + {(isintegrationAuthLoading || isIntegrationAuthAwsKmsKeysLoading) ? ( - - Terraform Cloud Integration + + Authorize Terraform Cloud Integration + + + + +
+
+ Terraform logo +
+ Terraform Cloud Integration + + +
+ + Docs + +
+
+ +
+
+ + setWorkSpacesId(e.target.value)} + /> + setApiKey(e.target.value)} /> - - setWorkSpacesId(e.target.value)} - /> -