From 907605236bca8e35644c99e19edb19bbbeb3809c Mon Sep 17 00:00:00 2001 From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com> Date: Mon, 7 Aug 2023 03:05:50 +0200 Subject: [PATCH 1/5] Add terraform generate command skeleton --- internal/cli/root.go | 1 + internal/cli/terraform.go | 112 +++++++++++++++++++++++++++++++++ internal/cli/terraform_test.go | 85 +++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 internal/cli/terraform.go create mode 100644 internal/cli/terraform_test.go diff --git a/internal/cli/root.go b/internal/cli/root.go index 19f7f7f0a..e4d39d2ff 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -168,6 +168,7 @@ func addSubCommands(rootCmd *cobra.Command, cli *cli) { rootCmd.AddCommand(testCmd(cli)) rootCmd.AddCommand(logsCmd(cli)) rootCmd.AddCommand(apiCmd(cli)) + rootCmd.AddCommand(terraformCmd(cli)) // keep completion at the bottom: rootCmd.AddCommand(completionCmd(cli)) diff --git a/internal/cli/terraform.go b/internal/cli/terraform.go new file mode 100644 index 000000000..0e6bd05a9 --- /dev/null +++ b/internal/cli/terraform.go @@ -0,0 +1,112 @@ +package cli + +import ( + "os" + "path" + + "github.com/spf13/cobra" +) + +var tfFlags = terraformFlags{ + OutputDIR: Flag{ + Name: "Output Dir", + LongForm: "output-dir", + ShortForm: "o", + Help: "Output directory for the generated Terraform config files.", + }, +} + +type ( + terraformFlags struct { + OutputDIR Flag + } + + terraformInputs struct { + OutputDIR string + } +) + +func terraformCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "terraform", + Aliases: []string{"tf"}, + Short: "Manage terraform configuration for your Auth0 Tenant", + Long: "This command facilitates the integration of Auth0 with [Terraform](https://www.terraform.io/), an " + + "infrastructure as code tool.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(generateTerraformCmd(cli)) + + return cmd +} + +func generateTerraformCmd(cli *cli) *cobra.Command { + var inputs terraformInputs + + cmd := &cobra.Command{ + Use: "generate", + Aliases: []string{"gen", "export"}, + Short: "Generate terraform configuration for your Auth0 Tenant", + Long: "This command is designed to streamline the process of generating Terraform configuration files for " + + "your Auth0 resources, serving as a bridge between the two.\n\n It automatically scans your Auth0 Tenant " + + "and compiles a set of Terraform configuration files based on the existing resources and configurations." + + "\n\nThe generated Terraform files are written in HashiCorp Configuration Language (HCL).", + RunE: generateTerraformCmdRun(cli, &inputs), + } + + tfFlags.OutputDIR.RegisterString(cmd, &inputs.OutputDIR, "./") + + return cmd +} + +func generateTerraformCmdRun(cli *cli, inputs *terraformInputs) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if err := generateTerraformConfigFiles(inputs); err != nil { + return err + } + + cli.renderer.Infof("Terraform config files generated successfully.") + cli.renderer.Infof( + "Follow this " + + "[quickstart](https://registry.terraform.io/providers/auth0/auth0/latest/docs/guides/quickstart) " + + "to go through setting up an Auth0 application for the provider to authenticate against and manage " + + "resources.", + ) + + return nil + } +} + +func generateTerraformConfigFiles(inputs *terraformInputs) error { + const readWritePermission = 0755 + if err := os.MkdirAll(inputs.OutputDIR, readWritePermission); err != nil { + if !os.IsExist(err) { + return err + } + } + + mainTerraformConfigFile, err := os.Create(path.Join(inputs.OutputDIR, "main.tf")) + if err != nil { + return err + } + defer mainTerraformConfigFile.Close() + + mainTerraformConfigFileContent := `terraform { + required_version = "~> 1.5.0" + required_providers { + auth0 = { + source = "auth0/auth0" + version = "1.0.0-beta.1" + } + } +} + +provider "auth0" { + debug = true +} +` + + _, err = mainTerraformConfigFile.WriteString(mainTerraformConfigFileContent) + return err +} diff --git a/internal/cli/terraform_test.go b/internal/cli/terraform_test.go new file mode 100644 index 000000000..c171a6949 --- /dev/null +++ b/internal/cli/terraform_test.go @@ -0,0 +1,85 @@ +package cli + +import ( + "os" + "path" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGenerateTerraformConfigFiles(t *testing.T) { + testInputs := terraformInputs{ + OutputDIR: "./terraform/dev", + } + defer os.RemoveAll(testInputs.OutputDIR) + + t.Run("it can correctly generate the terraform main config file", func(t *testing.T) { + assertTerraformConfigFilesWereGeneratedWithCorrectContent(t, &testInputs) + }) + + t.Run("it can correctly generate the terraform main config file even if the dir exists", func(t *testing.T) { + err := os.MkdirAll(testInputs.OutputDIR, 0755) + require.NoError(t, err) + + assertTerraformConfigFilesWereGeneratedWithCorrectContent(t, &testInputs) + }) + + t.Run("it fails to create the directory if path is a read only location", func(t *testing.T) { + testInputs := terraformInputs{ + OutputDIR: "/terraform/dev", + } + + err := generateTerraformConfigFiles(&testInputs) + assert.EqualError(t, err, "mkdir /terraform: read-only file system") + }) + + t.Run("it fails to create the main.tf file if file is already created and read only", func(t *testing.T) { + err := os.MkdirAll(testInputs.OutputDIR, 0755) + require.NoError(t, err) + + mainFilePath := path.Join(testInputs.OutputDIR, "main.tf") + _, err = os.Create(mainFilePath) + require.NoError(t, err) + + err = os.Chmod(mainFilePath, 0444) + require.NoError(t, err) + + err = generateTerraformConfigFiles(&testInputs) + assert.EqualError(t, err, "open terraform/dev/main.tf: permission denied") + }) +} + +func assertTerraformConfigFilesWereGeneratedWithCorrectContent(t *testing.T, testInputs *terraformInputs) { + err := generateTerraformConfigFiles(testInputs) + require.NoError(t, err) + + // Assert that the directory was created. + _, err = os.Stat(testInputs.OutputDIR) + assert.NoError(t, err) + + // Assert that the main.tf file was created with the correct content. + mainTerraformConfigFilePath := path.Join(testInputs.OutputDIR, "main.tf") + _, err = os.Stat(mainTerraformConfigFilePath) + assert.NoError(t, err) + + expectedContent := `terraform { + required_version = "~> 1.5.0" + required_providers { + auth0 = { + source = "auth0/auth0" + version = "1.0.0-beta.1" + } + } +} + +provider "auth0" { + debug = true +} +` + // Read the file content and check if it matches the expected content + content, err := os.ReadFile(mainTerraformConfigFilePath) + assert.NoError(t, err) + assert.Equal(t, expectedContent, string(content)) +} From f6322f78c2bc4411769c6b11bf97a505b3b2d511 Mon Sep 17 00:00:00 2001 From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com> Date: Mon, 7 Aug 2023 11:22:14 +0200 Subject: [PATCH 2/5] Generate docs --- docs/auth0_terraform.md | 13 +++++++++ docs/auth0_terraform_generate.md | 47 ++++++++++++++++++++++++++++++++ docs/index.md | 1 + internal/cli/terraform.go | 3 +- 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 docs/auth0_terraform.md create mode 100644 docs/auth0_terraform_generate.md diff --git a/docs/auth0_terraform.md b/docs/auth0_terraform.md new file mode 100644 index 000000000..bb79100fb --- /dev/null +++ b/docs/auth0_terraform.md @@ -0,0 +1,13 @@ +--- +layout: default +has_toc: false +has_children: true +--- +# auth0 terraform + +This command facilitates the integration of Auth0 with [Terraform](https://www.terraform.io/), an infrastructure as code tool. + +## Commands + +- [auth0 terraform generate](auth0_terraform_generate.md) - Generate terraform configuration for your Auth0 Tenant + diff --git a/docs/auth0_terraform_generate.md b/docs/auth0_terraform_generate.md new file mode 100644 index 000000000..79fb6fd85 --- /dev/null +++ b/docs/auth0_terraform_generate.md @@ -0,0 +1,47 @@ +--- +layout: default +parent: auth0 terraform +has_toc: false +--- +# auth0 terraform generate + +This command is designed to streamline the process of generating Terraform configuration files for your Auth0 resources, serving as a bridge between the two. + + It automatically scans your Auth0 Tenant and compiles a set of Terraform configuration files based on the existing resources and configurations. + +The generated Terraform files are written in HashiCorp Configuration Language (HCL). + +## Usage +``` +auth0 terraform generate [flags] +``` + +## Examples + +``` + +``` + + +## Flags + +``` + -o, --output-dir string Output directory for the generated Terraform config files. If not provided, the files will be saved in the current working directory. (default "./") +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 terraform generate](auth0_terraform_generate.md) - Generate terraform configuration for your Auth0 Tenant + + diff --git a/docs/index.md b/docs/index.md index 13cff24d5..df3f736d3 100644 --- a/docs/index.md +++ b/docs/index.md @@ -80,6 +80,7 @@ Authenticating as a user is not supported for **private cloud** tenants. Instead - [auth0 roles](auth0_roles.md) - Manage resources for roles - [auth0 rules](auth0_rules.md) - Manage resources for rules - [auth0 tenants](auth0_tenants.md) - Manage configured tenants +- [auth0 terraform](auth0_terraform.md) - Manage terraform configuration for your Auth0 Tenant - [auth0 test](auth0_test.md) - Try your Universal Login box or get a token - [auth0 universal-login](auth0_universal-login.md) - Manage the Universal Login experience - [auth0 users](auth0_users.md) - Manage resources for users diff --git a/internal/cli/terraform.go b/internal/cli/terraform.go index 0e6bd05a9..7c5d744fa 100644 --- a/internal/cli/terraform.go +++ b/internal/cli/terraform.go @@ -12,7 +12,8 @@ var tfFlags = terraformFlags{ Name: "Output Dir", LongForm: "output-dir", ShortForm: "o", - Help: "Output directory for the generated Terraform config files.", + Help: "Output directory for the generated Terraform config files. If not provided, the files will be " + + "saved in the current working directory.", }, } From d8808962149de2295f05d6d97c0705325e3cc931 Mon Sep 17 00:00:00 2001 From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:00:05 +0200 Subject: [PATCH 3/5] Adjust tests for terraform config files generation --- internal/cli/terraform_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/cli/terraform_test.go b/internal/cli/terraform_test.go index c171a6949..4f16420b5 100644 --- a/internal/cli/terraform_test.go +++ b/internal/cli/terraform_test.go @@ -13,7 +13,7 @@ func TestGenerateTerraformConfigFiles(t *testing.T) { testInputs := terraformInputs{ OutputDIR: "./terraform/dev", } - defer os.RemoveAll(testInputs.OutputDIR) + defer os.RemoveAll("./terraform") t.Run("it can correctly generate the terraform main config file", func(t *testing.T) { assertTerraformConfigFilesWereGeneratedWithCorrectContent(t, &testInputs) @@ -26,13 +26,13 @@ func TestGenerateTerraformConfigFiles(t *testing.T) { assertTerraformConfigFilesWereGeneratedWithCorrectContent(t, &testInputs) }) - t.Run("it fails to create the directory if path is a read only location", func(t *testing.T) { + t.Run("it fails to create the directory if path is empty", func(t *testing.T) { testInputs := terraformInputs{ - OutputDIR: "/terraform/dev", + OutputDIR: "", } err := generateTerraformConfigFiles(&testInputs) - assert.EqualError(t, err, "mkdir /terraform: read-only file system") + assert.EqualError(t, err, "mkdir : no such file or directory") }) t.Run("it fails to create the main.tf file if file is already created and read only", func(t *testing.T) { From 6cd31e8259baf4b803dea2dc1a7cdb576d4bc9ca Mon Sep 17 00:00:00 2001 From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:32:01 +0200 Subject: [PATCH 4/5] Fix typos --- internal/cli/terraform.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/cli/terraform.go b/internal/cli/terraform.go index 7c5d744fa..d11d50b65 100644 --- a/internal/cli/terraform.go +++ b/internal/cli/terraform.go @@ -33,7 +33,7 @@ func terraformCmd(cli *cli) *cobra.Command { Aliases: []string{"tf"}, Short: "Manage terraform configuration for your Auth0 Tenant", Long: "This command facilitates the integration of Auth0 with [Terraform](https://www.terraform.io/), an " + - "infrastructure as code tool.", + "Infrastructure as Code tool.", } cmd.SetUsageTemplate(resourceUsageTemplate()) @@ -47,10 +47,10 @@ func generateTerraformCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "generate", - Aliases: []string{"gen", "export"}, + Aliases: []string{"gen", "export"}, // Reconsider aliases and command name before releasing. Short: "Generate terraform configuration for your Auth0 Tenant", Long: "This command is designed to streamline the process of generating Terraform configuration files for " + - "your Auth0 resources, serving as a bridge between the two.\n\n It automatically scans your Auth0 Tenant " + + "your Auth0 resources, serving as a bridge between the two.\n\nIt automatically scans your Auth0 Tenant " + "and compiles a set of Terraform configuration files based on the existing resources and configurations." + "\n\nThe generated Terraform files are written in HashiCorp Configuration Language (HCL).", RunE: generateTerraformCmdRun(cli, &inputs), From 810de085b4955d036634b877731c1db2254e2100 Mon Sep 17 00:00:00 2001 From: Sergiu Ghitea <28300158+sergiught@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:32:35 +0200 Subject: [PATCH 5/5] Update docs --- docs/auth0_terraform.md | 2 +- docs/auth0_terraform_generate.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/auth0_terraform.md b/docs/auth0_terraform.md index bb79100fb..0d8043ffb 100644 --- a/docs/auth0_terraform.md +++ b/docs/auth0_terraform.md @@ -5,7 +5,7 @@ has_children: true --- # auth0 terraform -This command facilitates the integration of Auth0 with [Terraform](https://www.terraform.io/), an infrastructure as code tool. +This command facilitates the integration of Auth0 with [Terraform](https://www.terraform.io/), an Infrastructure as Code tool. ## Commands diff --git a/docs/auth0_terraform_generate.md b/docs/auth0_terraform_generate.md index 79fb6fd85..548c18609 100644 --- a/docs/auth0_terraform_generate.md +++ b/docs/auth0_terraform_generate.md @@ -7,7 +7,7 @@ has_toc: false This command is designed to streamline the process of generating Terraform configuration files for your Auth0 resources, serving as a bridge between the two. - It automatically scans your Auth0 Tenant and compiles a set of Terraform configuration files based on the existing resources and configurations. +It automatically scans your Auth0 Tenant and compiles a set of Terraform configuration files based on the existing resources and configurations. The generated Terraform files are written in HashiCorp Configuration Language (HCL).