This project aims to provide a reference implementation of the Terraform Provider for Microsoft 365 which is part of TerraProvider.
For the Terraform templates you will need:
- A recent build of Terraform (tested with 1.7.5)
- An EntraID Tenant and an App Registration in this tenant. See Authentication for details.
Download the latest Terraform version from here and extract the binary to a folder in your PATH.
Alternatively (on Windows) use WinGet:
winget install Hashicorp.Terraform
Feel free to use OpenTofu instead.
Read terraform.tf
and fill in the required values like tenant and Azure subscription. By default we assume to store the Terraform state in an Azure Storage Account in the same tenant.
I.e.: Configure the backend
section in terraform.tf
to use the Azure Storage Account.
See the Configuring a Service Principal for managing EntraID for more details on how to authenticate against Entra ID.
Make sure the Service Principal has write permissions to the Terraform state. You can achieve this by assigning the "Storage Blob Data Contributor" role to the Service Principal.
We recommend using Federated Credentials via OpenID Connect. This way no credentials need to be stored.
For interactive and testing use, we recommend using a client ID and secret.
You can create a PowerShell script to load the needed environment variables, e.g.:
$Env:ARM_CLIENT_ID = "{client_id}"
$Env:ARM_CLIENT_SECRET = "{client_secret}"
$Env:ARM_SUBSCRIPTION_ID = "{subscription_id}"
$Env:ARM_TENANT_ID = "{tenant_id}"
This template includes a GitHub Actions workflows to plan or plan and apply the Terraform templates.
See .github/workflows/ - The workflows are configured to run only manually by default. Uncomment the "push" trigger to run on every push to the repository.
"workflow_dispatch" allows you to run the workflow manually from the GitHub Actions UI. Comment it out to disable manual runs.
on:
# push:
# branches:
# - main
workflow_dispatch:
Be aware, by default our workflow use OpenTofu instead of TerraForm.
- Clone this repository.
- fill/replace the placeholders in
terraform.tf
with your values - Read and modify the
terraform.tfvar
to match your needs. - Load the environment variables (see Authenticating Terraform to Entra ID)
- Run
terraform init
to initialize the Terraform environment. This will also download the required Terraform providers incl. the Workplace provider. - Run
terraform apply
to create all resources.
The templates expect a "Break Glass Account" called cloudadmin@{initialDomain}.onmicrosoft.com
to be present in the Entra ID Tenant. It will be excluded from critical conditional access policies and can be used to access the tenant in case of an emergency.
The templates include Conditional Access Policies, so please make sure the the Entra ID security defaults are disabled. See here for details.
Additionally, the "Admin 1" policy template is currently disabled by default to avoid locking yourself out.
Terraform allows you to create, update and delete resources. It is intended to be used as a "single source of truth" for your infrastructure.
Terraform will only update or delete resources that it feels responsible for. This is tracked in the Terraform state.
When you use templates to create resources these are automatically added to the state. It will never delete resources that are not in the state.
You can manually import existing resources to the state. See Importing existing resources for details. By this you can "adopt" existing resources into the state and manage them with Terraform.
The TerraForm state should either be included in the repository (when no other storage exists) or stored in a separate storage account when configuring a pipeline/GitHub Action.
After running steps 1-3 from Quick Setup, you can use Terraform to manage resources.
The following commands are the most important ones:
terraform init
- Initializes the Terraform environment. This will also download the required Terraform providers incl. the Workplace provider.terraform validate
- Validates the Terraform templates. This will check for syntax errors and missing variables.terraform plan
- Shows the changes that Terraform will apply (as a "dry run"). It will not change any resources.terraform apply
- Applies the changes to the resources. This will create, update or delete resources as required.terraform destroy
- Destroys all resources that are managed by Terraform. This will not delete resources that are not in the state.
The templates are separated into modules. Each module is contained in a single folder. Terraform will look for .tf files in a folder and apply them at the same time.
We tried to separate the templates by policy/object type. Each module will handly a specific type of resource and in most cases where there are multiple resources, the definition of resources is found in a file prefixed by def.
. The other files are scaffolding and configuration.
Each module can be activated or deactivated by a variable prefixed by manage_
in terraform.tfvars
.
You can import existing AzureAD groups and policies into the Terraform state using the terraform import
command.
Assume you already have a group called "All AutoPilot Devices" and want to match it to the all_autopilot_device
resource in the groups
module.
resource "azuread_group" "all_autopilot_device" {
display_name = "All Autopilot Devices"
mail_enabled = false
security_enabled = true
types = [
"DynamicMembership"
]
...
You can import the group into the state using the following command, of course replacing <object_id>
with the actual object id of the group:
terraform import module.groups.azuread_group.all_autopilot_device <object_id>
The next time you run terraform plan
or terraform apply
Terraform will recognize the group and will not try to create it again.
Be aware - running terraform destroy
or removing the template will delete the group from AzureAD.
We include a script to create a Terraform template from an existing Entra ID or Intune object. This script will create a .tf file if given the correct type and object id. You can use this to export existing objects or understand how to write a new object.
Example to export a device configuration policy:
. ./aad2tf.ps1 "microsoft365wp_device_management_configuration_policy" "ce9b10b1-ca89-4e30-912d-595d2093f63a"
Make sure to load the environment variables (for authentication) before running the script.