diff --git a/pkg/cmd/secrets.go b/pkg/cmd/secrets.go index 5502f491..65d85b4f 100644 --- a/pkg/cmd/secrets.go +++ b/pkg/cmd/secrets.go @@ -575,7 +575,11 @@ func downloadSecrets(cmd *cobra.Command, args []string) { func substituteSecrets(cmd *cobra.Command, args []string) { localConfig := configuration.LocalConfig(cmd) - utils.RequireValue("token", localConfig.Token.Value) + useEnv := cmd.Flag("use-env").Value.String() + + if useEnv != "only" { + utils.RequireValue("token", localConfig.Token.Value) + } var outputFilePath string var err error @@ -586,24 +590,32 @@ func substituteSecrets(cmd *cobra.Command, args []string) { utils.HandleError(err, "Unable to parse output file path") } } + secretsMap := map[string]string{} - dynamicSecretsTTL := utils.GetDurationFlag(cmd, "dynamic-ttl") - response, responseErr := http.GetSecrets(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, localConfig.EnclaveConfig.Value, nil, true, dynamicSecretsTTL) - if !responseErr.IsNil() { - utils.HandleError(responseErr.Unwrap(), responseErr.Message) + if useEnv != "none" { + for k, v := range utils.ParseEnvStrings(os.Environ()) { + secretsMap[k] = v + } } - secrets, parseErr := models.ParseSecrets(response) - if parseErr != nil { - utils.HandleError(parseErr, "Unable to parse API response") - } + if useEnv != "only" { + dynamicSecretsTTL := utils.GetDurationFlag(cmd, "dynamic-ttl") + response, responseErr := http.GetSecrets(localConfig.APIHost.Value, utils.GetBool(localConfig.VerifyTLS.Value, true), localConfig.Token.Value, localConfig.EnclaveProject.Value, localConfig.EnclaveConfig.Value, nil, true, dynamicSecretsTTL) + if !responseErr.IsNil() { + utils.HandleError(responseErr.Unwrap(), responseErr.Message) + } - secretsMap := map[string]string{} - for _, secret := range secrets { - if secret.ComputedValue != nil { - // By not providing a default value when ComputedValue is nil (e.g. it's a restricted secret), we default - // to the same behavior the substituter provides if the template file contains a secret that doesn't exist. - secretsMap[secret.Name] = *secret.ComputedValue + secrets, parseErr := models.ParseSecrets(response) + if parseErr != nil { + utils.HandleError(parseErr, "Unable to parse API response") + } + + for _, secret := range secrets { + if secret.ComputedValue != nil { + // By not providing a default value when ComputedValue is nil (e.g. it's a restricted secret), we default + // to the same behavior the substituter provides if the template file contains a secret that doesn't exist. + secretsMap[secret.Name] = *secret.ComputedValue + } } } @@ -739,6 +751,7 @@ func init() { if err := secretsSubstituteCmd.RegisterFlagCompletionFunc("config", configNamesValidArgs); err != nil { utils.HandleError(err) } + secretsSubstituteCmd.Flags().String("use-env", "none", "setting for how to use environment variables passed to 'doppler secrets substitute'. 'none' (default) will not expose environment variables to templates. 'mix' will expose both templates and Doppler secrets to templates. 'only' will only expose environment variables to templates (and will not fetch Doppler secrets).") secretsSubstituteCmd.Flags().String("output", "", "path to the output file. by default the rendered text will be written to stdout.") secretsSubstituteCmd.Flags().Duration("dynamic-ttl", 0, "(BETA) dynamic secrets will expire after specified duration, (e.g. '3h', '15m')") secretsCmd.AddCommand(secretsSubstituteCmd)