Skip to main content
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.

Step 1: Create your Terraform configuration

Create a new directory for your Terraform project and add the following files:

Configure the provider

Create a file named main.tf:
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:
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):
terraform.tfvars
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:
main.tf
# 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"
}
Replace [email protected] with a valid user principal name for your tenant (e.g., [email protected]).

Step 3: Initialize Terraform

Run the following command to download the provider:
terraform init
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:
terraform plan
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:
terraform apply
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:
  1. Logging into the Entra ID admin center
  2. Navigate to Identity > Users > All users to see the new user
  3. Navigate to Identity > Groups > All groups to see the new group
  4. Click on the group to verify the user is a member

Step 7: Clean up (optional)

To remove the resources you created:
terraform destroy
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:
  1. Your tenant ID, client ID, and client secret are correct
  2. Your Entra ID application has the required API permissions
  3. Admin consent has been granted for the API permissions

Permission errors

If operations fail with permission errors:
  1. Check that your application has the necessary Graph API permissions
  2. Ensure admin consent has been granted
  3. 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

Build docs developers (and LLMs) love