mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 06:35:21 +00:00
Terraform Cloud Secret Sync Integration
This commit is contained in:
@@ -1791,9 +1791,10 @@ export const SecretSyncs = {
|
||||
},
|
||||
TERRAFORM_CLOUD: {
|
||||
org: "The ID of the Terraform Cloud org to sync secrets to.",
|
||||
project: "The ID of the Terraform Cloud project to sync secrets to.",
|
||||
workspace: "The ID of the Terraform Cloud workspace to sync secrets to.",
|
||||
scope: "The Terraform Cloud scope that secrets should be synced to."
|
||||
destinationName: "The name of the Terraform Cloud variable set / workspace to sync secrets to.",
|
||||
destinationId: "The ID of the Terraform Cloud variable set / workspace to sync secrets to.",
|
||||
scope: "The Terraform Cloud scope that secrets should be synced to.",
|
||||
category: "The Terraform Cloud category that secrets should be synced to."
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
|
||||
import {
|
||||
CreateTerraformCloudConnectionSchema,
|
||||
SanitizedTerraformCloudConnectionSchema,
|
||||
TerraformCloudOrgWithApps,
|
||||
TTerraformCloudOrganization,
|
||||
UpdateTerraformCloudConnectionSchema
|
||||
} from "@app/services/app-connection/terraform-cloud";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@@ -38,10 +38,12 @@ export const registerTerraformCloudConnectionRouter = async (server: FastifyZodP
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
projects: z
|
||||
variableSets: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string()
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
global: z.boolean().optional()
|
||||
})
|
||||
.array(),
|
||||
workspaces: z
|
||||
@@ -58,7 +60,7 @@ export const registerTerraformCloudConnectionRouter = async (server: FastifyZodP
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const organizations: TerraformCloudOrgWithApps[] =
|
||||
const organizations: TTerraformCloudOrganization[] =
|
||||
await server.services.appConnection.terraformCloud.listOrganizations(connectionId, req.permission);
|
||||
|
||||
return organizations;
|
||||
|
||||
@@ -38,6 +38,12 @@ import {
|
||||
THumanitecConnectionInput,
|
||||
TValidateHumanitecConnectionCredentials
|
||||
} from "./humanitec";
|
||||
import {
|
||||
TTerraformCloudConnection,
|
||||
TTerraformCloudConnectionConfig,
|
||||
TTerraformCloudConnectionInput,
|
||||
TValidateTerraformCloudConnectionCredentials
|
||||
} from "./terraform-cloud";
|
||||
|
||||
export type TAppConnection = { id: string } & (
|
||||
| TAwsConnection
|
||||
@@ -47,6 +53,7 @@ export type TAppConnection = { id: string } & (
|
||||
| TAzureAppConfigurationConnection
|
||||
| TDatabricksConnection
|
||||
| THumanitecConnection
|
||||
| TTerraformCloudConnection
|
||||
);
|
||||
|
||||
export type TAppConnectionInput = { id: string } & (
|
||||
@@ -57,6 +64,7 @@ export type TAppConnectionInput = { id: string } & (
|
||||
| TAzureAppConfigurationConnectionInput
|
||||
| TDatabricksConnectionInput
|
||||
| THumanitecConnectionInput
|
||||
| TTerraformCloudConnectionInput
|
||||
);
|
||||
|
||||
export type TCreateAppConnectionDTO = Pick<
|
||||
@@ -75,7 +83,8 @@ export type TAppConnectionConfig =
|
||||
| TAzureKeyVaultConnectionConfig
|
||||
| TAzureAppConfigurationConnectionConfig
|
||||
| TDatabricksConnectionConfig
|
||||
| THumanitecConnectionConfig;
|
||||
| THumanitecConnectionConfig
|
||||
| TTerraformCloudConnectionConfig;
|
||||
|
||||
export type TValidateAppConnectionCredentials =
|
||||
| TValidateAwsConnectionCredentials
|
||||
@@ -84,7 +93,8 @@ export type TValidateAppConnectionCredentials =
|
||||
| TValidateAzureKeyVaultConnectionCredentials
|
||||
| TValidateAzureAppConfigurationConnectionCredentials
|
||||
| TValidateDatabricksConnectionCredentials
|
||||
| TValidateHumanitecConnectionCredentials;
|
||||
| TValidateHumanitecConnectionCredentials
|
||||
| TValidateTerraformCloudConnectionCredentials;
|
||||
|
||||
export type TListAwsConnectionKmsKeys = {
|
||||
connectionId: string;
|
||||
|
||||
+16
-14
@@ -10,7 +10,7 @@ import {
|
||||
TTerraformCloudConnection,
|
||||
TTerraformCloudConnectionConfig,
|
||||
TTerraformCloudOrganization,
|
||||
TTerraformCloudProject,
|
||||
TTerraformCloudVariableSet,
|
||||
TTerraformCloudWorkspace
|
||||
} from "./terraform-cloud-connection-types";
|
||||
|
||||
@@ -81,12 +81,12 @@ export const listOrganizations = async (
|
||||
}
|
||||
|
||||
const orgEntities = orgsResponse.data.data;
|
||||
const orgsWithProjectsAndWorkspaces: TTerraformCloudOrganization[] = [];
|
||||
const orgsWithVariableSetsAndWorkspaces: TTerraformCloudOrganization[] = [];
|
||||
|
||||
const projectPromises = orgEntities.map((org) =>
|
||||
const variableSetPromises = orgEntities.map((org) =>
|
||||
request
|
||||
.get<{ data: { id: string; attributes: { name: string } }[] }>(
|
||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${org.id}/projects`,
|
||||
.get<{ data: { id: string; attributes: { name: string; description?: string; global?: boolean } }[] }>(
|
||||
`${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${org.id}/varsets`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
@@ -111,19 +111,21 @@ export const listOrganizations = async (
|
||||
.catch(() => ({ data: { data: [] } }))
|
||||
);
|
||||
|
||||
const [projectResponses, workspaceResponses] = await Promise.all([
|
||||
Promise.all(projectPromises),
|
||||
const [variableSetResponses, workspaceResponses] = await Promise.all([
|
||||
Promise.all(variableSetPromises),
|
||||
Promise.all(workspacePromises)
|
||||
]);
|
||||
|
||||
for (let i = 0; i < orgEntities.length; i += 1) {
|
||||
const org = orgEntities[i];
|
||||
const projectsData = projectResponses[i].data?.data || [];
|
||||
const variableSetsData = variableSetResponses[i].data?.data || [];
|
||||
const workspacesData = workspaceResponses[i].data?.data || [];
|
||||
|
||||
const projects: TTerraformCloudProject[] = projectsData.map((project) => ({
|
||||
id: project.id,
|
||||
name: project.attributes.name
|
||||
const variableSets: TTerraformCloudVariableSet[] = variableSetsData.map((varSet) => ({
|
||||
id: varSet.id,
|
||||
name: varSet.attributes.name,
|
||||
description: varSet.attributes.description,
|
||||
global: varSet.attributes.global
|
||||
}));
|
||||
|
||||
const workspaces: TTerraformCloudWorkspace[] = workspacesData.map((workspace) => ({
|
||||
@@ -131,13 +133,13 @@ export const listOrganizations = async (
|
||||
name: workspace.attributes.name
|
||||
}));
|
||||
|
||||
orgsWithProjectsAndWorkspaces.push({
|
||||
orgsWithVariableSetsAndWorkspaces.push({
|
||||
id: org.id,
|
||||
name: org.attributes.name,
|
||||
projects,
|
||||
variableSets,
|
||||
workspaces
|
||||
});
|
||||
}
|
||||
|
||||
return orgsWithProjectsAndWorkspaces;
|
||||
return orgsWithVariableSetsAndWorkspaces;
|
||||
};
|
||||
|
||||
+6
-4
@@ -39,9 +39,11 @@ export type TerraformCloudOrgWithApps = TerraformCloudOrg & {
|
||||
apps: TerraformCloudApp[];
|
||||
};
|
||||
|
||||
export type TTerraformCloudProject = {
|
||||
export type TTerraformCloudVariableSet = {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
global?: boolean;
|
||||
};
|
||||
|
||||
export type TTerraformCloudWorkspace = {
|
||||
@@ -52,15 +54,15 @@ export type TTerraformCloudWorkspace = {
|
||||
export type TTerraformCloudOrganization = {
|
||||
id: string;
|
||||
name: string;
|
||||
projects: TTerraformCloudProject[];
|
||||
variableSets: TTerraformCloudVariableSet[];
|
||||
workspaces: TTerraformCloudWorkspace[];
|
||||
};
|
||||
|
||||
export type TTerraformCloudConnectionOrganization = TTerraformCloudOrganization;
|
||||
export type TTerraformCloudConnectionProject = TTerraformCloudProject;
|
||||
export type TTerraformCloudConnectionVariableSet = TTerraformCloudVariableSet;
|
||||
export type TTerraformCloudConnectionWorkspace = TTerraformCloudWorkspace;
|
||||
|
||||
export enum TerraformCloudSyncScope {
|
||||
Project = "project",
|
||||
VariableSet = "variableSet",
|
||||
Workspace = "workspace"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
export enum TerraformCloudSyncScope {
|
||||
Project = "project",
|
||||
VariableSet = "variable-set",
|
||||
Workspace = "workspace"
|
||||
}
|
||||
|
||||
export enum TerraformCloudSyncCategory {
|
||||
Environment = "env",
|
||||
Terraform = "terraform"
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { request } from "@app/lib/config/request";
|
||||
import { logger } from "@app/lib/logger";
|
||||
import { IntegrationUrls } from "@app/services/integration-auth/integration-list";
|
||||
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
|
||||
import { SECRET_SYNC_NAME_MAP } from "@app/services/secret-sync/secret-sync-maps";
|
||||
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
import { SECRET_SYNC_NAME_MAP } from "../secret-sync-maps";
|
||||
import { TerraformCloudSyncScope } from "./terraform-cloud-sync-enums";
|
||||
import {
|
||||
TerraformCloudApiResponse,
|
||||
@@ -25,10 +25,14 @@ const getTerraformCloudVariables = async (
|
||||
} = secretSync;
|
||||
|
||||
let url;
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.Project) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/projects/${destinationConfig.project}/vars`;
|
||||
let source: TerraformCloudVariable["source"];
|
||||
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.VariableSet) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/varsets/${destinationConfig.destinationId}/relationships/vars`;
|
||||
source = "varset";
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars`;
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${destinationConfig.destinationId}/vars`;
|
||||
source = "workspace";
|
||||
}
|
||||
|
||||
const response = await request.get<TerraformCloudApiResponse<TerraformCloudApiVariable[]>>(url, {
|
||||
@@ -49,7 +53,7 @@ const getTerraformCloudVariables = async (
|
||||
sensitive: variable.attributes.sensitive,
|
||||
description: variable.attributes.description || "",
|
||||
category: variable.attributes.category,
|
||||
source: destinationConfig.scope === TerraformCloudSyncScope.Project ? "project" : "workspace"
|
||||
source
|
||||
}));
|
||||
|
||||
return variables;
|
||||
@@ -68,10 +72,11 @@ const deleteVariable = async (
|
||||
|
||||
try {
|
||||
let url;
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.Project) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/projects/${destinationConfig.project}/vars/${variable.id}`;
|
||||
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.VariableSet) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/varsets/${destinationConfig.destinationId}/relationships/vars/${variable.id}`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars/${variable.id}`;
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${destinationConfig.destinationId}/vars/${variable.id}`;
|
||||
}
|
||||
|
||||
await request.delete(url, {
|
||||
@@ -104,10 +109,11 @@ const createVariable = async (
|
||||
} = secretSync;
|
||||
|
||||
let url;
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.Project) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/projects/${destinationConfig.project}/vars`;
|
||||
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.VariableSet) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/varsets/${destinationConfig.destinationId}/relationships/vars`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars`;
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${destinationConfig.destinationId}/vars`;
|
||||
}
|
||||
|
||||
await request.post(
|
||||
@@ -119,9 +125,8 @@ const createVariable = async (
|
||||
key,
|
||||
value: secretMap[key].value,
|
||||
description: secretMap[key].comment || "",
|
||||
category: "env",
|
||||
sensitive: true,
|
||||
hcl: false
|
||||
category: secretSync.destinationConfig.category,
|
||||
sensitive: true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -156,10 +161,11 @@ const updateVariable = async (
|
||||
} = secretSync;
|
||||
|
||||
let url;
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.Project) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/projects/${destinationConfig.project}/vars/${variable.id}`;
|
||||
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.VariableSet) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/varsets/${destinationConfig.destinationId}/relationships/vars/${variable.id}`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars/${variable.id}`;
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/workspaces/${destinationConfig.destinationId}/vars/${variable.id}`;
|
||||
}
|
||||
|
||||
await request.patch(
|
||||
@@ -170,7 +176,8 @@ const updateVariable = async (
|
||||
id: variable.id,
|
||||
attributes: {
|
||||
value: secretMap[variable.key].value,
|
||||
description: secretMap[variable.key].comment || ""
|
||||
description: secretMap[variable.key].comment || "",
|
||||
category: secretSync.destinationConfig.category
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -9,24 +9,39 @@ import {
|
||||
GenericUpdateSecretSyncFieldsSchema
|
||||
} from "@app/services/secret-sync/secret-sync-schemas";
|
||||
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
|
||||
import { TerraformCloudSyncScope } from "@app/services/secret-sync/terraform-cloud/terraform-cloud-sync-enums";
|
||||
import {
|
||||
TerraformCloudSyncCategory,
|
||||
TerraformCloudSyncScope
|
||||
} from "@app/services/secret-sync/terraform-cloud/terraform-cloud-sync-enums";
|
||||
|
||||
const TerraformCloudSyncDestinationConfigSchema = z.discriminatedUnion("scope", [
|
||||
z.object({
|
||||
scope: z.literal(TerraformCloudSyncScope.Project).describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.scope),
|
||||
scope: z
|
||||
.literal(TerraformCloudSyncScope.VariableSet)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.scope),
|
||||
org: z.string().min(1, "Org ID is required").describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.org),
|
||||
project: z
|
||||
destinationName: z
|
||||
.string()
|
||||
.min(1, "Project ID is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.project)
|
||||
.min(1, "Variable set name is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.destinationName),
|
||||
destinationId: z
|
||||
.string()
|
||||
.min(1, "Variable set ID is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.destinationId),
|
||||
category: z.nativeEnum(TerraformCloudSyncCategory).describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.category)
|
||||
}),
|
||||
z.object({
|
||||
scope: z.literal(TerraformCloudSyncScope.Workspace).describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.scope),
|
||||
org: z.string().min(1, "Org ID is required").describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.org),
|
||||
workspace: z
|
||||
destinationName: z
|
||||
.string()
|
||||
.min(1, "Workspace name is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.destinationName),
|
||||
destinationId: z
|
||||
.string()
|
||||
.min(1, "Workspace ID is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.workspace)
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.destinationId),
|
||||
category: z.nativeEnum(TerraformCloudSyncCategory).describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.category)
|
||||
})
|
||||
]);
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ export type TerraformCloudVariable = {
|
||||
sensitive: boolean;
|
||||
description: string;
|
||||
category: "terraform" | "env";
|
||||
source: "project" | "workspace";
|
||||
source: "varset" | "workspace";
|
||||
};
|
||||
|
||||
export type TerraformCloudApiResponse<T> = {
|
||||
@@ -83,8 +83,3 @@ export type TerraformCloudApiResponse<T> = {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export enum TerraformCloudSyncScope {
|
||||
Project = "project",
|
||||
Workspace = "workspace"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user