mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 04:29:10 +00:00
General improvements to Terraform Integration
This commit is contained in:
@@ -1694,6 +1694,9 @@ export const AppConnections = {
|
|||||||
sslEnabled: "Whether or not to use SSL when connecting to the database.",
|
sslEnabled: "Whether or not to use SSL when connecting to the database.",
|
||||||
sslRejectUnauthorized: "Whether or not to reject unauthorized SSL certificates.",
|
sslRejectUnauthorized: "Whether or not to reject unauthorized SSL certificates.",
|
||||||
sslCertificate: "The SSL certificate to use for connection."
|
sslCertificate: "The SSL certificate to use for connection."
|
||||||
|
},
|
||||||
|
TERRAFORM_CLOUD: {
|
||||||
|
apiToken: "The API token to use to connect with Terraform Cloud."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+29
-39
@@ -64,62 +64,52 @@ export const listOrganizations = async (
|
|||||||
credentials: { apiToken }
|
credentials: { apiToken }
|
||||||
} = appConnection;
|
} = appConnection;
|
||||||
|
|
||||||
const orgsResponse = await request.get<{ data: { id: string; attributes: { name: string } }[] }>(
|
const headers = {
|
||||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations`,
|
Authorization: `Bearer ${apiToken}`,
|
||||||
{
|
"Content-Type": "application/vnd.api+json"
|
||||||
headers: {
|
};
|
||||||
Authorization: `Bearer ${apiToken}`,
|
|
||||||
"Content-Type": "application/vnd.api+json"
|
const fetchAllPages = async <T>(url: string): Promise<T[]> => {
|
||||||
}
|
let results: T[] = [];
|
||||||
|
let nextUrl: string | null = url;
|
||||||
|
|
||||||
|
while (nextUrl) {
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const res: AxiosResponse<{ data: T[]; links?: { next?: string } }> = await request.get(nextUrl, { headers });
|
||||||
|
results = results.concat(res.data.data);
|
||||||
|
nextUrl = res.data.links?.next || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
const orgEntities = await fetchAllPages<{ id: string; attributes: { name: string } }>(
|
||||||
|
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!orgsResponse.data?.data) {
|
|
||||||
throw new InternalServerError({
|
|
||||||
message: "Failed to get organizations: Response was empty"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const orgEntities = orgsResponse.data.data;
|
|
||||||
const orgsWithVariableSetsAndWorkspaces: TTerraformCloudOrganization[] = [];
|
const orgsWithVariableSetsAndWorkspaces: TTerraformCloudOrganization[] = [];
|
||||||
|
|
||||||
const variableSetPromises = orgEntities.map((org) =>
|
const variableSetPromises = orgEntities.map((org) =>
|
||||||
request
|
fetchAllPages<{ id: string; attributes: { name: string; description?: string; global?: boolean } }>(
|
||||||
.get<{ data: { id: string; attributes: { name: string; description?: string; global?: boolean } }[] }>(
|
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${org.id}/varsets`
|
||||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${org.id}/varsets`,
|
).catch(() => [])
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${apiToken}`,
|
|
||||||
"Content-Type": "application/vnd.api+json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch(() => ({ data: { data: [] } }))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const workspacePromises = orgEntities.map((org) =>
|
const workspacePromises = orgEntities.map((org) =>
|
||||||
request
|
fetchAllPages<{ id: string; attributes: { name: string } }>(
|
||||||
.get<{ data: { id: string; attributes: { name: string } }[] }>(
|
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${org.id}/workspaces`
|
||||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${org.id}/workspaces`,
|
).catch(() => [])
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${apiToken}`,
|
|
||||||
"Content-Type": "application/vnd.api+json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch(() => ({ data: { data: [] } }))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const [variableSetResponses, workspaceResponses] = await Promise.all([
|
const [variableSetResults, workspaceResults] = await Promise.all([
|
||||||
Promise.all(variableSetPromises),
|
Promise.all(variableSetPromises),
|
||||||
Promise.all(workspacePromises)
|
Promise.all(workspacePromises)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for (let i = 0; i < orgEntities.length; i += 1) {
|
for (let i = 0; i < orgEntities.length; i += 1) {
|
||||||
const org = orgEntities[i];
|
const org = orgEntities[i];
|
||||||
const variableSetsData = variableSetResponses[i].data?.data || [];
|
const variableSetsData = variableSetResults[i];
|
||||||
const workspacesData = workspaceResponses[i].data?.data || [];
|
const workspacesData = workspaceResults[i];
|
||||||
|
|
||||||
const variableSets: TTerraformCloudVariableSet[] = variableSetsData.map((varSet) => ({
|
const variableSets: TTerraformCloudVariableSet[] = variableSetsData.map((varSet) => ({
|
||||||
id: varSet.id,
|
id: varSet.id,
|
||||||
|
|||||||
-24
@@ -25,21 +25,6 @@ export type TTerraformCloudConnectionConfig = DiscriminativePick<
|
|||||||
orgId: string;
|
orgId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TerraformCloudOrg = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TerraformCloudApp = {
|
|
||||||
name: string;
|
|
||||||
id: string;
|
|
||||||
envs: { name: string; id: string }[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TerraformCloudOrgWithApps = TerraformCloudOrg & {
|
|
||||||
apps: TerraformCloudApp[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TTerraformCloudVariableSet = {
|
export type TTerraformCloudVariableSet = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -58,12 +43,3 @@ export type TTerraformCloudOrganization = {
|
|||||||
variableSets: TTerraformCloudVariableSet[];
|
variableSets: TTerraformCloudVariableSet[];
|
||||||
workspaces: TTerraformCloudWorkspace[];
|
workspaces: TTerraformCloudWorkspace[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TTerraformCloudConnectionOrganization = TTerraformCloudOrganization;
|
|
||||||
export type TTerraformCloudConnectionVariableSet = TTerraformCloudVariableSet;
|
|
||||||
export type TTerraformCloudConnectionWorkspace = TTerraformCloudWorkspace;
|
|
||||||
|
|
||||||
export enum TerraformCloudSyncScope {
|
|
||||||
VariableSet = "variableSet",
|
|
||||||
Workspace = "workspace"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable no-await-in-loop */
|
/* eslint-disable no-await-in-loop */
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { logger } from "@app/lib/logger";
|
|
||||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||||
@@ -13,6 +12,7 @@ import {
|
|||||||
TerraformCloudVariable,
|
TerraformCloudVariable,
|
||||||
TTerraformCloudSyncWithCredentials
|
TTerraformCloudSyncWithCredentials
|
||||||
} from "./terraform-cloud-sync-types";
|
} from "./terraform-cloud-sync-types";
|
||||||
|
import { AxiosResponse } from "axios";
|
||||||
|
|
||||||
const getTerraformCloudVariables = async (
|
const getTerraformCloudVariables = async (
|
||||||
secretSync: TTerraformCloudSyncWithCredentials
|
secretSync: TTerraformCloudSyncWithCredentials
|
||||||
@@ -24,7 +24,7 @@ const getTerraformCloudVariables = async (
|
|||||||
}
|
}
|
||||||
} = secretSync;
|
} = secretSync;
|
||||||
|
|
||||||
let url;
|
let url: string;
|
||||||
let source: TerraformCloudVariable["source"];
|
let source: TerraformCloudVariable["source"];
|
||||||
|
|
||||||
if (destinationConfig.scope === TerraformCloudSyncScope.VariableSet) {
|
if (destinationConfig.scope === TerraformCloudSyncScope.VariableSet) {
|
||||||
@@ -35,18 +35,35 @@ const getTerraformCloudVariables = async (
|
|||||||
source = "workspace";
|
source = "workspace";
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await request.get<TerraformCloudApiResponse<TerraformCloudApiVariable[]>>(url, {
|
const headers = {
|
||||||
headers: {
|
Authorization: `Bearer ${apiToken}`,
|
||||||
Authorization: `Bearer ${apiToken}`,
|
"Content-Type": "application/vnd.api+json"
|
||||||
"Content-Type": "application/vnd.api+json"
|
};
|
||||||
|
|
||||||
|
const fetchAllPages = async (): Promise<TerraformCloudApiVariable[]> => {
|
||||||
|
let results: TerraformCloudApiVariable[] = [];
|
||||||
|
let nextUrl: string | null = url;
|
||||||
|
|
||||||
|
while (nextUrl) {
|
||||||
|
const res: AxiosResponse<TerraformCloudApiResponse<TerraformCloudApiVariable[]>> = await request.get<
|
||||||
|
TerraformCloudApiResponse<TerraformCloudApiVariable[]>
|
||||||
|
>(nextUrl, {
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.data?.data) {
|
||||||
|
results = results.concat(res.data.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextUrl = res.data?.links?.next ?? null;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.data || !response.data.data) {
|
return results;
|
||||||
return [];
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const variables: TerraformCloudVariable[] = response.data.data.map((variable: TerraformCloudApiVariable) => ({
|
const allVariableData = await fetchAllPages();
|
||||||
|
|
||||||
|
const variables: TerraformCloudVariable[] = allVariableData.map((variable) => ({
|
||||||
id: variable.id,
|
id: variable.id,
|
||||||
key: variable.attributes.key,
|
key: variable.attributes.key,
|
||||||
value: variable.attributes.value || "",
|
value: variable.attributes.value || "",
|
||||||
@@ -85,8 +102,6 @@ const deleteVariable = async (
|
|||||||
"Content-Type": "application/vnd.api+json"
|
"Content-Type": "application/vnd.api+json"
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`Deleted variable ${variable.key} from Terraform Cloud ${destinationConfig.scope}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new SecretSyncError({
|
throw new SecretSyncError({
|
||||||
error,
|
error,
|
||||||
@@ -137,8 +152,6 @@ const createVariable = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info(`Created variable ${key} in Terraform Cloud ${destinationConfig.scope}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new SecretSyncError({
|
throw new SecretSyncError({
|
||||||
error,
|
error,
|
||||||
@@ -188,8 +201,6 @@ const updateVariable = async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info(`Updated variable ${variable.key} in Terraform Cloud ${destinationConfig.scope}`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new SecretSyncError({
|
throw new SecretSyncError({
|
||||||
error,
|
error,
|
||||||
|
|||||||
@@ -18,14 +18,6 @@ export type TTerraformCloudSyncWithCredentials = TTerraformCloudSync & {
|
|||||||
connection: TTerraformCloudConnection;
|
connection: TTerraformCloudConnection;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TerraformCloudSecret = {
|
|
||||||
description: string;
|
|
||||||
is_secret: boolean;
|
|
||||||
key: string;
|
|
||||||
source: "project" | "workspace";
|
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TerraformCloudApiVariable = {
|
export type TerraformCloudApiVariable = {
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: "Create"
|
title: "Create"
|
||||||
openapi: "POST /api/v1/app-connections/terraform-cloud"
|
openapi: "POST /api/v1/app-connections/terraform-cloud"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Check out the configuration docs for [Terraform Cloud Connections](/integrations/app-connections/terraform-cloud) to learn how to obtain
|
||||||
|
the required credentials.
|
||||||
|
</Note>
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: "Update"
|
title: "Update"
|
||||||
openapi: "PATCH /api/v1/app-connections/terraform-cloud/{connectionId}"
|
openapi: "PATCH /api/v1/app-connections/terraform-cloud/{connectionId}"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
Check out the configuration docs for [Terraform Cloud Connections](/integrations/app-connections/terraform-cloud) to learn how to obtain
|
||||||
|
the required credentials.
|
||||||
|
</Note>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
---
|
---
|
||||||
title: "Create"
|
title: "Create"
|
||||||
openapi: "POST /api/v1/secret-syncs/terraform-cloud"
|
openapi: "POST /api/v1/secret-syncs/humanitec"
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Infisical supports connecting to Terraform Cloud using a service user.
|
|||||||

|

|
||||||
2. Select the **Terraform Cloud Connection** option from the connection options modal.
|
2. Select the **Terraform Cloud Connection** option from the connection options modal.
|
||||||

|

|
||||||
3. Fill the Terraform Cloud Connection modal, here you will need to provide the API Token generated in the previous step.
|
3. Fill out the Terraform Cloud Connection modal, here you will need to provide the API Token generated in the previous step.
|
||||||

|

|
||||||
4. Your **Terraform Cloud Connection** is now available for use.
|
4. Your **Terraform Cloud Connection** is now available for use.
|
||||||

|

|
||||||
|
|||||||
@@ -94,7 +94,7 @@ description: "Learn how to configure a Terraform Cloud Sync for Infisical."
|
|||||||
},
|
},
|
||||||
"destinationConfig": {
|
"destinationConfig": {
|
||||||
"scope": "variable-set",
|
"scope": "variable-set",
|
||||||
"destinationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
|
"destinationId": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
|
||||||
"destinationName": "my-variable-set",
|
"destinationName": "my-variable-set",
|
||||||
"org": "my-organization-id",
|
"org": "my-organization-id",
|
||||||
"category": "env"
|
"category": "env"
|
||||||
|
|||||||
-1
@@ -17,7 +17,6 @@ export const SecretSyncDestinationFields = () => {
|
|||||||
const { watch } = useFormContext<TSecretSyncForm>();
|
const { watch } = useFormContext<TSecretSyncForm>();
|
||||||
|
|
||||||
const destination = watch("destination");
|
const destination = watch("destination");
|
||||||
console.log(destination);
|
|
||||||
|
|
||||||
switch (destination) {
|
switch (destination) {
|
||||||
case SecretSync.AWSParameterStore:
|
case SecretSync.AWSParameterStore:
|
||||||
|
|||||||
+28
-38
@@ -1,10 +1,8 @@
|
|||||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||||
import { SingleValue } from "react-select";
|
import { SingleValue } from "react-select";
|
||||||
import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
|
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
||||||
|
|
||||||
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
import { SecretSyncConnectionField } from "@app/components/secret-syncs/forms/SecretSyncConnectionField";
|
||||||
import { FilterableSelect, FormControl, Select, SelectItem, Tooltip } from "@app/components/v2";
|
import { FilterableSelect, FormControl, Select, SelectItem } from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
TERRAFORM_CLOUD_SYNC_SCOPES,
|
TERRAFORM_CLOUD_SYNC_SCOPES,
|
||||||
TerraformCloudSyncCategory,
|
TerraformCloudSyncCategory,
|
||||||
@@ -82,6 +80,28 @@ export const TerraformCloudSyncFields = () => {
|
|||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
label="Category"
|
label="Category"
|
||||||
|
tooltipClassName="max-w-lg py-3"
|
||||||
|
tooltipText={
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<ul className="flex list-disc flex-col gap-3 pl-4">
|
||||||
|
<li>
|
||||||
|
<p className="text-mineshaft-300">
|
||||||
|
<span className="font-medium text-bunker-200">
|
||||||
|
Environment variables configure Terraform's behavior (e.g.,
|
||||||
|
credentials).
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p className="text-mineshaft-300">
|
||||||
|
<span className="font-medium text-bunker-200">
|
||||||
|
Terraform variables are used as input values in your configuration.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
value={value}
|
value={value}
|
||||||
@@ -92,9 +112,9 @@ export const TerraformCloudSyncFields = () => {
|
|||||||
placeholder="Select category..."
|
placeholder="Select category..."
|
||||||
dropdownContainerClassName="max-w-none"
|
dropdownContainerClassName="max-w-none"
|
||||||
>
|
>
|
||||||
{Object.values(TerraformCloudSyncCategory).map((category) => (
|
{Object.entries(TerraformCloudSyncCategory).map(([envKey, envValue]) => (
|
||||||
<SelectItem className="capitalize" value={category} key={category}>
|
<SelectItem className="capitalize" value={envValue} key={envValue}>
|
||||||
{category.replace("-", " ")}
|
{envKey.replace("-", " ")}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
@@ -157,22 +177,7 @@ export const TerraformCloudSyncFields = () => {
|
|||||||
name="destinationConfig.destinationId"
|
name="destinationConfig.destinationId"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl isError={Boolean(error)} errorText={error?.message} label="Variable Set">
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Variable Set"
|
|
||||||
helperText={
|
|
||||||
<Tooltip
|
|
||||||
className="max-w-md"
|
|
||||||
content="Ensure that the variable set exists in the selected organization and the service account used on this connection has write permissions for the specified variable set."
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<span>Don't see the variable set you're looking for?</span>{" "}
|
|
||||||
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FilterableSelect
|
<FilterableSelect
|
||||||
menuPlacement="top"
|
menuPlacement="top"
|
||||||
isLoading={isOrganizationsPending && Boolean(connectionId) && Boolean(currentOrg)}
|
isLoading={isOrganizationsPending && Boolean(connectionId) && Boolean(currentOrg)}
|
||||||
@@ -203,22 +208,7 @@ export const TerraformCloudSyncFields = () => {
|
|||||||
name="destinationConfig.destinationId"
|
name="destinationConfig.destinationId"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
render={({ field: { value, onChange }, fieldState: { error } }) => (
|
||||||
<FormControl
|
<FormControl isError={Boolean(error)} errorText={error?.message} label="Workspace">
|
||||||
isError={Boolean(error)}
|
|
||||||
errorText={error?.message}
|
|
||||||
label="Workspace"
|
|
||||||
helperText={
|
|
||||||
<Tooltip
|
|
||||||
className="max-w-md"
|
|
||||||
content="Ensure that the workspace exists in the selected organization and the service account used on this connection has write permissions for the specified workspace."
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<span>Don't see the workspace you're looking for?</span>{" "}
|
|
||||||
<FontAwesomeIcon icon={faCircleInfo} className="text-mineshaft-400" />
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<FilterableSelect
|
<FilterableSelect
|
||||||
menuPlacement="top"
|
menuPlacement="top"
|
||||||
isLoading={isOrganizationsPending && Boolean(connectionId) && Boolean(currentOrg)}
|
isLoading={isOrganizationsPending && Boolean(connectionId) && Boolean(currentOrg)}
|
||||||
|
|||||||
+2
-2
@@ -14,14 +14,14 @@ export const TerraformCloudSyncDestinationSchema = BaseSecretSyncSchema().merge(
|
|||||||
z.object({
|
z.object({
|
||||||
scope: z.literal(TerraformCloudSyncScope.VariableSet),
|
scope: z.literal(TerraformCloudSyncScope.VariableSet),
|
||||||
org: z.string().trim().min(1, "Organization required"),
|
org: z.string().trim().min(1, "Organization required"),
|
||||||
destinationId: z.string().trim().min(1, "Variable set id required"),
|
destinationId: z.string().trim().min(1, "Variable Set required"),
|
||||||
destinationName: z.string().trim().min(1, "Variable set name required"),
|
destinationName: z.string().trim().min(1, "Variable set name required"),
|
||||||
category: z.nativeEnum(TerraformCloudSyncCategory)
|
category: z.nativeEnum(TerraformCloudSyncCategory)
|
||||||
}),
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
scope: z.literal(TerraformCloudSyncScope.Workspace),
|
scope: z.literal(TerraformCloudSyncScope.Workspace),
|
||||||
org: z.string().trim().min(1, "Organization required"),
|
org: z.string().trim().min(1, "Organization required"),
|
||||||
destinationId: z.string().trim().min(1, "Workspace id required"),
|
destinationId: z.string().trim().min(1, "Workspace required"),
|
||||||
destinationName: z.string().trim().min(1, "Workspace name required"),
|
destinationName: z.string().trim().min(1, "Workspace name required"),
|
||||||
category: z.nativeEnum(TerraformCloudSyncCategory)
|
category: z.nativeEnum(TerraformCloudSyncCategory)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ export const getAppConnectionMethodDetails = (method: TAppConnection["method"])
|
|||||||
case DatabricksConnectionMethod.ServicePrincipal:
|
case DatabricksConnectionMethod.ServicePrincipal:
|
||||||
return { name: "Service Principal", icon: faUser };
|
return { name: "Service Principal", icon: faUser };
|
||||||
case HumanitecConnectionMethod.ApiToken:
|
case HumanitecConnectionMethod.ApiToken:
|
||||||
return { name: "API Token", icon: faKey };
|
|
||||||
case TerraformCloudConnectionMethod.ApiToken:
|
case TerraformCloudConnectionMethod.ApiToken:
|
||||||
return { name: "API Token", icon: faKey };
|
return { name: "API Token", icon: faKey };
|
||||||
case PostgresConnectionMethod.UsernameAndPassword:
|
case PostgresConnectionMethod.UsernameAndPassword:
|
||||||
|
|||||||
+2
-2
@@ -35,7 +35,7 @@ const formSchema = z.discriminatedUnion("method", [
|
|||||||
rootSchema.extend({
|
rootSchema.extend({
|
||||||
method: z.literal(TerraformCloudConnectionMethod.ApiToken),
|
method: z.literal(TerraformCloudConnectionMethod.ApiToken),
|
||||||
credentials: z.object({
|
credentials: z.object({
|
||||||
apiToken: z.string().trim().min(1, "Service API Token required")
|
apiToken: z.string().trim().min(1, "API Token required")
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
@@ -102,7 +102,7 @@ export const TerraformCloudConnectionForm = ({ appConnection, onSubmit }: Props)
|
|||||||
<FormControl
|
<FormControl
|
||||||
errorText={error?.message}
|
errorText={error?.message}
|
||||||
isError={Boolean(error?.message)}
|
isError={Boolean(error?.message)}
|
||||||
label="Service API Token"
|
label="API Token"
|
||||||
>
|
>
|
||||||
<SecretInput
|
<SecretInput
|
||||||
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
|
containerClassName="text-gray-400 group-focus-within:!border-primary-400/50 border border-mineshaft-500 bg-mineshaft-900 px-2.5 py-1.5"
|
||||||
|
|||||||
+2
-2
@@ -9,7 +9,6 @@ import { ProjectPermissionSub } from "@app/context";
|
|||||||
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
import { ProjectPermissionSecretSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||||
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
import { APP_CONNECTION_MAP } from "@app/helpers/appConnections";
|
||||||
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
import { SecretSync, TSecretSync } from "@app/hooks/api/secretSyncs";
|
||||||
import { TerraformCloudSyncDestinationCol } from "@app/pages/secret-manager/IntegrationsListPage/components/SecretSyncsTab/SecretSyncTable/SecretSyncDestinationCol/TerraformCloudSyncDestinationCol";
|
|
||||||
import { AwsParameterStoreSyncDestinationSection } from "@app/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/AwsParameterStoreSyncDestinationSection";
|
import { AwsParameterStoreSyncDestinationSection } from "@app/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/AwsParameterStoreSyncDestinationSection";
|
||||||
import { AwsSecretsManagerSyncDestinationSection } from "@app/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/AwsSecretsManagerSyncDestinationSection";
|
import { AwsSecretsManagerSyncDestinationSection } from "@app/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/AwsSecretsManagerSyncDestinationSection";
|
||||||
import { DatabricksSyncDestinationSection } from "@app/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/DatabricksSyncDestinationSection";
|
import { DatabricksSyncDestinationSection } from "@app/pages/secret-manager/SecretSyncDetailsByIDPage/components/SecretSyncDestinationSection/DatabricksSyncDestinationSection";
|
||||||
@@ -19,6 +18,7 @@ import { AzureAppConfigurationSyncDestinationSection } from "./AzureAppConfigura
|
|||||||
import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection";
|
import { AzureKeyVaultSyncDestinationSection } from "./AzureKeyVaultSyncDestinationSection";
|
||||||
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
|
import { GcpSyncDestinationSection } from "./GcpSyncDestinationSection";
|
||||||
import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection";
|
import { HumanitecSyncDestinationSection } from "./HumanitecSyncDestinationSection";
|
||||||
|
import { TerraformCloudSyncDestinationSection } from "./TerraformCloudSyncDestinationCol";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
secretSync: TSecretSync;
|
secretSync: TSecretSync;
|
||||||
@@ -59,7 +59,7 @@ export const SecretSyncDestinationSection = ({ secretSync, onEditDestination }:
|
|||||||
DestinationComponents = <HumanitecSyncDestinationSection secretSync={secretSync} />;
|
DestinationComponents = <HumanitecSyncDestinationSection secretSync={secretSync} />;
|
||||||
break;
|
break;
|
||||||
case SecretSync.TerraformCloud:
|
case SecretSync.TerraformCloud:
|
||||||
DestinationComponents = <TerraformCloudSyncDestinationCol secretSync={secretSync} />;
|
DestinationComponents = <TerraformCloudSyncDestinationSection secretSync={secretSync} />;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
throw new Error(`Unhandled Destination Section components: ${destination}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user