Last week Hashicorp released an updated version of the Azure AD Provider which is built upon the Microsoft Graph API (and will be moving away from the AzureAD API which will be deprecated by Microsoft summer next year.
Therefore, I wanted to dive a bit into the Azure AD Provider and how you can use it to handle for instance access management to Azure AD Subscriptions, to combine the Azure AD and Azure provider to create groups/users/service principals and assign them to a subscription.
NOTE: Hashicorp has also mentioned that using the new Graph API will also open for other possibilities when it comes to automation of other resources in Azure such as Conditional Access, Security settings, and other settings within Azure.
Using Terraform against Azure AD and Azure Resource Manager can easily be managed using a single set of configuration files. So as an example, I have a backend file that defines my providers and information about where to connect.
NOTE: Both providers also support MSI meaning that you can use a managed VM in Azure which has an managed identity that can be used to connect to Azure AD and Azure Resource Manager. You can read more about it here –> Authenticating via Managed Identity | Guides | hashicorp/azuread | Terraform Registry
backend.tf (This just adds the latest version of Azure AD and Azure RM (also defines the random provider to create a random password for the BTG accounts) which can then be pushed to Vault for instance.
terraform { required_providers { azuread = { source = "hashicorp/azuread" version = "1.5.0" } azurerm = { source = "hashicorp/azurerm" } } }
provider "azuread" { tenant_id = var.tenant_id client_secret = var.client_secret client_id = var.client_id } provider "azurerm" { features {} tenant_id = var.tenant_id subscription_id = var.subscription_id client_secret = var.client_secret client_id = var.client_id } provider "random" {}
## Create Azure AD Group used for Read Access ## resource "azuread_group" "aadgroup-btg" { display_name = "aadbtg" }
data "azuread_group" "data-btg" { display_name = "aaadbtg" security_enabled = true depends_on = [azuread_group.aadgroup-btg] } ## Create Service Principals for access ##
resource "random_password" "password" { length = 32 min_upper = 4 min_numeric = 4 special = true override_special = "_%@" }
resource "azuread_user" "aaabtg01" { user_principal_name = "[email protected]" display_name = "Break The Glass" mail_nickname = "aaabtg01" password = "random_password.password.result" } data "azuread_user" "daabtg01" { user_principal_name = "[email protected]" depends_on = [azuread_user.aaabtg01] }
resource "azuread_group_member" "example" { group_object_id = azuread_group.aadgroup-btg.id member_object_id = data.azuread_user.daabtg01.id }
resource "azurerm_role_assignment" "example" { scope = data.azurerm_subscription.primary.id role_definition_name = "Contributer" principal_id = data.azuread_group.data-btg.object_id depends_on = [data.azuread_group.data-btg] }
resource "azurerm_resource_group" "example-ra" { name = "example-resources2" location = "West Europe" } resource "azurerm_log_analytics_workspace" "example" { name = "example-workspace" location = azurerm_resource_group.example-ra.location resource_group_name = azurerm_resource_group.example-ra.name sku = "pergb2018" } resource "azurerm_log_analytics_solution" "la_sentinel" { solution_name = "SecurityInsights" location = azurerm_resource_group.example-ra.location resource_group_name = azurerm_resource_group.example-ra.name workspace_resource_id = azurerm_log_analytics_workspace.example.id workspace_name = azurerm_log_analytics_workspace.example.name plan { publisher = "Microsoft" product = "OMSGallery/SecurityInsights" } }
resource "azurerm_sentinel_alert_rule_scheduled" "example3" { name = "example" log_analytics_workspace_id = azurerm_log_analytics_solution.la_sentinel.workspace_resource_id display_name = "example" severity = "High" tactics = ["InitialAccess"] query = <<QUERY SigninLogs | where UserPrincipalName == "[email protected]" | where Status.errorCode == 0 | extend AccountCustomEntity = Identity | extend IPCustomEntity = IPAddress | extend HostCustomEntity = SourceSystem QUERY }
Eureka!