Manage your Google Workspace organization
Google Workspace provides organizations with custom email and collaboration tools like Gmail, Calendar, Docs and more. Terraform provides several benefits over using the Google Workspace dashboard to manage your organization:
Safety and consistency - Defining your infrastructure as code (IaC) reduces the risk of human error. With IaC, you can version control your configuration and ensure that the proper reviewers approve changes before merging them. IaC allows you to track and revert changes as your organization grows. Once you introduce these processes, you can manage users and permissions more efficiently in code. Declaratively managing large organizations and teams through infrastructure as code is faster than configuring permissions, users, and groups manually in the Google Workspace dashboard.
Improved automation - Terraform can create, update, and delete tracked resources without requiring you to keep track of each resource's dependencies. You can also develop modules for your users, groups, applications, and service principals that comply with your organization's policies. For example, you can use Terraform to ensure that every new user has access to the right tools to enable their success.
In this tutorial, you will create a service account to allow Terraform to perform actions on your behalf in your Google Workspace. Then, you will create new users, a new group, add users to a group, and assign permissions in your Google Workspace with Terraform. You can use this workflow to create templates for your users, groups, and permissions.
Prerequisites
For this tutorial, you will need:
- The Terraform 1.0.4+ CLI installed locally.
- A domain. If you do not have a domain, create one when you set up your Google Workspace account.
If you already have a Google Workspace account, go directly to the Create a service account section.
Note
This tutorial requires you to enter credit card information for Google Workspace. You have a 14 day free trial subscription to Google Workspace to explore features. We are not responsible for any charges you incur.
Set up your Google Workspace
If you do not have a Google Workspace account, navigate to workspace.google.com and select "Get started" in the top right corner. Follow the prompts to set up your new Google Workspace account.
If you are using your own domain, you will need to follow your domain registrar's steps to add MX records and verification to your domain. These instructions will differ depending on your domain registry.
Purchasing a domain through Google Workspace requires an immediate charge on your credit card and you will need to pay a yearly renewal fee to continue to use the domain.
Create a service account
Terraform needs to authenticate to your Google Workspace account with a service account. After creating it, you can use the same service account for future Terraform operations in this organization. Any additional organizations you create will need their own service accounts.
A service account is a Google Cloud Platform (GCP) account with permissions to communicate to your Google Workspace domain. Service accounts authenticate automated processes, like Terraform, to the Google API.
Navigate to the Google Cloud Platform dashboard, and agree to the terms of service.
Select your domain from the account dropdown list in the top menu bar. In this tutorial, dadgarcorp.org
is the example domain.
In the GCP dashboard, open a Google Cloud Shell by clicking on the icon in the upper-right corner.
Authorize the Google Cloud Shell if prompted.
In your Google Cloud Shell session, execute the example script and follow the prompts to create a new GCP project, enable the necessary APIs, create a service account, assign permissions to the service account, and generate a service account key. This script adds the necessary scopes (permissions) to your service account to complete this tutorial. Click below to find the full list of scopes granted to your service account to give your administrative user the ability to manage users, groups, and permissions for your organization.
- https://apps-apis.google.com/a/feeds/emailsettings/2.0/
- https://sites.google.com/feeds
- https://www.googleapis.com/auth/admin.directory.customer
- https://www.googleapis.com/auth/admin.directory.customer.readonly
- https://www.googleapis.com/auth/admin.directory.domain
- https://www.googleapis.com/auth/admin.directory.group
- https://www.googleapis.com/auth/admin.directory.group.member
- https://www.googleapis.com/auth/admin.directory.orgunit
- https://www.googleapis.com/auth/admin.directory.resource.calendar
- https://www.googleapis.com/auth/admin.directory.rolemanagement
- https://www.googleapis.com/auth/admin.directory.rolemanagement.readonly
- https://www.googleapis.com/auth/admin.directory.user
- https://www.googleapis.com/auth/admin.directory.userschema
- https://www.googleapis.com/auth/apps.groups.migration
- https://www.googleapis.com/auth/apps.groups.settings
- https://www.googleapis.com/auth/calendar
- https://www.googleapis.com/auth/chrome.management.policy
- https://www.googleapis.com/auth/cloud-platform
- https://www.googleapis.com/auth/contacts
- https://www.googleapis.com/auth/drive
- https://www.googleapis.com/auth/drive.appdata
- https://www.googleapis.com/auth/drive.file
- https://www.googleapis.com/auth/gmail.modify
- https://www.googleapis.com/auth/gmail.settings.basic
- https://www.googleapis.com/auth/gmail.settings.sharing
- https://www.googleapis.com/auth/migrate.deployment.interop
- https://www.googleapis.com/auth/tasks
- https://www.googleapis.com/auth/userinfo.email
For a full overview of Google API scopes, visit the Google Identity documentation
$ python3 <(curl -s -S -L https://raw.githubusercontent.com/hashicorp/learn-terraform-google-workspace/main/gw-service-account.py)Welcome! This script will create and authorize the resources that are necessary to useTerraform Google Workspace. The following steps will be performed on your behalf:1. Create a Google Cloud Platform project2. Enable APIs3. Create a service account4. Authorize the service account5. Create a service account keyIn the end, you will be prompted to download the service account key. This key can then be used for TFWS.If you would like to perform these steps manually, then you can follow the instructions at <https://support.google.com/workspacemigrate/answer/10839762>.Press Enter to continue or 'n' to exit:Creating project...tfws-1628101640943 successfully created ✅Verifying acceptance of Terms of service...Terms of service acceptance verified ✅Enabling APIs...APIs successfully enabled ✅Creating service account...tfws-service-account successfully created ✅Before using Terraform Google Workspace, you must authorize the service account to perform actions on behalf of your users. You can do so by clicking:https://admin.google.com/ac/owl/domainwidedelegation…
Click on the link in the output to authorize domain-wide delegation on your account. Select "Authorize" then return to the Google Cloud Provider terminal. Press "Enter" to continue with the script.
After clicking 'Authorize', return here and press Enter to continue.Creating service account key...Service account key successfully created ✅Verifying service account authorization…Service account successfully authorized ✅Verifying API access…API access verified ✅Done! ✅If you have already downloaded the file, then you may close this page. Please remember that this file is highly sensitive. Any person who gains access to the key file will then have full access to all resources to which the service account has access. You should treat it just like you would a password.Next, follow the instructions to create the OAuth web client ID for project tfws-1628101640943. You can create this by going to <https://console.cloud.google.com/apis/credentials/consent?project=tfws-1628101640943>. The instructions can be found here: <https://support.google.com/workspacemigrate/answer/9222992>.
This script generates a JSON key to authorize your service account. Download this key and make a note of the download location. Terraform will use this key to authenticate to Google Workspace.
Configure your credentials
In the previous section, you downloaded your Google Workspace service account keys to your local machine. Your browser automatically downloads the credentials file to a default directory like "Downloads" or your desktop. To use these credentials, create a new environment variable with the full path to your key file.
$ export GOOGLEWORKSPACE_CREDENTIALS=/users/<NAME>/Downloads/tfws-service-account-key-2021-08-04.json
Note
It is recommended practice to move your credentials file to a more secure location. In a production environment, be sure to move this file out of the Downloads folder and re-run the export command with the new location.
In your local terminal, create your credential environment variables. Terraform will use these credentials to authenticate with Google Workspace and create your resources.
Create an environment variable with the customer ID from your account information settings in the Google Workspace dashboard.
$ export GOOGLEWORKSPACE_CUSTOMER_ID=
Create another environment variable for the impersonated user's email. Use the administrator account you created and signed into Google Workspace with.
$ export GOOGLEWORKSPACE_IMPERSONATED_USER_EMAIL=
Clone the example repository
In your terminal, clone the example repository, which contains Terraform configuration to define Google Workspace users based on a CSV file.
$ git clone https://github.com/hashicorp-education/learn-terraform-google-workspace
Change into the repository directory.
$ cd learn-terraform-google-workspace
Review the user CSV File
Open users.csv
. This CSV file contains a list of user information that Terraform will use to create and manage Google Workspace users.
users.csv
first_name,last_name,org_unit_path,status,recovery_email,dept,titleJim,Halpert,jhalpert@dadgarcorp.org,5f4dcc3b5aa765d61d8327deb882cf99,/,Active,jhalpert@gmail.com,sales,agentMichael,Scott,mscott@dadgarcorp.org,5f4dcc3b5aa765d61d8327deb882cf99,/Active,mscott@hotmail.com,sales,managerPam,Beesly,pbeesly@dadgarcorp.org,5f4dcc3b5aa765d61d8327deb882cf99,/,Active,pbeesly@gmail.com,admin,receptionist
The first line is the header. Each field in the header is a key for the corresponding value at the same position in the remaining rows. Each remaining row represents a user and must contain the same number of fields as the header. The header keys allow you to reference a specific user attribute in your Terraform configuration.
Review the configuration
Open versions.tf
. This file contains a terraform {}
block that defines Terraform settings, including the required providers Terraform will use to provision your infrastructure. The source
attribute defines an optional hostname, a namespace, and the provider type for each provider. Terraform installs providers from the Terraform Registry by default. This example configuration defines the googleworkspace
provider's source as hashicorp/googleworkspace
, which is shorthand for registry.terraform.io/hashicorp/googleworkspace
.
You can also set a version constraint for each provider defined in the required_providers
block. The version attribute is optional, but we recommend using it so that Terraform does not install a provider version that does not work with your configuration. If you do not specify a provider version, Terraform will automatically download the most recent version during initialization.
versions.tf
terraform { required_version = "~> 1.0.0" required_providers { googleworkspace = { source = "hashicorp/googleworkspace" version = "0.4.0" } }}
The Terraform configuration in users.tf
uses the data from the users.csv
file to create your Google Workspace users. Open users.tf
to review the configuration. Terraform configuration consists of blocks of code written in HashiCorp Configuration Language (HCL). Review each block below to learn
what this Terraform configuration defines.
Provider block
To use the Google Workspace provider, you must define a provider block for it in your configuration.
users.tf
provider "googleworkspace" {}
This block is empty because the provider uses the credentials in your environment variable. While you can set your credentials in the provider
block, it is safer to use the GOOGLEWORKSPACE_CREDENTIALS
environment variable to avoid committing sensitive values into source control. You can also configure other optional, provider-specific settings in this block.
Local values
A locals
block allows you to define values that you reference throughout your configuration. Locals capture common values to make your configuration easier to read and less repetitive.
users.tf
locals { users = csvdecode(file("${path.module}/users.csv"))}
The users
local value parses the users.csv
file using the csvdecode
and file
functions. The csvdecode
function converts the file contents into a list of maps, allowing Terraform to reference the values in the CSV file in the configuration.
Resources
Resource blocks are the primary way you interact with the provider to manage its components. Google Workspace resources include users, groups, and roles.
Resource blocks have two strings in the first line of the block: the resource type and the resource name, which together form a resource ID. This googleworkspace_user
resource block is named users
, so the resource ID is googleworkspace_user.users
.
users.tf
resource "googleworkspace_user" "users" { for_each = { for user in local.users : user.first_name => user }##...
The for_each
meta-argument tells Terraform that this block defines multiple users, each one mapped to a user in the local.users
value. By storing information about your users in a CSV file and using the for_each
meta-argument iterate over the parsed contents, you avoid creating unique resource blocks for each user, which can become difficult to manage as your organization scales.
Each resource has required or optional arguments that you can use to configure the resource to your specifications. Refer to the resource's documentation in Terraform Registry to find a complete list of arguments.
The googleworkspace_user
resource requires the primary email address, password, password hash function, and the user's first and last name. Based on your organization's needs, you can set other optional arguments, like department or keywords, by adding them to the CSV file and setting the attribute on the resource.
The primary_email
attribute uses the format()
function to generate an email based on the user's first and name and the Google Workspace domain name (var.domain
).
The password
attribute uses the format()
function to create a standardized temporary password. The md5()
function computes the MD5 hash of the temporary password and encodes it with hexadecimal digits based on HashiCorp and Google Workspace recommended practices. In this example, Jim Halpert's password would be halpertj3!
and Terraform stores the password in state as 792894557a0930e0e904c90c5fc928f2
.
When the user logs in, the change_password_at_next_login = true
attribute forces them to update the password.
Tip
This block references a specific user's information by using each.value.attribute_name
. The attribute_name
maps to the header row in the CSV file. For example, each.value.first_name
maps to the user's first name.
users.tf
##... primary_email = format( "%s%s@%s", substr(lower(each.value.first_name), 0, 1), lower(each.value.last_name), var.domain) password = md5(format( "%s%s%s!", lower(each.value.last_name), substr(lower(each.value.first_name), 0, 1), length(each.value.first_name) )) hash_function = "MD5" change_password_at_next_login = true name { family_name = each.value.last_name given_name = each.value.first_name } organizations { department = each.value.dept primary = true title = each.value.title type = "work" } recovery_email = each.value.recovery_email}
Input variables
Input variables make your Terraform configuration more flexible. Your configuration defines a domain
variable.
Open variables.tf
to review the domain
variable.
variables.tf
variable "domain" { description = "The domain address of your Google Workspace account"}
Outputs
Outputs are return values from your Terraform configuration.
Open the outputs.tf
file. This file contains the output
block named user_email
that returns a list of the emails of the created users.
users.tf
output "user_email" { value = [for value in googleworkspace_user.users: value.primary_email]}
Define variables
Open terraform.tfvars
in your file editor and change your-domain-name.com
to the domain address you configured for your Google Workspace. Terraform automatically parses the terraform.tfvars
file and applies the variable definitions to your configuration.
terraform.tfvars
domain = "your-domain-name.com"
Create Google Workspace users
Initialize the Terraform configuration. Initializing a configuration directory downloads and installs the providers defined in the configuration, which in this case is the googleworkspace
provider.
$ terraform initInitializing the backend...Initializing provider plugins...- Finding hashicorp/googleworkspace versions matching "0.4.0"...- Installing hashicorp/googleworkspace v0.4.0...- Installed hashicorp/googleworkspace v0.4.0 (signed by HashiCorp)Terraform has created a lock file .terraform.lock.hcl to record the providerselections it made above. Include this file in your version control repositoryso that Terraform can guarantee to make the same selections by default whenyou run "terraform init" in the future.Terraform has been successfully initialized!You may now begin working with Terraform. Try running "terraform plan" to seeany changes that are required for your infrastructure. All Terraform commandsshould now work.If you ever set or change modules or backend configuration for Terraform,rerun this command to reinitialize your working directory. If you forget, othercommands will detect it and remind you to do so if necessary.
Apply your configuration. Enter yes
when prompted to confirm your changes. This will create users in your organization and assign a password, email address, and organizational information to each as defined in the users.csv
file. After Terraform creates a user, they can log-in and begin using Google Workspace in your organization.
$ terraform applyTerraform used the selected providers to generate the following execution plan.Resource actions are indicated with the following symbols: + createTerraform will perform the following actions: # googleworkspace_user.users["Jim"] will be created + resource "googleworkspace_user" "users" {##...Plan: 3 to add, 0 to change, 0 to destroy.Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yesgoogleworkspace_user.users["Pam"]: Creating...googleworkspace_user.users["Michael"]: Creating...googleworkspace_user.users["Jim"]: Creating...##...Apply complete! Resources: 3 added, 0 changed, 0 destroyed.Outputs:user_email = [ "jhalpert@dadgarcorp.org", "mscott@dadgarcorp.org", "pbeesly@dadgarcorp.org",]
Navigate to the Google Workspace user list to verify Terraform successfully created these users.
Add roles and privileges to your user
The users you created require administrative access to the "Billing" service.
Create a new file called privileges.tf
and paste the following configuration into it. Organizing your Terraform files by resource type helps with configuration readability. Terraform parses every file in your directory when you run an apply and creates a single state file to keep track of your resources.
Tip
Resource separation aids in readability and organization, but resources in the configurations retain dependencies on one another. Changes to one file may result in changes to another resource not represented in that resource group configuration.
privileges.tf
data "googleworkspace_privileges" "privileges" {}locals { billing_admin_privileges = [ for priv in data.googleworkspace_privileges.privileges.items : priv if length(regexall("BILLING", priv.privilege_name)) > 0 ]}resource "googleworkspace_role" "billing-admin" { name = "billing-admin" dynamic "privileges" { for_each = local.billing_admin_privileges content { service_id = privileges.value["service_id"] privilege_name = privileges.value["privilege_name"] } }}resource "googleworkspace_role_assignment" "billing-admin" { for_each = {for u in googleworkspace_user.users: u.id => u if element(u.organizations[*].title, 0) == "manager"} role_id = googleworkspace_role.billing-admin.id assigned_to = each.key}
This configuration does the following:
The
googleworkspace_priviliges
data block returns all possible privileges in your Google Workspace organization. Thelocals
block creates a list calledbilling_admin_privileges
and filters the privileges returned from the data source for a privilege name matching "BILLING". This privilege gives the user full access to the billing resource in your organization.The
googleworkspace_role
resource creates a new role. Thedynamic
privileges block iterates through each item returned from thelocal.billing_admin_privileges
to find theservice_id
and theprivilege_name
and grant that privilege to the role.The
googleworkspace_role_assignment
resource assigns thebilling-admin
role to any user managed by this configuration that has the "manager" title. It references thegoogleworkspace_role
using the resource's role ID.
Update your configuration with these new resources, which will grant privileges to your "manager" user, Michael Scott. Enter yes
when prompted to confirm your changes.
$ terraform applyTerraform used the selected providers to generate the following execution plan. Resource actionsare indicated with the following symbols: + createTerraform will perform the following actions: # googleworkspace_role.billing-admin will be created + resource "googleworkspace_role" "billing-admin" {##... } # googleworkspace_role_assignment.billing-admin will be created + resource "googleworkspace_role_assignment" "billing-admin" {##.. }Plan: 2 to add, 0 to change, 0 to destroy.Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yesTerraform will perform the following actions: # googleworkspace_role.billing-admin will be createdPlan: 2 to add, 0 to change, 0 to destroy.googleworkspace_role.billing-admin: Creating...googleworkspace_role.billing-admin: Still creating... [10s elapsed]googleworkspace_role.billing-admin: Creation complete after 16s [id=52361352102019088]googleworkspace_role_assignment.billing-admin["106428202843259506927"]: Creating...googleworkspace_role_assignment.billing-admin["106428202843259506927"]: Still creating... [10s elapsed]googleworkspace_role_assignment.billing-admin["106428202843259506927"]: Creation complete after 11s [id=52361352102019089]Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
To confirm that your user has the correct permissions, visit the users page in your Google Workspace admin console and choose the "Michael Scott" user. Scroll to the "Role and Groups" section to confirm the user has the "billing-admin" role assigned.
Create a Google Group
Create a new file called groups.tf
and add the following new resources to that file. Save your configuration.
groups.tf
resource "googleworkspace_group" "sales" { email = "sales@${var.domain}" name = "Sales" description = "Sales Group" aliases = ["paper-sales@${var.domain}", "sales-dept@${var.domain}"]}resource "googleworkspace_group_member" "member" { for_each = { for u in googleworkspace_user.users : u.primary_email => u if element(u.organizations[*].department, 0) == "sales" } group_id = googleworkspace_group.sales.id email = each.value.primary_email role = "MEMBER"}
This configuration does the following:
The
googleworkspace_group
resource creates a new group named "Sales." This resource creates a group email address with additional aliases.The
googleworkspace_group_member
resource assigns all the users in thesales
department to the new "Sales" group. The userdepartment
inusers.csv
determines the department assignment for each user in yourusers.tf
file.
Apply your configuration. Enter yes
when prompted to confirm your changes.
$ terraform apply##...Terraform will perform the following actions: # googleworkspace_group.sales will be created + resource "googleworkspace_group" "sales" { + admin_created = (known after apply) + aliases = [ + "paper-sales@dadgarcorp.org", + "sales-dept@dadgarcorp.org", ] + description = "Sales Group" + direct_members_count = (known after apply) + email = "sales@dadgarcorp.org" + etag = (known after apply) + id = (known after apply) + name = "Sales" + non_editable_aliases = (known after apply) } # googleworkspace_group_member.member["jhalpert@dadgarcorp.org"] will be created + resource "googleworkspace_group_member" "member" { + delivery_settings = "ALL_MAIL" + email = "jhalpert@dadgarcorp.org" + etag = (known after apply) + group_id = (known after apply) + id = (known after apply) + member_id = (known after apply) + role = "MEMBER" + status = (known after apply) + type = "USER" } # googleworkspace_group_member.member["mscott@dadgarcorp.org"] will be created + resource "googleworkspace_group_member" "member" { + delivery_settings = "ALL_MAIL" + email = "mscott@dadgarcorp.org" + etag = (known after apply) + group_id = (known after apply) + id = (known after apply) + member_id = (known after apply) + role = "MEMBER" + status = (known after apply) + type = "USER" }Plan: 3 to add, 0 to change, 0 to destroy.Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes##...Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Navigate to Jim Halpert's information in the Google Workspace admin console to verify Terraform assigned the group correctly.
Clean up resources
Use Terraform to destroy the users and groups you created in this tutorial. Confirm your changes by typing yes
when prompted.
$ terraform destroy##...googleworkspace_group.sales: Refreshing state... [id=02pta16n4jpr1rc]googleworkspace_user.users["Pam"]: Refreshing state... [id=108238685260077265860]googleworkspace_user.users["Jim"]: Refreshing state... [id=100836806027738418222]Terraform used the selected providers to generate the following execution plan.Resource actions are indicated with the following symbols: - destroyTerraform will perform the following actions: # googleworkspace_group_member.member["jhalpert@dadgarcorp.org"] will be destroyed##...Do you really want to destroy all resources? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes##...googleworkspace_user.users["Pam"]: Destroying... [id=108238685260077265860]googleworkspace_user.users["Jim"]: Destruction complete after 5sgoogleworkspace_group.sales: Destruction complete after 5sgoogleworkspace_user.users["Michael"]: Destruction complete after 5sgoogleworkspace_user.users["Pam"]: Destruction complete after 5sgoogleworkspace_role.billing-admin: Destruction complete after 6sDestroy complete! Resources: 8 destroyed.
Note
After your 14 day Google Workspace trial expires, you will be charged monthly for the subscription. If you do not wish to use this Google Workspace account, remember to stop your subscription.
Next Steps
Congratulations! You created a Google Workspace organization and used Terraform to manage its resources. Over the course of this tutorial, you created new users in your Google Workspace account with data populated from a CSV file. Then, you created permissions and dynamically assigned users permissions based on their title. Finally, you created a new group and assigned users to that group based on their department within the organization. In the process, you used a wide variety of Terraform's configuration language elements and functions like resource blocks, locals, csvdecode
, format
, md5
, and learned about the resources the Google Workspace provider can manage.
For more information on topics covered in this tutorial, review the documentation below:
- Learn more about input variables and dynamic expressions, which you used in this tutorial.
- Complete the Reuse Configuration with Modules collection to learn how to create reusable modules to enable repeatable workflows.
- Visit the Google Workspace Provider documentation to learn more about the Google Workspace resources and data sources you can manage using Terraform.