Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add apply/destroy actions for all stages for the foundation deploy helper #986

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions 0-bootstrap/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ locals {
org_admins_org_iam_permissions = var.org_policy_admin_role == true ? [
"roles/orgpolicy.policyAdmin", "roles/resourcemanager.organizationAdmin", "roles/billing.user"
] : ["roles/resourcemanager.organizationAdmin", "roles/billing.user"]
group_org_admins = var.groups.create_groups ? var.groups.required_groups.group_org_admins : var.group_org_admins
group_billing_admins = var.groups.create_groups ? var.groups.required_groups.group_billing_admins : var.group_billing_admins
group_org_admins = var.groups.create_groups ? module.required_group["group_org_admins"].id : var.group_org_admins
group_billing_admins = var.groups.create_groups ? module.required_group["group_billing_admins"].id : var.group_billing_admins
}

resource "google_folder" "bootstrap" {
Expand Down
55 changes: 50 additions & 5 deletions helpers/foundation-deployer/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# [WIP] Terraform Example Foundation deploy helper
# Terraform Example Foundation deploy helper

Helper tool to deploy the Terraform example foundation.

## Usage

### Validate required tools

- Check if required tools, Go 1.18+, Terraform 1.3.0+, gcloud 393.0.0+, and Git 2.28.0+, are installed:

```bash
Expand All @@ -16,6 +18,16 @@ Helper tool to deploy the Terraform example foundation.
git --version
```

- check if required components of `gcloud` are installed:

```bash
gcloud components list --filter="id=beta OR id=terraform-tools"
```

- Follow the instructions in the output of the command if components `beta` and `terraform-tools` are not installed to install them.

### Prepare the deploy environment

- Create a directory in the file system to host the Cloud Source repositories the will be created and a copy of the terraform example foundation.
- Clone the `terraform-example-foundation` repository on this directory.

Expand All @@ -33,7 +45,10 @@ Helper tool to deploy the Terraform example foundation.
```

- Update `global.tfvars` with values from your environment.
See the READMEs for the stages for additional information:
- The `0-bootstrap` README [prerequisites](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/0-bootstrap/README.md#prerequisites) section has additional prerequisites needed to run this helper.
- Variable `code_checkout_path` is the full path to `deploy-directory` directory.
- Variable `foundation_code_path` is the full path to `terraform-example-foundation` directory.
- See the READMEs for the stages for additional information:
- [0-bootstrap](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/0-bootstrap/README.md)
- [1-org](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/1-org/README.md)
- [2-environments](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/2-environments/README.md)
Expand All @@ -42,15 +57,45 @@ See the READMEs for the stages for additional information:
- [4-projects](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/4-projects)
- [5-app-infra](https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/5-app-infra)

- Variable `code_checkout_path` is the full path to `deploy-directory` directory.
- Variable `foundation_code_path` is the full path to `terraform-example-foundation` directory.
### Location

By default the foundation regional resources are deployed in `us-west1` and `us-central1` regions and multi-regional resources are deployed in the `US` multi-region.

Im addition to the variables declared in the file `global.tfvars` for configuring location, there are two locals, `default_region1` and `default_region2`, in each one of the environments (`production`, `non-production`, and `development`) in the network steps (`3-networks-dual-svpc` and `3-networks-hub-and-spoke`) . They are located in the [main.tf](../../3-networks-dual-svpc/envs/production/main.tf#L20-L21) files for each environments.

**Note:** the region used for the variable `default_region` in the file `global.tfvars` **MUST** be one of the regions used for the `default_region1` and `default_region2` locals.

### Application default credentials

- Set the billing quota project in the `gcloud` configuration

```
gcloud config set billing/quota_project <QUOTA-PROJECT>

gcloud services enable \
"cloudresourcemanager.googleapis.com" \
"iamcredentials.googleapis.com" \
"cloudbuild.googleapis.com" \
"securitycenter.googleapis.com" \
"accesscontextmanager.googleapis.com" \
--project <QUOTA-PROJECT>
```

- Configure [Application Default Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login)

```bash
gcloud auth application-default login
```

### Run the helper

- Install the helper:

```bash
go install
```

- Validate the tfvars file:
- Validate the tfvars file. If you configured a `validator_project_id` in the `global.tfvars` file `validate` will do additional checks for the Secure Command Center notification name and for the Tag Key name. For these extra check you need at least the roles *Security Center Notification Configurations Viewer* (`roles/securitycenter.notificationConfigViewer`) and *Tag Viewer* (`roles/resourcemanager.tagViewer`):

```bash
$HOME/go/bin/foundation-deployer -tfvars_file <PATH TO 'global.tfvars' FILE> -validate
Expand Down
33 changes: 33 additions & 0 deletions helpers/foundation-deployer/global.tfvars.example
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ default_region = "us-central1"

group_org_admins = "REPLACE_ME" # "[email protected]"
group_billing_admins = "REPLACE_ME" # "[email protected]"
org_project_creators = []

bucket_force_destroy = false
project_prefix = "prj"
Expand All @@ -50,6 +51,36 @@ folder_prefix = "fldr"

//parent_folder = "01234567890"

// Optional - for enabling the automatic groups creation, uncomment the groups
// variable and update the values with the desired group names
// https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/0-bootstrap/README.md#optional---automatic-creation-of-google-cloud-identity-groups

// After deploy, the Bootstrap service account will need to be granted "Group Admin" role in the
// Google Workspace by a Super Admin before Cloud Build builds can be executed by the Bootstrap workspace.
// https://github.com/terraform-google-modules/terraform-google-group/blob/main/README.md#google-workspace-formerly-known-as-g-suite-roles

//initial_group_config = "WITH_INITIAL_OWNER"
//groups = {
// create_groups = true,
// billing_project = "BILLING-PROJECT",
// required_groups = {
// group_org_admins = "[email protected]"
// group_billing_admins = "[email protected]"
// billing_data_users = "[email protected]"
// audit_data_users = "[email protected]"
// monitoring_workspace_users = "[email protected]"
// },
// optional_groups = { # fill in only the groups to be created
// gcp_platform_viewer = ""
// gcp_security_reviewer = ""
// gcp_network_viewer = ""
// gcp_scc_admin = ""
// gcp_global_secrets_admin = ""
// gcp_audit_viewer = ""
// }
//}
//


// 1-org inputs
// https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/1-org/envs/shared/README.md#inputs
Expand All @@ -63,6 +94,8 @@ essential_contacts_domains_to_allow = ["@example.com"]
scc_notification_name = "scc-notify"
audit_logs_table_delete_contents_on_destroy = false
log_export_storage_force_destroy = false
log_export_storage_location = "US"
billing_export_dataset_location = "US"

// Choose witch network architecture to use:
// Dual Shared VPC: https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/3-networks-dual-svpc/README.md
Expand Down
2 changes: 1 addition & 1 deletion helpers/foundation-deployer/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test v0.5.0
github.com/gruntwork-io/terratest v0.41.12
github.com/hashicorp/hcl/v2 v2.16.1
github.com/mitchellh/go-testing-interface v1.14.2-0.20210217184823-a52172cd2f64
github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770
github.com/stretchr/testify v1.8.2
github.com/terraform-google-modules/terraform-example-foundation/test/integration v0.0.0-20230503230051-e9e2618ef515
github.com/tidwall/gjson v1.14.4
Expand Down
6 changes: 2 additions & 4 deletions helpers/foundation-deployer/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-testing-interface v1.14.2-0.20210217184823-a52172cd2f64 h1:+9bM6qWXndPx7+czi9+Jj6zHPioFpfdhwVGOYOgujMY=
github.com/mitchellh/go-testing-interface v1.14.2-0.20210217184823-a52172cd2f64/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 h1:drhDO54gdT/a15GBcMRmunZiNcLgPiFIJa23KzmcvcU=
github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770/go.mod h1:SO/iHr6q2EzbqRApt+8/E9wqebTwQn5y+UlB04bxzo0=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
Expand Down Expand Up @@ -447,8 +447,6 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/terraform-google-modules/terraform-example-foundation/test/integration v0.0.0-20230329165204-e5db58ea752a h1:TYICMneChDJfEEOOmQzYUFwIh4GvoRKXjiJD9fii2EM=
github.com/terraform-google-modules/terraform-example-foundation/test/integration v0.0.0-20230329165204-e5db58ea752a/go.mod h1:4EkFeYb9/Cjbqc4uynVvcmV8MCK7VK5dGuR2yD4r8EU=
github.com/terraform-google-modules/terraform-example-foundation/test/integration v0.0.0-20230503230051-e9e2618ef515 h1:9WpwfiGRUEX5e3qeC93M/TWxmVeSD8vz1B8c5FumdAE=
github.com/terraform-google-modules/terraform-example-foundation/test/integration v0.0.0-20230503230051-e9e2618ef515/go.mod h1:4EkFeYb9/Cjbqc4uynVvcmV8MCK7VK5dGuR2yD4r8EU=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
Expand Down
123 changes: 122 additions & 1 deletion helpers/foundation-deployer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ func main() {
Logger: utils.GetLogger(cfg.quiet),
}

// only enable services if they are not already enabled
// only enable services if they are not already enabled
if globalTFVars.HasValidatorProj() {
conf.ValidatorProject = *globalTFVars.ValidatorProjectId
var apis []string
Expand Down Expand Up @@ -158,6 +158,61 @@ func main() {
// destroy stages
if cfg.destroy {
// Note: destroy is only terraform destroy, local directories are not deleted.
// 5-app-infra
msg.PrintStageMsg("Destroying 5-app-infra stage")
err = s.RunDestroyStep("bu1-example-app", func() error {
io := stages.GetInfraPipelineOutputs(t, conf.CheckoutPath, "bu1-example-app")
return stages.DestroyExampleAppStage(t, s, io, conf)
})
if err != nil {
fmt.Printf("# Example app step destroy failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 4-projects
msg.PrintStageMsg("Destroying 4-projects stage")
err = s.RunDestroyStep("gcp-projects", func() error {
bo := stages.GetBootstrapStepOutputs(t, conf.FoundationPath)
return stages.DestroyProjectsStage(t, s, bo, conf)
})
if err != nil {
fmt.Printf("# Projects step destroy failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 3-networks
msg.PrintStageMsg("Destroying 3-networks stage")
err = s.RunDestroyStep("gcp-networks", func() error {
bo := stages.GetBootstrapStepOutputs(t, conf.FoundationPath)
return stages.DestroyNetworksStage(t, s, bo, conf)
})
if err != nil {
fmt.Printf("# Networks step destroy failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 2-environments
msg.PrintStageMsg("Destroying 2-environments stage")
err = s.RunDestroyStep("gcp-environments", func() error {
bo := stages.GetBootstrapStepOutputs(t, conf.FoundationPath)
return stages.DestroyEnvStage(t, s, bo, conf)
})
if err != nil {
fmt.Printf("# Environments step destroy failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 1-org
msg.PrintStageMsg("Destroying 1-org stage")
err = s.RunDestroyStep("gcp-org", func() error {
bo := stages.GetBootstrapStepOutputs(t, conf.FoundationPath)
return stages.DestroyOrgStage(t, s, bo, conf)
})
if err != nil {
fmt.Printf("# Org step destroy failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 0-bootstrap
msg.PrintStageMsg("Destroying 0-bootstrap stage")
err = s.RunDestroyStep("gcp-bootstrap", func() error {
Expand All @@ -167,6 +222,13 @@ func main() {
fmt.Printf("# Bootstrap step destroy failed. Error: %s\n", err.Error())
os.Exit(3)
}

// clean up the steps file
err = steps.DeleteStepsFile(cfg.stepsFile)
if err != nil {
fmt.Printf("# failed to delete state file %s. Error: %s\n", cfg.stepsFile, err.Error())
os.Exit(3)
}
return
}

Expand All @@ -189,5 +251,64 @@ func main() {
msg.PrintBuildMsg(bo.CICDProject, bo.DefaultRegion, conf.DisablePrompt)
}
msg.PrintQuotaMsg(bo.ProjectsSA, conf.DisablePrompt)
if globalTFVars.HasGroupsCreation() {
msg.PrintAdminGroupPermissionMsg(bo.BootstrapSA, conf.DisablePrompt)
}

// 1-org
msg.PrintStageMsg("Deploying 1-org stage")
err = s.RunStep("gcp-org", func() error {
return stages.DeployOrgStage(t, s, globalTFVars, bo, conf)
})
if err != nil {
fmt.Printf("# Org step failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 2-environments
msg.PrintStageMsg("Deploying 2-environments stage")
err = s.RunStep("gcp-environments", func() error {
return stages.DeployEnvStage(t, s, globalTFVars, bo, conf)
})
if err != nil {
fmt.Printf("# Environments step failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 3-networks
msg.PrintStageMsg("Deploying 3-networks stage")
err = s.RunStep("gcp-networks", func() error {
return stages.DeployNetworksStage(t, s, globalTFVars, bo, conf)
})
if err != nil {
fmt.Printf("# Networks step failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 4-projects
msg.PrintStageMsg("Deploying 4-projects stage")
msg.ConfirmQuota(bo.ProjectsSA, conf.DisablePrompt)

err = s.RunStep("gcp-projects", func() error {
return stages.DeployProjectsStage(t, s, globalTFVars, bo, conf)
})
if err != nil {
fmt.Printf("# Projects step failed. Error: %s\n", err.Error())
os.Exit(3)
}

// 5-app-infra
msg.PrintStageMsg("Deploying 5-app-infra stage")
io := stages.GetInfraPipelineOutputs(t, conf.CheckoutPath, "bu1-example-app")
io.RemoteStateBucket = bo.RemoteStateBucketProjects

msg.PrintBuildMsg(io.InfraPipeProj, io.DefaultRegion, conf.DisablePrompt)

err = s.RunStep("bu1-example-app", func() error {
return stages.DeployExampleAppStage(t, s, globalTFVars, io, conf)
})
if err != nil {
fmt.Printf("# Example app step failed. Error: %s\n", err.Error())
os.Exit(3)
}
}
20 changes: 18 additions & 2 deletions helpers/foundation-deployer/msg/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
buildErrorURL = "https://console.cloud.google.com/cloud-build/builds;region=%s/%s?project=%s"
quotaURL = "https://support.google.com/code/contact/billing_quota_increase"
troubleQuotaURL = "https://github.com/terraform-google-modules/terraform-example-foundation/blob/master/docs/TROUBLESHOOTING.md#billing-quota-exceeded"
groupAdminURL = "https://cloud.google.com/identity/docs/how-to/setup#assigning_an_admin_role_to_the_service_account"
)

var (
Expand Down Expand Up @@ -67,10 +68,10 @@ func PrintStageMsg(msg string) {

func PrintBuildMsg(project, region string, disablePrompt bool) {
fmt.Println("")
fmt.Println("# Check build results in the Google console:")
fmt.Println("# Follow build execution and check build results in the Google console:")
fmt.Printf("# %s\n", CloudBuildURL(project, region))
if !disablePrompt {
PressEnter("")
PressEnter("# Press Enter to continue at any time")
fmt.Println("")
}
}
Expand All @@ -90,6 +91,21 @@ func PrintQuotaMsg(sa string, disablePrompt bool) {
}
}

func PrintAdminGroupPermissionMsg(sa string, disablePrompt bool) {
fmt.Println("")
fmt.Println("# Request a Super Admin to Grant 'Group Admin' role in the")
fmt.Println("# Admin Console of the Google Workspace to the Bootstrap service account:")
fmt.Printf("# %s \n", sa)
fmt.Println("")
fmt.Printf("# See: %s\n", groupAdminURL)
fmt.Println("# for additional information")
fmt.Println("")
if !disablePrompt {
PressEnter("")
fmt.Println("")
}
}

func ConfirmQuota(sa string, disablePrompt bool) {
fmt.Println("")
fmt.Println("# Proceed if you received confirmation of billing quota increase for the service account of stage 4-projects")
Expand Down
Loading