mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 21:51:56 +00:00
Initial Commit for terraform cloud intergation
This commit is contained in:
@@ -6,7 +6,8 @@ export enum SecretSync {
|
||||
AzureKeyVault = "azure-key-vault",
|
||||
AzureAppConfiguration = "azure-app-configuration",
|
||||
Databricks = "databricks",
|
||||
Humanitec = "humanitec"
|
||||
Humanitec = "humanitec",
|
||||
TerraformCloud = "terraform-cloud"
|
||||
}
|
||||
|
||||
export enum SecretSyncInitialSyncBehavior {
|
||||
|
||||
@@ -26,6 +26,7 @@ import { GCP_SYNC_LIST_OPTION } from "./gcp";
|
||||
import { GcpSyncFns } from "./gcp/gcp-sync-fns";
|
||||
import { HUMANITEC_SYNC_LIST_OPTION } from "./humanitec";
|
||||
import { HumanitecSyncFns } from "./humanitec/humanitec-sync-fns";
|
||||
import { TERRAFORM_CLOUD_SYNC_LIST_OPTION, TerraformCloudSyncFns } from "./terraform-cloud";
|
||||
|
||||
const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.AWSParameterStore]: AWS_PARAMETER_STORE_SYNC_LIST_OPTION,
|
||||
@@ -35,7 +36,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
|
||||
[SecretSync.AzureKeyVault]: AZURE_KEY_VAULT_SYNC_LIST_OPTION,
|
||||
[SecretSync.AzureAppConfiguration]: AZURE_APP_CONFIGURATION_SYNC_LIST_OPTION,
|
||||
[SecretSync.Databricks]: DATABRICKS_SYNC_LIST_OPTION,
|
||||
[SecretSync.Humanitec]: HUMANITEC_SYNC_LIST_OPTION
|
||||
[SecretSync.Humanitec]: HUMANITEC_SYNC_LIST_OPTION,
|
||||
[SecretSync.TerraformCloud]: TERRAFORM_CLOUD_SYNC_LIST_OPTION
|
||||
};
|
||||
|
||||
export const listSecretSyncOptions = () => {
|
||||
@@ -121,6 +123,8 @@ export const SecretSyncFns = {
|
||||
}).syncSecrets(secretSync, secretMap);
|
||||
case SecretSync.Humanitec:
|
||||
return HumanitecSyncFns.syncSecrets(secretSync, secretMap);
|
||||
case SecretSync.TerraformCloud:
|
||||
return TerraformCloudSyncFns.syncSecrets(secretSync, secretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -165,6 +169,9 @@ export const SecretSyncFns = {
|
||||
case SecretSync.Humanitec:
|
||||
secretMap = await HumanitecSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
case SecretSync.TerraformCloud:
|
||||
secretMap = await TerraformCloudSyncFns.getSecrets(secretSync);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
@@ -207,6 +214,8 @@ export const SecretSyncFns = {
|
||||
}).removeSecrets(secretSync, secretMap);
|
||||
case SecretSync.Humanitec:
|
||||
return HumanitecSyncFns.removeSecrets(secretSync, secretMap);
|
||||
case SecretSync.TerraformCloud:
|
||||
return TerraformCloudSyncFns.removeSecrets(secretSync, secretMap);
|
||||
default:
|
||||
throw new Error(
|
||||
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
|
||||
|
||||
@@ -9,7 +9,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
|
||||
[SecretSync.AzureKeyVault]: "Azure Key Vault",
|
||||
[SecretSync.AzureAppConfiguration]: "Azure App Configuration",
|
||||
[SecretSync.Databricks]: "Databricks",
|
||||
[SecretSync.Humanitec]: "Humanitec"
|
||||
[SecretSync.Humanitec]: "Humanitec",
|
||||
[SecretSync.TerraformCloud]: "Terraform Cloud"
|
||||
};
|
||||
|
||||
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
@@ -20,5 +21,6 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
|
||||
[SecretSync.AzureKeyVault]: AppConnection.AzureKeyVault,
|
||||
[SecretSync.AzureAppConfiguration]: AppConnection.AzureAppConfiguration,
|
||||
[SecretSync.Databricks]: AppConnection.Databricks,
|
||||
[SecretSync.Humanitec]: AppConnection.Humanitec
|
||||
[SecretSync.Humanitec]: AppConnection.Humanitec,
|
||||
[SecretSync.TerraformCloud]: AppConnection.TerraformCloud
|
||||
};
|
||||
|
||||
@@ -49,6 +49,12 @@ import {
|
||||
THumanitecSyncListItem,
|
||||
THumanitecSyncWithCredentials
|
||||
} from "./humanitec";
|
||||
import {
|
||||
TTerraformCloudSync,
|
||||
TTerraformCloudSyncInput,
|
||||
TTerraformCloudSyncListItem,
|
||||
TTerraformCloudSyncWithCredentials
|
||||
} from "./terraform-cloud";
|
||||
|
||||
export type TSecretSync =
|
||||
| TAwsParameterStoreSync
|
||||
@@ -58,7 +64,8 @@ export type TSecretSync =
|
||||
| TAzureKeyVaultSync
|
||||
| TAzureAppConfigurationSync
|
||||
| TDatabricksSync
|
||||
| THumanitecSync;
|
||||
| THumanitecSync
|
||||
| TTerraformCloudSync;
|
||||
|
||||
export type TSecretSyncWithCredentials =
|
||||
| TAwsParameterStoreSyncWithCredentials
|
||||
@@ -68,7 +75,8 @@ export type TSecretSyncWithCredentials =
|
||||
| TAzureKeyVaultSyncWithCredentials
|
||||
| TAzureAppConfigurationSyncWithCredentials
|
||||
| TDatabricksSyncWithCredentials
|
||||
| THumanitecSyncWithCredentials;
|
||||
| THumanitecSyncWithCredentials
|
||||
| TTerraformCloudSyncWithCredentials;
|
||||
|
||||
export type TSecretSyncInput =
|
||||
| TAwsParameterStoreSyncInput
|
||||
@@ -78,7 +86,8 @@ export type TSecretSyncInput =
|
||||
| TAzureKeyVaultSyncInput
|
||||
| TAzureAppConfigurationSyncInput
|
||||
| TDatabricksSyncInput
|
||||
| THumanitecSyncInput;
|
||||
| THumanitecSyncInput
|
||||
| TTerraformCloudSyncInput;
|
||||
|
||||
export type TSecretSyncListItem =
|
||||
| TAwsParameterStoreSyncListItem
|
||||
@@ -88,7 +97,8 @@ export type TSecretSyncListItem =
|
||||
| TAzureKeyVaultSyncListItem
|
||||
| TAzureAppConfigurationSyncListItem
|
||||
| TDatabricksSyncListItem
|
||||
| THumanitecSyncListItem;
|
||||
| THumanitecSyncListItem
|
||||
| TTerraformCloudSyncListItem;
|
||||
|
||||
export type TSyncOptionsConfig = {
|
||||
canImportSecrets: boolean;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./terraform-cloud-sync-constants";
|
||||
export * from "./terraform-cloud-sync-enums";
|
||||
export * from "./terraform-cloud-sync-fns";
|
||||
export * from "./terraform-cloud-sync-schemas";
|
||||
export * from "./terraform-cloud-sync-types";
|
||||
@@ -0,0 +1,10 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
|
||||
|
||||
export const TERRAFORM_CLOUD_SYNC_LIST_OPTION: TSecretSyncListItem = {
|
||||
name: "Terraform Cloud",
|
||||
destination: SecretSync.TerraformCloud,
|
||||
connection: AppConnection.TerraformCloud,
|
||||
canImportSecrets: false
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export enum TerraformCloudSyncScope {
|
||||
Project = "project",
|
||||
Workspace = "workspace"
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
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 { TerraformCloudSyncScope } from "./terraform-cloud-sync-enums";
|
||||
import {
|
||||
TerraformCloudApiResponse,
|
||||
TerraformCloudApiVariable,
|
||||
TerraformCloudVariable,
|
||||
TTerraformCloudSyncWithCredentials
|
||||
} from "./terraform-cloud-sync-types";
|
||||
|
||||
const getTerraformCloudVariables = async (
|
||||
secretSync: TTerraformCloudSyncWithCredentials
|
||||
): Promise<TerraformCloudVariable[]> => {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
credentials: { apiToken }
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
let url;
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.Project) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/projects/${destinationConfig.project}/vars`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars`;
|
||||
}
|
||||
|
||||
const response = await request.get<TerraformCloudApiResponse<TerraformCloudApiVariable[]>>(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
"Content-Type": "application/vnd.api+json"
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.data || !response.data.data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const variables: TerraformCloudVariable[] = response.data.data.map((variable: TerraformCloudApiVariable) => ({
|
||||
id: variable.id,
|
||||
key: variable.attributes.key,
|
||||
value: variable.attributes.value || "",
|
||||
sensitive: variable.attributes.sensitive,
|
||||
description: variable.attributes.description || "",
|
||||
category: variable.attributes.category,
|
||||
source: destinationConfig.scope === TerraformCloudSyncScope.Project ? "project" : "workspace"
|
||||
}));
|
||||
|
||||
return variables;
|
||||
};
|
||||
|
||||
const deleteVariable = async (
|
||||
secretSync: TTerraformCloudSyncWithCredentials,
|
||||
variable: TerraformCloudVariable
|
||||
): Promise<void> => {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
credentials: { apiToken }
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
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}`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars/${variable.id}`;
|
||||
}
|
||||
|
||||
await request.delete(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
"Content-Type": "application/vnd.api+json"
|
||||
}
|
||||
});
|
||||
|
||||
logger.info(`Deleted variable ${variable.key} from Terraform Cloud ${destinationConfig.scope}`);
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: variable.key
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const createVariable = async (
|
||||
secretSync: TTerraformCloudSyncWithCredentials,
|
||||
secretMap: TSecretMap,
|
||||
key: string
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
credentials: { apiToken }
|
||||
}
|
||||
} = secretSync;
|
||||
|
||||
let url;
|
||||
if (destinationConfig.scope === TerraformCloudSyncScope.Project) {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/projects/${destinationConfig.project}/vars`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars`;
|
||||
}
|
||||
|
||||
await request.post(
|
||||
url,
|
||||
{
|
||||
data: {
|
||||
type: "vars",
|
||||
attributes: {
|
||||
key,
|
||||
value: secretMap[key].value,
|
||||
description: secretMap[key].comment || "",
|
||||
category: "env",
|
||||
sensitive: true,
|
||||
hcl: false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
"Content-Type": "application/vnd.api+json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
logger.info(`Created variable ${key} in Terraform Cloud ${destinationConfig.scope}`);
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: key
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const updateVariable = async (
|
||||
secretSync: TTerraformCloudSyncWithCredentials,
|
||||
secretMap: TSecretMap,
|
||||
variable: TerraformCloudVariable
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const {
|
||||
destinationConfig,
|
||||
connection: {
|
||||
credentials: { apiToken }
|
||||
}
|
||||
} = 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}`;
|
||||
} else {
|
||||
url = `${IntegrationUrls.TERRAFORM_CLOUD_API_URL}/api/v2/organizations/${destinationConfig.org}/workspaces/${destinationConfig.workspace}/vars/${variable.id}`;
|
||||
}
|
||||
|
||||
await request.patch(
|
||||
url,
|
||||
{
|
||||
data: {
|
||||
type: "vars",
|
||||
id: variable.id,
|
||||
attributes: {
|
||||
value: secretMap[variable.key].value,
|
||||
description: secretMap[variable.key].comment || ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiToken}`,
|
||||
"Content-Type": "application/vnd.api+json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
logger.info(`Updated variable ${variable.key} in Terraform Cloud ${destinationConfig.scope}`);
|
||||
} catch (error) {
|
||||
throw new SecretSyncError({
|
||||
error,
|
||||
secretKey: variable.key
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const TerraformCloudSyncFns = {
|
||||
syncSecrets: async (secretSync: TTerraformCloudSyncWithCredentials, secretMap: TSecretMap): Promise<void> => {
|
||||
const terraformCloudVariables = await getTerraformCloudVariables(secretSync);
|
||||
const terraformCloudVariablesMap = new Map<string, TerraformCloudVariable>(
|
||||
terraformCloudVariables.map((v) => [v.key, v])
|
||||
);
|
||||
|
||||
const secretKeys = Object.keys(secretMap);
|
||||
for (const key of secretKeys) {
|
||||
const existingVariable = terraformCloudVariablesMap.get(key);
|
||||
|
||||
if (!existingVariable) {
|
||||
await createVariable(secretSync, secretMap, key);
|
||||
} else {
|
||||
await updateVariable(secretSync, secretMap, existingVariable);
|
||||
}
|
||||
}
|
||||
|
||||
if (secretSync.syncOptions.disableSecretDeletion) return;
|
||||
|
||||
for (const terraformCloudVariable of terraformCloudVariables) {
|
||||
if (!Object.prototype.hasOwnProperty.call(secretMap, terraformCloudVariable.key)) {
|
||||
await deleteVariable(secretSync, terraformCloudVariable);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getSecrets: async (secretSync: TTerraformCloudSyncWithCredentials): Promise<TSecretMap> => {
|
||||
throw new Error(`${SECRET_SYNC_NAME_MAP[secretSync.destination]} does not support importing secrets.`);
|
||||
},
|
||||
|
||||
removeSecrets: async (secretSync: TTerraformCloudSyncWithCredentials, secretMap: TSecretMap): Promise<void> => {
|
||||
const terraformCloudVariables = await getTerraformCloudVariables(secretSync);
|
||||
|
||||
for (const variable of terraformCloudVariables) {
|
||||
if (Object.prototype.hasOwnProperty.call(secretMap, variable.key)) {
|
||||
await deleteVariable(secretSync, variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { SecretSyncs } from "@app/lib/api-docs";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
import {
|
||||
BaseSecretSyncSchema,
|
||||
GenericCreateSecretSyncFieldsSchema,
|
||||
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";
|
||||
|
||||
const TerraformCloudSyncDestinationConfigSchema = z.discriminatedUnion("scope", [
|
||||
z.object({
|
||||
scope: z.literal(TerraformCloudSyncScope.Project).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
|
||||
.string()
|
||||
.min(1, "Project ID is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.project)
|
||||
}),
|
||||
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
|
||||
.string()
|
||||
.min(1, "Workspace ID is required")
|
||||
.describe(SecretSyncs.DESTINATION_CONFIG.TERRAFORM_CLOUD.workspace)
|
||||
})
|
||||
]);
|
||||
|
||||
const TerraformCloudSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: false };
|
||||
|
||||
export const TerraformCloudSyncSchema = BaseSecretSyncSchema(
|
||||
SecretSync.TerraformCloud,
|
||||
TerraformCloudSyncOptionsConfig
|
||||
).extend({
|
||||
destination: z.literal(SecretSync.TerraformCloud),
|
||||
destinationConfig: TerraformCloudSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const CreateTerraformCloudSyncSchema = GenericCreateSecretSyncFieldsSchema(
|
||||
SecretSync.TerraformCloud,
|
||||
TerraformCloudSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: TerraformCloudSyncDestinationConfigSchema
|
||||
});
|
||||
|
||||
export const UpdateTerraformCloudSyncSchema = GenericUpdateSecretSyncFieldsSchema(
|
||||
SecretSync.TerraformCloud,
|
||||
TerraformCloudSyncOptionsConfig
|
||||
).extend({
|
||||
destinationConfig: TerraformCloudSyncDestinationConfigSchema.optional()
|
||||
});
|
||||
|
||||
export const TerraformCloudSyncListItemSchema = z.object({
|
||||
name: z.literal("Terraform Cloud"),
|
||||
connection: z.literal(AppConnection.TerraformCloud),
|
||||
destination: z.literal(SecretSync.TerraformCloud),
|
||||
canImportSecrets: z.literal(false)
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
import z from "zod";
|
||||
|
||||
import { TTerraformCloudConnection } from "@app/services/app-connection/terraform-cloud";
|
||||
|
||||
import {
|
||||
CreateTerraformCloudSyncSchema,
|
||||
TerraformCloudSyncListItemSchema,
|
||||
TerraformCloudSyncSchema
|
||||
} from "./terraform-cloud-sync-schemas";
|
||||
|
||||
export type TTerraformCloudSyncListItem = z.infer<typeof TerraformCloudSyncListItemSchema>;
|
||||
|
||||
export type TTerraformCloudSync = z.infer<typeof TerraformCloudSyncSchema>;
|
||||
|
||||
export type TTerraformCloudSyncInput = z.infer<typeof CreateTerraformCloudSyncSchema>;
|
||||
|
||||
export type TTerraformCloudSyncWithCredentials = TTerraformCloudSync & {
|
||||
connection: TTerraformCloudConnection;
|
||||
};
|
||||
|
||||
export type TerraformCloudSecret = {
|
||||
description: string;
|
||||
is_secret: boolean;
|
||||
key: string;
|
||||
source: "project" | "workspace";
|
||||
value: string;
|
||||
};
|
||||
|
||||
export type TerraformCloudApiVariable = {
|
||||
id: string;
|
||||
type: string;
|
||||
attributes: {
|
||||
key: string;
|
||||
value: string | null;
|
||||
sensitive: boolean;
|
||||
category: "terraform" | "env";
|
||||
hcl: boolean;
|
||||
description: string | null;
|
||||
};
|
||||
relationships: {
|
||||
workspace?: {
|
||||
data: {
|
||||
id: string;
|
||||
type: string;
|
||||
};
|
||||
};
|
||||
project?: {
|
||||
data: {
|
||||
id: string;
|
||||
type: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type TerraformCloudVariable = {
|
||||
id: string;
|
||||
key: string;
|
||||
value: string;
|
||||
sensitive: boolean;
|
||||
description: string;
|
||||
category: "terraform" | "env";
|
||||
source: "project" | "workspace";
|
||||
};
|
||||
|
||||
export type TerraformCloudApiResponse<T> = {
|
||||
data: T;
|
||||
included?: unknown[];
|
||||
links?: {
|
||||
self?: string;
|
||||
first?: string;
|
||||
prev?: string;
|
||||
next?: string;
|
||||
last?: string;
|
||||
};
|
||||
meta?: {
|
||||
pagination?: {
|
||||
current_page: number;
|
||||
prev_page: number | null;
|
||||
next_page: number | null;
|
||||
total_pages: number;
|
||||
total_count: number;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export enum TerraformCloudSyncScope {
|
||||
Project = "project",
|
||||
Workspace = "workspace"
|
||||
}
|
||||
Reference in New Issue
Block a user