Repository: chanzuckerberg/fogg
Pull request #837 (2023-04-20 21:57:01)

fix: Add AWS Account ID to fogg init interview
Fogg requires the AWS Provider's AccountID to be set in the default configuration. The `fogg init` interview does not ask for this account ID, so the `fogg.yml` file produced is immediately invalid. Users trying to create a new fogg project are greeted with an error when running any subsequent command.

This PR adds AWS Account ID to the interview for the initial `fogg.yml` file so fogg subsequently works properly. 


Patch:
diff --git a/cmd/exp_examine.go b/cmd/exp_examine.go
index c4df242f..5cbde1fc 100644
--- a/cmd/exp_examine.go
+++ b/cmd/exp_examine.go
@@ -13,7 +13,7 @@ func init() {
 	expCmd.AddCommand(examineCmd)
 }
 
-//TODO:(EC) Create a flag for path to walk
+// TODO:(EC) Create a flag for path to walk
 var examineCmd = &cobra.Command{
 	Use:   "examine",
 	Short: "Detects terraform module updates",
diff --git a/cmd/init.go b/cmd/init.go
index 95785a45..45fa46ba 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -20,6 +20,7 @@ func init() {
 	initCmd.Flags().String("table", "", "Use this to pass the infra dynamo table name via CLI.")
 	initCmd.Flags().String("profile", "", "Use this to pass the aws auth profile via CLI.")
 	initCmd.Flags().String("owner", "", "Use this to pass the owner name via CLI.")
+	initCmd.Flags().String("aws-account-id", "", "Use this to pass the primary AWS Account ID via CLI.")
 	rootCmd.AddCommand(initCmd)
 }
 
@@ -103,6 +104,15 @@ func userPrompt(cmd *cobra.Command) (*FoggProject, error) {
 	}
 	foggProject.Owner = &owner
 
+	awsAccountID, err := cmd.Flags().GetString("aws-account-id")
+	if err != nil {
+		return nil, err
+	}
+	if awsAccountID == "" {
+		awsAccountID = prompt.StringRequired("AWS Account ID?")
+	}
+	foggProject.AwsAccountID = &awsAccountID
+
 	return foggProject, nil
 }
 
