Skip to content

Commit

Permalink
DXCDT-498: Add terraform generate command skeleton (#792)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiught authored Aug 8, 2023
1 parent 35c1650 commit 198c776
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 0 deletions.
13 changes: 13 additions & 0 deletions docs/auth0_terraform.md
Original file line number Diff line number Diff line change
@@ -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

47 changes: 47 additions & 0 deletions docs/auth0_terraform_generate.md
Original file line number Diff line number Diff line change
@@ -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


1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
113 changes: 113 additions & 0 deletions internal/cli/terraform.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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. If not provided, the files will be " +
"saved in the current working directory.",
},
}

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"}, // 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\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),
}

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
}
85 changes: 85 additions & 0 deletions internal/cli/terraform_test.go
Original file line number Diff line number Diff line change
@@ -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("./terraform")

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 empty", func(t *testing.T) {
testInputs := terraformInputs{
OutputDIR: "",
}

err := generateTerraformConfigFiles(&testInputs)
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) {
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))
}

0 comments on commit 198c776

Please sign in to comment.