mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 00:27:30 +00:00
feat(azure-app-integration): label & reference support
This commit is contained in:
@@ -1126,6 +1126,8 @@ export const INTEGRATION = {
|
|||||||
shouldAutoRedeploy: "Used by Render to trigger auto deploy.",
|
shouldAutoRedeploy: "Used by Render to trigger auto deploy.",
|
||||||
secretGCPLabel: "The label for GCP secrets.",
|
secretGCPLabel: "The label for GCP secrets.",
|
||||||
secretAWSTag: "The tags for AWS secrets.",
|
secretAWSTag: "The tags for AWS secrets.",
|
||||||
|
azureUseLabels:
|
||||||
|
"If enabled, each secret will be given a label that represents which Infisical environment they belong to.",
|
||||||
githubVisibility:
|
githubVisibility:
|
||||||
"Define where the secrets from the Github Integration should be visible. Option 'selected' lets you directly define which repositories to sync secrets to.",
|
"Define where the secrets from the Github Integration should be visible. Option 'selected' lets you directly define which repositories to sync secrets to.",
|
||||||
githubVisibilityRepoIds:
|
githubVisibilityRepoIds:
|
||||||
|
|||||||
@@ -299,6 +299,11 @@ const syncSecretsAzureAppConfig = async ({
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format: {\"uri\":\"https://SOME-KEY-VAULT.vault.azure.net/secrets/SOME-SECRET-KEY\"}
|
||||||
|
// Also works without the backslash escapes
|
||||||
|
const azureSecretReferenceUriRegex =
|
||||||
|
/^\{(\\"|")uri(\\"|"):(\\"|")https:\/\/[a-zA-Z0-9-]+\.vault\.azure\.net\/secrets\/[a-zA-Z0-9-]+\\?\2\}$/;
|
||||||
|
|
||||||
const getCompleteAzureAppConfigValues = async (url: string) => {
|
const getCompleteAzureAppConfigValues = async (url: string) => {
|
||||||
let result: AzureAppConfigKeyValue[] = [];
|
let result: AzureAppConfigKeyValue[] = [];
|
||||||
while (url) {
|
while (url) {
|
||||||
@@ -405,14 +410,24 @@ const syncSecretsAzureAppConfig = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create or update secrets on Azure App Config
|
// create or update secrets on Azure App Config
|
||||||
|
|
||||||
for await (const key of Object.keys(secrets)) {
|
for await (const key of Object.keys(secrets)) {
|
||||||
if (!(key in azureAppConfigSecrets) || secrets[key]?.value !== azureAppConfigSecrets[key]) {
|
if (!(key in azureAppConfigSecrets) || secrets[key]?.value !== azureAppConfigSecrets[key]) {
|
||||||
await request.put(
|
await request.put(
|
||||||
`${integration.app}/kv/${key}?api-version=2023-11-01`,
|
`${integration.app}/kv/${key}?api-version=2023-11-01`,
|
||||||
{
|
{
|
||||||
value: secrets[key]?.value
|
value: secrets[key]?.value,
|
||||||
|
...(azureSecretReferenceUriRegex.test(secrets[key]?.value || "") && {
|
||||||
|
content_type: "application/vnd.microsoft.appconfig.keyvaultref+json;charset=utf-8"
|
||||||
|
})
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
...(metadata.azureUseLabels && {
|
||||||
|
params: {
|
||||||
|
label: integration.environment.slug
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`
|
||||||
},
|
},
|
||||||
@@ -432,6 +447,11 @@ const syncSecretsAzureAppConfig = async ({
|
|||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${accessToken}`
|
Authorization: `Bearer ${accessToken}`
|
||||||
},
|
},
|
||||||
|
...(metadata.azureUseLabels && {
|
||||||
|
params: {
|
||||||
|
label: integration.environment.slug
|
||||||
|
}
|
||||||
|
}),
|
||||||
// we force IPV4 because docker setup fails with ipv6
|
// we force IPV4 because docker setup fails with ipv6
|
||||||
httpsAgent: new https.Agent({
|
httpsAgent: new https.Agent({
|
||||||
family: 4
|
family: 4
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export const IntegrationMetadataSchema = z.object({
|
|||||||
.optional()
|
.optional()
|
||||||
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
|
.describe(INTEGRATION.CREATE.metadata.secretAWSTag),
|
||||||
|
|
||||||
|
azureUseLabels: z.boolean().optional().describe(INTEGRATION.CREATE.metadata.azureUseLabels),
|
||||||
|
|
||||||
githubVisibility: z
|
githubVisibility: z
|
||||||
.union([z.literal("selected"), z.literal("private"), z.literal("all")])
|
.union([z.literal("selected"), z.literal("private"), z.literal("all")])
|
||||||
.optional()
|
.optional()
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export const useCreateIntegration = () => {
|
|||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value: string;
|
||||||
}[];
|
}[];
|
||||||
|
azureUseLabels?: boolean;
|
||||||
githubVisibility?: string;
|
githubVisibility?: string;
|
||||||
githubVisibilityRepoIds?: string[];
|
githubVisibilityRepoIds?: string[];
|
||||||
kmsKeyId?: string;
|
kmsKeyId?: string;
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import Head from "next/head";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { faArrowUpRightFromSquare, faBookOpen } from "@fortawesome/free-solid-svg-icons";
|
import {
|
||||||
|
faArrowUpRightFromSquare,
|
||||||
|
faBookOpen,
|
||||||
|
faQuestionCircle
|
||||||
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import queryString from "query-string";
|
import queryString from "query-string";
|
||||||
@@ -21,7 +25,9 @@ import {
|
|||||||
FormControl,
|
FormControl,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
SelectItem
|
SelectItem,
|
||||||
|
Switch,
|
||||||
|
Tooltip
|
||||||
} from "../../../components/v2";
|
} from "../../../components/v2";
|
||||||
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
|
import { useGetIntegrationAuthById } from "../../../hooks/api/integrationAuth";
|
||||||
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
import { useGetWorkspaceById } from "../../../hooks/api/workspace";
|
||||||
@@ -39,7 +45,8 @@ const schema = z.object({
|
|||||||
secretPath: z.string().trim().min(1, { message: "Secret path is required" }),
|
secretPath: z.string().trim().min(1, { message: "Secret path is required" }),
|
||||||
sourceEnvironment: z.string().trim().min(1, { message: "Source environment is required" }),
|
sourceEnvironment: z.string().trim().min(1, { message: "Source environment is required" }),
|
||||||
initialSyncBehavior: z.nativeEnum(IntegrationSyncBehavior),
|
initialSyncBehavior: z.nativeEnum(IntegrationSyncBehavior),
|
||||||
secretPrefix: z.string().default("")
|
secretPrefix: z.string().default(""),
|
||||||
|
useLabels: z.boolean().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
type TFormSchema = z.infer<typeof schema>;
|
type TFormSchema = z.infer<typeof schema>;
|
||||||
@@ -60,6 +67,7 @@ export default function AzureAppConfigurationCreateIntegration() {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
|
watch,
|
||||||
setValue,
|
setValue,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
formState: { isSubmitting }
|
formState: { isSubmitting }
|
||||||
@@ -85,8 +93,11 @@ export default function AzureAppConfigurationCreateIntegration() {
|
|||||||
}
|
}
|
||||||
}, [workspace]);
|
}, [workspace]);
|
||||||
|
|
||||||
|
const sourceEnv = watch("sourceEnvironment");
|
||||||
|
|
||||||
const handleIntegrationSubmit = async ({
|
const handleIntegrationSubmit = async ({
|
||||||
secretPath,
|
secretPath,
|
||||||
|
useLabels,
|
||||||
sourceEnvironment,
|
sourceEnvironment,
|
||||||
baseUrl,
|
baseUrl,
|
||||||
initialSyncBehavior,
|
initialSyncBehavior,
|
||||||
@@ -103,7 +114,8 @@ export default function AzureAppConfigurationCreateIntegration() {
|
|||||||
secretPath,
|
secretPath,
|
||||||
metadata: {
|
metadata: {
|
||||||
initialSyncBehavior,
|
initialSyncBehavior,
|
||||||
secretPrefix
|
secretPrefix,
|
||||||
|
azureUseLabels: useLabels
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,35 +167,75 @@ export default function AzureAppConfigurationCreateIntegration() {
|
|||||||
</div>
|
</div>
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<div className="px-6">
|
<div className="px-6">
|
||||||
<Controller
|
<div className="mb-2 -space-y-2">
|
||||||
control={control}
|
<Controller
|
||||||
name="sourceEnvironment"
|
control={control}
|
||||||
render={({ field, fieldState: { error } }) => (
|
name="sourceEnvironment"
|
||||||
<FormControl
|
render={({ field, fieldState: { error } }) => (
|
||||||
label="Project Environment"
|
<FormControl
|
||||||
errorText={error?.message}
|
label="Project Environment"
|
||||||
isError={Boolean(error)}
|
errorText={error?.message}
|
||||||
>
|
isError={Boolean(error)}
|
||||||
<Select
|
|
||||||
className="w-full border border-mineshaft-500"
|
|
||||||
dropdownContainerClassName="max-w-full"
|
|
||||||
value={field.value}
|
|
||||||
onValueChange={(val) => {
|
|
||||||
field.onChange(val);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{workspace?.environments.map((sourceEnvironment) => (
|
<Select
|
||||||
<SelectItem
|
className="w-full border border-mineshaft-500"
|
||||||
value={sourceEnvironment.slug}
|
dropdownContainerClassName="max-w-full"
|
||||||
key={`source-environment-${sourceEnvironment.slug}`}
|
value={field.value}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
field.onChange(val);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{workspace?.environments.map((sourceEnvironment) => (
|
||||||
|
<SelectItem
|
||||||
|
value={sourceEnvironment.slug}
|
||||||
|
key={`source-environment-${sourceEnvironment.slug}`}
|
||||||
|
>
|
||||||
|
{sourceEnvironment.name}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="useLabels"
|
||||||
|
render={({ field: { onChange, value } }) => (
|
||||||
|
<Switch
|
||||||
|
id="use-environment-labels"
|
||||||
|
onCheckedChange={(isChecked) => onChange(isChecked)}
|
||||||
|
isChecked={value}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
Use Environment Labels
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Use the environment slug as the label on the secret keys created in
|
||||||
|
Azure App Configuration.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{sourceEnv && (
|
||||||
|
<p>
|
||||||
|
You have selected the{" "}
|
||||||
|
<span className="font-semibold">{sourceEnv}</span> environment,
|
||||||
|
therefore the label will be set to{" "}
|
||||||
|
<span className="font-semibold">{sourceEnv}</span>.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{sourceEnvironment.name}
|
<FontAwesomeIcon icon={faQuestionCircle} size="1x" />
|
||||||
</SelectItem>
|
</Tooltip>
|
||||||
))}
|
</div>
|
||||||
</Select>
|
</Switch>
|
||||||
</FormControl>
|
)}
|
||||||
)}
|
/>
|
||||||
/>
|
</div>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name="secretPath"
|
name="secretPath"
|
||||||
|
|||||||
Reference in New Issue
Block a user