diff --git a/config/config.go b/config/config.go
index d137e085..1c1148b1 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1,6 +1,7 @@
 package config
 
 import (
+	"encoding/json"
 	"io/ioutil"
 	"path/filepath"
 
@@ -23,7 +24,9 @@ func defaultEnabled(a bool) *bool {
 }
 
 // InitConfig initializes the config file using user input
-func InitConfig(project, region, bucket, table, awsProfile, owner *string, awsProviderVersion string) *v2.Config {
+func InitConfig(project, region, bucket, table, awsProfile, owner, awsAccountID *string, awsProviderVersion string) *v2.Config {
+	accountID := json.Number(*awsAccountID)
+
 	return &v2.Config{
 		Defaults: v2.Defaults{
 			Common: v2.Common{
@@ -37,8 +40,9 @@ func InitConfig(project, region, bucket, table, awsProfile, owner *string, awsPr
 				Project: project,
 				Providers: &v2.Providers{
 					AWS: &v2.AWSProvider{
-						Profile: awsProfile,
-						Region:  region,
+						AccountID: &accountID,
+						Profile:   awsProfile,
+						Region:    region,
 						CommonProvider: v2.CommonProvider{
 							Enabled: defaultEnabled(true),
 							Version: &awsProviderVersion,
diff --git a/exp/examine/examine.go b/exp/examine/examine.go
index 9bb9c8a3..4ed374f2 100644
--- a/exp/examine/examine.go
+++ b/exp/examine/examine.go
@@ -4,8 +4,8 @@ import (
 	"github.com/spf13/afero"
 )
 
-//Examine loads local modules and compares them to their latest version to see differences
-//TODO: Comparison between local and latest
+// Examine loads local modules and compares them to their latest version to see differences
+// TODO: Comparison between local and latest
 func Examine(fs afero.Fs, path string) error {
 	//Collect local modules to be updated
 	module, err := GetLocalModules(fs, path)
diff --git a/exp/examine/local.go b/exp/examine/local.go
index cbbf9b1a..83847a84 100644
--- a/exp/examine/local.go
+++ b/exp/examine/local.go
@@ -10,8 +10,8 @@ import (
 
 //**Local refers to any files located within your local file system**
 
-//GetLocalModules retrieves all terraform modules within a given directory
-//TODO:(EC) Define local and global modules OR rename the values
+// GetLocalModules retrieves all terraform modules within a given directory
+// TODO:(EC) Define local and global modules OR rename the values
 func GetLocalModules(fs afero.Fs, dir string) (*tfconfig.Module, error) {
 	_, err := os.Stat(dir)
 	if err != nil {
diff --git a/exp/examine/util.go b/exp/examine/util.go
index 4442ba2e..5ed4763e 100644
--- a/exp/examine/util.go
+++ b/exp/examine/util.go
@@ -9,7 +9,7 @@ import (
 	"github.com/spf13/afero"
 )
 
-//TODO:(EC) Add a RegistryModule field
+// TODO:(EC) Add a RegistryModule field
 type ModuleWrapper struct {
 	moduleSource string
 	version      string
@@ -62,7 +62,7 @@ type Submodule struct {
 const githubURL = "github.com"
 const tagPattern = "ref="
 
-//GetFromGithub Retrieves modules that are available through github
+// GetFromGithub Retrieves modules that are available through github
 func GetFromGithub(fs afero.Fs, repo string) (*tfconfig.Module, error) {
 	//FIXME: (EC) Create temporary directory, when tests fail directory stays
 	//TODO: Make directory name more general
diff --git a/init/init.go b/init/init.go
index d38ccf2c..d82c15be 100644
--- a/init/init.go
+++ b/init/init.go
@@ -8,7 +8,7 @@ import (
 const AWSProviderVersion = "4.34.0"
 
 type FoggProject struct {
-	Project, Region, Bucket, Table, Profile, Owner *string
+	Project, Region, Bucket, Table, Profile, Owner, AwsAccountID *string
 }
 
 // Init reads user console input and generates a fogg.yml file
@@ -20,6 +20,7 @@ func Init(fs afero.Fs, foggProject *FoggProject) error {
 		foggProject.Table,
 		foggProject.Profile,
 		foggProject.Owner,
+		foggProject.AwsAccountID,
 		AWSProviderVersion,
 	)
 	e := config.Write(fs, "fogg.yml")
diff --git a/migrations/migrations.go b/migrations/migrations.go
index 76c1bd2d..24420e36 100644
--- a/migrations/migrations.go
+++ b/migrations/migrations.go
@@ -5,7 +5,7 @@ import (
 	"github.com/spf13/afero"
 )
 
-//Migration Defines a fogg migration and the actions that it can perform
+// Migration Defines a fogg migration and the actions that it can perform
 type Migration interface {
 	Description() string                      //Describes the migration taking
 	Guard(afero.Fs, string) (bool, error)     //Returns true if migration is runnable, otherwise error
@@ -13,7 +13,7 @@ type Migration interface {
 	Prompt() bool                             //Returns whether the user would like to run the migration
 }
 
-//RunMigrations cycles through a list of migrations and applies them if necessary
+// RunMigrations cycles through a list of migrations and applies them if necessary
 func RunMigrations(fs afero.Fs, configFile string, forceApply bool) error {
 	migrations := []Migration{}
 


Test patch:
diff --git a/config/config_test.go b/config/config_test.go
index 67780ce4..e9c2fe19 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -14,7 +14,7 @@ func getPtr(val string) *string {
 
 func TestInitConfig(t *testing.T) {
 	r := require.New(t)
-	c := InitConfig(getPtr("proj"), getPtr("reg"), getPtr("buck"), getPtr("table"), getPtr("prof"), getPtr("me@foo.example"), "0.99.0")
+	c := InitConfig(getPtr("proj"), getPtr("reg"), getPtr("buck"), getPtr("table"), getPtr("prof"), getPtr("me@foo.example"), getPtr("123456789"), "0.99.0")
 	r.Equal("prof", *c.Defaults.Common.Backend.Profile)
 	r.Equal("prof", *c.Defaults.Providers.AWS.Profile)
 	r.Equal("reg", *c.Defaults.Providers.AWS.Region)
diff --git a/exp/examine/latest.go b/exp/examine/latest.go
index fa1cf2bd..ef79fe5d 100644
--- a/exp/examine/latest.go
+++ b/exp/examine/latest.go
@@ -10,7 +10,7 @@ import (
 	"github.com/spf13/afero"
 )
 
-//LatestModuleVersions retrieves the latest version of the provided modules
+// LatestModuleVersions retrieves the latest version of the provided modules
 func LatestModuleVersions(fs afero.Fs, module *tfconfig.Module) ([]ModuleWrapper, error) {
 	var latestModules []ModuleWrapper
 	var moduleWrapper ModuleWrapper
@@ -38,7 +38,7 @@ func LatestModuleVersions(fs afero.Fs, module *tfconfig.Module) ([]ModuleWrapper
 	return latestModules, nil
 }
 
-//createGitURL retrieves the latest release version and creates an HTTP accessible link
+// createGitURL retrieves the latest release version and creates an HTTP accessible link
 func createGitURL(moduleCall *tfconfig.ModuleCall) (string, error) {
 	splitString := strings.Split(moduleCall.Source, "/")
 	owner, repo := splitString[1], splitString[2]
diff --git a/exp/examine/local_test.go b/exp/examine/local_test.go
index 8f2bff35..c6e7829c 100644
--- a/exp/examine/local_test.go
+++ b/exp/examine/local_test.go
@@ -8,7 +8,7 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-//TODO: Move fs to versioning.go
+// TODO: Move fs to versioning.go
 func TestGetLocalModules(t *testing.T) {
 	r := require.New(t)
 	pwd, err := os.Getwd()
diff --git a/init/init_test.go b/init/init_test.go
index fa0699af..dc73df0b 100644
--- a/init/init_test.go
+++ b/init/init_test.go
@@ -17,10 +17,12 @@ func TestInit(t *testing.T) {
 	table := "acme"
 	profile := "acme-auth"
 	owner := "infra@acme.example"
+	awsAccountID := "123456789"
+
 	fs, _, err := util.TestFs()
 	r.NoError(err)
 
-	conf := config.InitConfig(&project, &region, &bucket, &table, &profile, &owner, AWSProviderVersion)
+	conf := config.InitConfig(&project, &region, &bucket, &table, &profile, &owner, &awsAccountID, AWSProviderVersion)
 	r.NotNil(conf)
 	r.Equal(config.DefaultFoggVersion, conf.Version)