This guide walks you through deploying your first Microsoft 365 resources using Terraform. You’ll create a user account and a security group, then assign the user to that group.
Prerequisites
Before you begin, ensure you have:
Terraform >= 1.14.x installed (download here )
A Microsoft 365 tenant
An Entra ID application registration with the following Microsoft Graph API permissions :
User.ReadWrite.All - Create and manage users
Group.ReadWrite.All - Create and manage groups
GroupMember.ReadWrite.All - Manage group memberships
Your tenant ID, client ID, and client secret
If you haven’t created an Entra ID application yet, see the Installation guide for detailed instructions.
Create a new directory for your Terraform project and add the following files:
Create a file named main.tf:
terraform {
required_version = ">= 1.14.0"
required_providers {
microsoft365 = {
source = "deploymenttheory/microsoft365"
version = "~> 0.40.0"
}
}
}
provider "microsoft365" {
cloud = "public"
tenant_id = var . tenant_id
auth_method = "client_secret"
entra_id_options = {
client_id = var.client_id
client_secret = var.client_secret
}
# Optional: Disable telemetry
telemetry_optout = true
}
Define variables
Create a file named variables.tf:
variable "tenant_id" {
description = "Microsoft 365 tenant ID"
type = string
sensitive = true
}
variable "client_id" {
description = "Entra ID application client ID"
type = string
sensitive = true
}
variable "client_secret" {
description = "Entra ID application client secret"
type = string
sensitive = true
}
Set your credentials
Create a file named terraform.tfvars (this file should never be committed to version control):
tenant_id = "00000000-0000-0000-0000-000000000000"
client_id = "11111111-1111-1111-1111-111111111111"
client_secret = "your-client-secret-value-here"
Add terraform.tfvars to your .gitignore file to prevent accidentally committing credentials to version control.
Step 2: Create your first resources
Add the following to your main.tf file to create a security group and a user:
# Create a security group
resource "microsoft365_graph_beta_groups_group" "engineering" {
display_name = "Engineering Team"
mail_nickname = "engineering-team"
description = "Security group for engineering team members"
security_enabled = true
mail_enabled = false
group_types = []
}
# Create a user
resource "microsoft365_graph_beta_users_user" "john_doe" {
display_name = "John Doe"
user_principal_name = "[email protected] "
mail_nickname = "johndoe"
account_enabled = true
password_profile = {
password = "TemporaryPassword123!"
force_change_password_next_sign_in = true
}
}
# Assign the user to the security group
resource "microsoft365_graph_beta_groups_group_member_assignment" "john_to_engineering" {
group_id = microsoft365_graph_beta_groups_group . engineering . id
member_id = microsoft365_graph_beta_users_user . john_doe . id
member_object_type = "User"
}
Run the following command to download the provider:
You should see output indicating that the provider has been successfully installed:
Initializing provider plugins...
- Finding deploymenttheory/microsoft365 versions matching "~> 0.40.0"...
- Installing deploymenttheory/microsoft365 v0.40.x...
- Installed deploymenttheory/microsoft365 v0.40.x
Terraform has been successfully initialized!
Step 4: Preview the changes
Run terraform plan to see what resources will be created:
You’ll see output showing the three resources that will be created:
Terraform will perform the following actions:
# microsoft365_graph_beta_groups_group.engineering will be created
+ resource "microsoft365_graph_beta_groups_group" "engineering" {
+ display_name = "Engineering Team"
+ mail_nickname = "engineering-team"
+ description = "Security group for engineering team members"
...
}
# microsoft365_graph_beta_users_user.john_doe will be created
+ resource "microsoft365_graph_beta_users_user" "john_doe" {
+ display_name = "John Doe"
+ user_principal_name = "[email protected] "
...
}
# microsoft365_graph_beta_groups_group_member_assignment.john_to_engineering will be created
+ resource "microsoft365_graph_beta_groups_group_member_assignment" "john_to_engineering" {
+ group_id = (known after apply)
+ member_id = (known after apply)
+ member_object_type = "User"
}
Plan: 3 to add, 0 to change, 0 to destroy.
Step 5: Apply the configuration
Apply your configuration to create the resources:
Type yes when prompted to confirm. Terraform will create the resources and display the results:
microsoft365_graph_beta_groups_group.engineering: Creating...
microsoft365_graph_beta_users_user.john_doe: Creating...
microsoft365_graph_beta_groups_group.engineering: Creation complete after 2s
microsoft365_graph_beta_users_user.john_doe: Creation complete after 3s
microsoft365_graph_beta_groups_group_member_assignment.john_to_engineering: Creating...
microsoft365_graph_beta_groups_group_member_assignment.john_to_engineering: Creation complete after 1s
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Step 6: Verify the resources
You can verify that the resources were created by:
Logging into the Entra ID admin center
Navigate to Identity > Users > All users to see the new user
Navigate to Identity > Groups > All groups to see the new group
Click on the group to verify the user is a member
Step 7: Clean up (optional)
To remove the resources you created:
Type yes when prompted. Terraform will delete all three resources in the correct order.
The terraform destroy command permanently deletes resources. In this quick start, we set hard_delete = false by default, which means the user will be soft-deleted and can be recovered within 30 days. To permanently delete users immediately, add hard_delete = true to the user resource.
Next steps
Now that you’ve deployed your first resources, you can:
Explore authentication methods Learn about the 11 authentication methods supported by the provider, including managed identity and OIDC for CI/CD pipelines
Create conditional access policies Secure your tenant by managing conditional access policies as code
Manage Intune policies Configure device compliance, configuration profiles, and app protection policies
Browse the registry Explore all available resources and data sources
Advanced example: Conditional access policy
Here’s a more advanced example that creates a conditional access policy requiring MFA for all users:
resource "microsoft365_graph_beta_identity_and_access_conditional_access_policy" "require_mfa" {
display_name = "Require MFA for all users"
state = "enabledForReportingButNotEnforced"
conditions = {
client_app_types = [ "all" ]
users = {
include_users = [ "All" ]
exclude_users = []
include_groups = []
exclude_groups = [
microsoft365_graph_beta_groups_group.breakglass.id
]
include_roles = []
exclude_roles = []
}
applications = {
include_applications = [ "All" ]
exclude_applications = []
}
}
grant_controls = {
operator = "OR"
built_in_controls = [ "mfa" ]
}
}
Start new conditional access policies in enabledForReportingButNotEnforced state to test them before enforcement. Change to enabled after validation.
Common patterns
Using for_each for multiple resources
Create multiple users from a list:
locals {
users = {
"john.doe" = {
display_name = "John Doe"
mail_nickname = "johndoe"
}
"jane.smith" = {
display_name = "Jane Smith"
mail_nickname = "janesmith"
}
}
}
resource "microsoft365_graph_beta_users_user" "team_members" {
for_each = local . users
display_name = each . value . display_name
user_principal_name = " ${ each . key } @yourdomain.com"
mail_nickname = each . value . mail_nickname
account_enabled = true
password_profile = {
password = "TemporaryPassword123!"
force_change_password_next_sign_in = true
}
}
Using data sources
Reference existing resources:
data "microsoft365_graph_beta_groups_group" "existing_group" {
display_name = "Existing Security Group"
}
resource "microsoft365_graph_beta_groups_group_member_assignment" "add_to_existing" {
group_id = data . microsoft365_graph_beta_groups_group . existing_group . id
member_id = microsoft365_graph_beta_users_user . john_doe . id
member_object_type = "User"
}
Troubleshooting
Authentication errors
If you receive authentication errors, verify:
Your tenant ID, client ID, and client secret are correct
Your Entra ID application has the required API permissions
Admin consent has been granted for the API permissions
Permission errors
If operations fail with permission errors:
Check that your application has the necessary Graph API permissions
Ensure admin consent has been granted
Verify the service principal has the appropriate directory roles if needed
Enable debug mode
For detailed logging, enable debug mode:
provider "microsoft365" {
# ... other configuration ...
debug_mode = true
}
Get help
Join Discord Get help from the community
Report issues Found a bug? Let us know