diff --git a/README.md b/README.md index 93dbc0b..60ba1d3 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,15 @@ go mod tidy go build ``` +Or you can grab one of the executables under `Releases` + ## Usage -There are two main commands: +There are three main commands: - update: To create/update variables in Terraform Cloud - delete: To delete variables in Terraform Cloud +- copy: To copy variables from one workspace to another workspace irrespective of the organization in Terraform Cloud By default, the tool assumes that the variable will be environment variable. It will not marked as sensitive or as HCL value. @@ -137,6 +140,31 @@ tfc-helper delete --var some_variable tfc-helper delete -a ` +**8. Copy all variables from workspace test1 to workspace test2 but ignore the variables that have the same name in test2. Both workspaces are in the same organization:** + +The organization have already been set with environment variable TF_CLOUD_ORG_NAME +` +tfc-help copy --src-ws test1 --dst-ws test2 +` + +OR + +` +tfc-help copy --src-ws test1 --src-org org1 --dst-ws test2 +` + +OR If the source workspace and source organization have already been set with environment variables, but the destination organization is different. We can do: + +` +tfc-helper copy --dst-ws test2 --dst-org org2 +` + +**9. Copy all variables from workspace test1 to workspace test2 but overwrite the variables that have the same name in test2. Other variables from test1 will still be copied over to test2:** + +` +tfc-help copy --src-ws test1 --dst-ws test2 -r +` + ## TODO: - Develop test cases diff --git a/cmd/copy.go b/cmd/copy.go new file mode 100644 index 0000000..c48770e --- /dev/null +++ b/cmd/copy.go @@ -0,0 +1,120 @@ +package cmd + +import ( + "fmt" + "os" + "sync" + "tfc-helper/helper" + + "github.com/spf13/cobra" +) + +// copyCmd represents the copy command +var copyCmd = &cobra.Command{ + Use: "copy", + Short: "Command to duplicate configuration between workspaces", + Long: `Command to copy all variables existed in a workspace to another workspace. The destination workspace +can be the same organization or in a different organization + +Examples: +- Copy all variables from workspace test1 to workspace test2 but ignore the variables that have the same name in test2. Both workspaces are in the same organization +tfc-help copy --src-ws test1 --dst-ws test2 + +OR if the source workspace and source organization have already been set with environment variables. We can do: +tfc-helper copy --dst-ws test2 + +- Copy all variables from workspace test1 to workspace test2 but overwrite the variables that have the same name in test2: +tfc-help copy --src-ws test1 --dst-ws test2 -r`, + Run: func(cmd *cobra.Command, args []string) { + // Try to get value from command line first then try the environment variable + srcOrgName, _ := cmd.Flags().GetString("src-org") + if srcOrgName == "" { + // Try to get organization value from environment variable + srcOrgName = os.Getenv(OrgVar) + if srcOrgName == "" { + fmt.Println("Please set the source organization with the --src-org flag or use TF_CLOUD_ORG_NAME environment variable") + os.Exit(1) + } + } + + dstOrgName, _ := cmd.Flags().GetString("dst-org") + + // If dstOrgName is empty, we assume that the organization remains the same + if dstOrgName == "" { + dstOrgName = srcOrgName + } + + // Try to get value from command line first then try the environment variable + srcWsName, _ := cmd.Flags().GetString("src-ws") + if srcWsName == "" { + // Try to get workspace value from environment variable + srcWsName = os.Getenv(WorkspaceVar) + if srcWsName == "" { + fmt.Println("Please set the source workspace with the --src-ws flag or use TF_CLOUD_WS_NAME environment variable") + os.Exit(1) + } + } + + dstWsName, _ := cmd.Flags().GetString("dst-ws") + shouldReplace, _ := cmd.Flags().GetBool("replace") + + srcWorkspaceID := helper.GetWorkspaceID(srcOrgName, srcWsName) + dstWorkspaceID := helper.GetWorkspaceID(dstOrgName, dstWsName) + + variablesInDstWs := helper.ListAllVariables(dstWorkspaceID) + + var wg sync.WaitGroup + for _, variable := range helper.ListAllVariables(srcWorkspaceID) { + // Check if replace flag is set + if shouldReplace { + wg.Add(1) + newVariable := helper.NewVariable{ + ID: "", + Key: variable.Key, + Value: variable.Value, + Description: variable.Description, + Category: variable.Category, + HCL: variable.HCL, + Sensitive: variable.Sensitive, + } + // If a variable already exists, delete it and create a new one + if helper.CheckIfVariableExistInWs(variablesInDstWs, variable.Key) { + // Try to get the variable ID + variableToReplace, _ := helper.GetVar(dstWorkspaceID, variable.Key) + newVariable.ID = variableToReplace.ID + go helper.RecreateVariable(dstWorkspaceID, newVariable, &wg) + } else { + go helper.CreateVariable(dstWorkspaceID, newVariable, &wg) + } + } else { + // If a variable already exists -- having the same name, skip + if helper.CheckIfVariableExistInWs(variablesInDstWs, variable.Key) { + continue + } else { + wg.Add(1) + newVariable := helper.NewVariable{ + ID: "", + Key: variable.Key, + Value: variable.Value, + Description: variable.Description, + Category: variable.Category, + HCL: variable.HCL, + Sensitive: variable.Sensitive, + } + go helper.CreateVariable(dstWorkspaceID, newVariable, &wg) + } + } + } + wg.Wait() + }, +} + +func init() { + rootCmd.AddCommand(copyCmd) + copyCmd.PersistentFlags().String("src-org", "", "Specify the source organization") + copyCmd.PersistentFlags().String("dst-org", "", "Specify the destination organization") + copyCmd.PersistentFlags().String("src-ws", "", "Specify the source workspace") + copyCmd.PersistentFlags().String("dst-ws", "", "Specify the destination workspace") + _ = copyCmd.MarkPersistentFlagRequired("dst-ws") + copyCmd.PersistentFlags().BoolP("replace", "r", false, "Specify whether to overwrite the existing variables or not") +} diff --git a/cmd/update.go b/cmd/update.go index 904d947..6ebf8e3 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -37,21 +37,18 @@ tfc-help update --env -r -w ws-K33Rp -o big-corp - Create/Update only new variables found in the current workspace: tfc-help update --env -w ws-K33Rp -o big-corp`, Run: func(cmd *cobra.Command, args []string) { - // Try to get workspace value from environment variable - wsName := os.Getenv(WorkspaceVar) - if wsName != "" { - workspaceName = wsName - } else { - workspaceName, _ = cmd.Flags().GetString("workspace") + // Try to get value from command line first then try the environment variable + workspaceName, _ = cmd.Flags().GetString("workspace") + if workspaceName == "" { + // Try to get workspace value from environment variable + workspaceName = os.Getenv(WorkspaceVar) } - // Try to get organization value from environment variable - orgName := os.Getenv(OrgVar) - - if orgName != "" { - organizationName = orgName - } else { - organizationName, _ = cmd.Flags().GetString("organization") + // Try to get value from command line first then try the environment variable + organizationName, _ = cmd.Flags().GetString("organization") + if organizationName == "" { + // Try to get organization value from environment variable + organizationName = os.Getenv(OrgVar) } keyPairs, _ := cmd.Flags().GetStringSlice("var") diff --git a/helper/main.go b/helper/main.go index 6268645..5870c9f 100644 --- a/helper/main.go +++ b/helper/main.go @@ -110,6 +110,16 @@ func ListAllVariables(workspaceID string) []*tfe.Variable { return variableList.Items } +// CheckIfVariableExistInWs checks if a variable is in a workspace or not +func CheckIfVariableExistInWs(wsVariableList []*tfe.Variable, variableName string) bool { + for _, variable := range wsVariableList { + if variable.Key == variableName { + return true + } + } + return false +} + // GetVar gets the variable that matches the variable name in the list of variable func GetVar(workspaceID string, varName string) (variable *tfe.Variable, err error) { for _, variable := range ListAllVariables(workspaceID) {