// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package command import ( "errors" "fmt" "github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/modsdir" "github.com/hashicorp/terraform/internal/moduleref" "github.com/hashicorp/terraform/internal/terraform" "github.com/hashicorp/terraform/internal/tfdiags" ) // ModulesCommand is a Command implementation that prints out information // about the modules declared by the current configuration. type ModulesCommand struct { Meta viewType arguments.ViewType } func (c *ModulesCommand) Help() string { return modulesCommandHelp } func (c *ModulesCommand) Synopsis() string { return "Show all declared modules in a working directory" } func (c *ModulesCommand) Run(rawArgs []string) int { // Parse global view arguments rawArgs = c.Meta.process(rawArgs) common, rawArgs := arguments.ParseView(rawArgs) c.View.Configure(common) // Parse command specific flags args, diags := arguments.ParseModules(rawArgs) if diags.HasErrors() { c.View.Diagnostics(diags) c.View.HelpPrompt("modules") return 1 } c.viewType = args.ViewType // Set up the command's view view := views.NewModules(c.viewType, c.View) rootModPath, err := ModulePath([]string{}) if err != nil { diags = diags.Append(err) view.Diagnostics(diags) return 1 } // Read the root module path so we can then traverse the tree rootModEarly, earlyConfDiags := c.loadSingleModule(rootModPath) if rootModEarly == nil { diags = diags.Append(errors.New("root module not found. Please run terraform init"), earlyConfDiags) view.Diagnostics(diags) return 1 } config, confDiags := c.loadConfig(rootModPath) // Here we check if there are any uninstalled dependencies versionDiags := terraform.CheckCoreVersionRequirements(config) if versionDiags.HasErrors() { view.Diagnostics(versionDiags) return 1 } diags = diags.Append(earlyConfDiags) if earlyConfDiags.HasErrors() { view.Diagnostics(diags) return 1 } diags = diags.Append(confDiags) if confDiags.HasErrors() { view.Diagnostics(diags) return 1 } // Fetch the module manifest internalManifest, diags := c.internalManifest() if diags.HasErrors() { view.Diagnostics(diags) return 1 } // Create a module reference resolver resolver := moduleref.NewResolver(internalManifest) // Crawl the Terraform config and find entries with references manifestWithRef := resolver.Resolve(config) // Render the new manifest with references return view.Display(*manifestWithRef) } // internalManifest will use the configuration loader to refresh and load the // internal manifest. func (c *ModulesCommand) internalManifest() (modsdir.Manifest, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics loader, err := c.initConfigLoader() if err != nil { diags = diags.Append(fmt.Errorf("Failed to initialize config loader: %w", err)) return nil, diags } if err = loader.RefreshModules(); err != nil { diags = diags.Append(fmt.Errorf("Failed to refresh module manifest: %w", err)) return nil, diags } return loader.ModuleManifest(), diags } const modulesCommandHelp = ` Usage: terraform [global options] modules [options] Prints out a list of all declared Terraform modules and their resolved versions in a Terraform working directory. Options: -json If specified, output declared Terraform modules and their resolved versions in a machine-readable format. `