How can I create Azure AD group memberships using Terraform? - azure

I'm trying to create Group Memberships in an Azure AD group for the purpose of inheriting the other group's access
I can do this in the Azure portal here:
Azure Portal Group Membership Blade
But I can't figure out how to do this with powershell/azure-cli or terraform
Is this even possible ? If not, is there a workaround for it ?

You can give it a try using the Terraform Azure AD provider. We use it for user membership but I see there's support for a group object as well.
data "azuread_user" "example" {
user_principal_name = "jdoe#hashicorp.com"
}
resource "azuread_group" "example" {
name = "my_group"
}
resource "azuread_group_member" "example" {
group_object_id = azuread_group.example.id
member_object_id = data.azuread_user.example.id
}
member_object_id - (Required) The Object ID of the Azure AD Object you want to add as a Member to the Group. Supported Object types are Users, Groups or Service Principals. Changing this forces a new resource to be created.
So you would use something like this instead:
data "azuread_group" "example" {
display_name = "MyGroupName"
security_enabled = true
}
resource "azuread_group_member" "example" {
group_object_id = azuread_group.example.id
member_object_id = data.azuread_group.example.id
}

Azure support got back to me with a solution:
There's 2 ways to do this with powershell:
With the msonline module (this does not directly work in powershell core)
> Add-MsolGroupMember -GroupObjectId $groupid -GroupMemberType User -GroupMemberObjectId $userid
With the azuread module (this works directly in powershell core)
> Add-AzADGroupMember -MemberObjectId $targetGroupIdValue --TargetGroupObjectId $groupIdValue
Note, this solution adds a member to the other group for no apparent reason, might be a bug

Related

Bootstraping an Azure service account in Terraform

I am trying to write the Terraform to create an Azure "service account" and am getting quite confused by the distinction between what Azure AD calls "Applications" and "Service Principals". Effectively, I'm trying to mimic the following Azure CLI call:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${subscription_id}"
The idea would be for a human administrator to run the Terraform, to set it up once, then those credentials could later be used to authenticate for the remaining IaC. (i.e., It's a bootstrapping exercise.)
I wish to do it in Terraform, rather than a Bash script, as it seems more explicit and fits with the rest of my IaC. This is what I have so far:
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Terraform"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
# resource "azuread_service_principal" "terraform" {
# application_id = azuread_application.terraform.application_id
# owners = [data.azuread_client_config.current.object_id]
# }
#
# resource "azuread_service_principal_password" "terraform" {
# service_principal_id = azuread_service_principal.terraform.object_id
# }
resource "local_file" "azurerc" {
filename = ".azurerc"
file_permission = "0600"
content = <<EOF
export ARM_ENVIRONMENT="public"
export ARM_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
export ARM_TENANT_ID="${data.azuread_client_config.current.tenant_id}"
export ARM_CLIENT_ID="${azuread_application.terraform.application_id}"
export ARM_CLIENT_SECRET="${azuread_application_password.terraform.value}"
EOF
}
This runs, but later authenticating with the generated credentials gives an authentication error. Specifically, Terraforms says:
If you are accessing as application please make sure service principal is properly created in the tenant.
Clearly I haven't done that -- it's commented out in the above snippet -- but that's because this is where my understanding starts to break down. Why do I need both? Why do both the application and the service principal have password resources? If I generate passwords for both, which is the ARM_CLIENT_SECRET (I think the application password is the right one)? Then there's the role assignment: I see there's an azuread_app_role_assignment resource, but I'm having trouble unpicking it.
I am trying to write the Terraform to create an Azure "service
account" and am getting quite confused by the distinction between what
Azure AD calls "Applications" and "Service Principals".
Applications can be seen from Azure AD App registrations blade Where as Service Principals are other wise know as Enterprise Applications. The difference is well documented in this Microsoft Documentation.
This runs, but later authenticating with the generated credentials
gives an authentication error. Specifically, Terraforms says:
If you are accessing as application please make sure service principal
is properly created in the tenant.
This is because you have no associated service principal to that azure ad application which you have created from Terraform. The association is needed to access the application or authenticating to the azure environment with contributor role. When a App registration is created from portal it by default creates an association of AD app and Service principal , which by default results in creating a service principal for that app registration. It also applies the same concept when we use az ad sp create-for-rbac.
Effectively, I'm trying to mimic the following Azure CLI call:
az ad sp create-for-rbac --role="Contributor" --scopes="/subscriptions/${subscription_id}"
You can use the below to mimic the above command :
provider "azurerm" {
features{}
}
provider "azuread" {}
data "azurerm_subscription" "current" {}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Ansumantest"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
resource "azuread_service_principal" "terraform" {
application_id = azuread_application.terraform.application_id
owners = [data.azuread_client_config.current.object_id]
}
resource "azurerm_role_assignment" "example" {
scope = data.azurerm_subscription.current.id
role_definition_name = "Contributor"
principal_id = azuread_service_principal.terraform.object_id
}
resource "local_file" "azurerc" {
filename = ".azurerc"
file_permission = "0600"
content = <<EOF
export ARM_ENVIRONMENT="public"
export ARM_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
export ARM_TENANT_ID="${data.azuread_client_config.current.tenant_id}"
export ARM_CLIENT_ID="${azuread_application.terraform.application_id}"
export ARM_CLIENT_SECRET="${azuread_application_password.terraform.value}"
EOF
}
Output :
Using the above details I created a reosurce group in the subscription :
provider "azurerm" {
features{}
subscription_id = "88073b30-cadd-459e-b90b-8442c93573ae"
tenant_id = "ab078f81-xxxx-xxxx-xxxx-620b694ded30"
client_id = "c022ec46-xxxx-xxxx-xxxx-c72a9b82f429"
client_secret = "wdV7Q~8Grxxxxxxxxxxxxxx~SCwbRrKIq9"
}
resource "azurerm_resource_group" "name" {
name = "testansterraform"
location = "west us 2"
}

Create Azure Service Principal for multiple subscriptions

I have been able to successfully create a Service Principal secret with the below terraform. However, I am a bit confused on what is the correct approach for trying to create a Service Principal for several subscriptions. I am bit of a novice and unfortunately running into a wall with this. What is the best/right way to implement service principal for multiple subscriptions?
data "azurerm_subscription" "example-subscription" {
subscription_id = "959e460c-209e-43d7-a6e9-e30c716e0691"
}
# Azure AD App
resource "azuread_application" "example-subscription" {
name = "example-subscription"
available_to_other_tenants = false
}
# Service Principal associated with the Azure AD App
resource "azuread_service_principal" "example-subscription" {
application_id = azuread_application.example-subscription.application_id
}
# Random string to be used for Service Principal password
resource "random_password" "password-subscription" {
length = 32
special = true
}
# Service Principal password
resource "azuread_service_principal_password" "example-subscription" {
service_principal_id = azuread_service_principal.example-subscription.id
value = random_password.password-subscription.result
end_date_relative = "17520h"
}
# Role assignment for service principal
resource "azurerm_role_assignment" "example-subscription" {
scope = data.azurerm_subscription.example-subscription.id
role_definition_name = "Contributor"
principal_id = azuread_service_principal.example-subscription.id
}
Example Subscriptions
data "azurerm_subscription" "example-subscription" {
subscription_id = "959e460c-209e-43d7-a6e9-e30c716e0691"
}
data "azurerm_subscription" "example-subscription2” {
subscription_id = "b344b74c-4600-470d-ad73-e918b0d0ccd3"
}
data "azurerm_subscription" "example-subscription3” {
subscription_id = "242d05b2-e06e-4713-8094-44955dab1ee8"
}
A service principal is the local representation, or application instance, of a global application object in a single Azure AD tenant or directory. A service principal is a concrete instance created from the application object and inherits certain properties from that application object.
https://learn.microsoft.com/en-us/azure/active-directory/develop/app-objects-and-service-principals
If you have multiple Azure Subscription in one Azure AD tenant you may use your single Service Principal across all of your Azure Subscriptions.

Adding role assignments to multiple Azure subscriptions for a managed identity using terraform

I have an Azure function app that is hosted in subscription "sub-test1" and I want to add role assignment to give the managed system identity(for app) access to the subscription "sub-test1"(current) and I have been able to do it via the following:
data "azurerm_subscription" "current" {}
data "azurerm_role_definition" "owner" {
name = "Owner"
}
resource "azurerm_role_assignment" "custom_role_assignment" {
name = "${var.random_guid}"
scope = data.azurerm_subscription.current.id
role_definition_id = "${data.azurerm_subscription.current.id}${data.azurerm_role_definition.owner.id}"
principal_id = azurerm_function_app.app.identity.0.principal_id
}
But I need to give this app access to multiple subscriptions(dynamic number) inside the tenant, say "sub-test2","sub-test3","sub-test4",etc. What is the best way I can do it using terraform only? Also, can this be done using only one "azurerm_role_assignment" resource block as shown above or do I need multiple such blocks respective to each subscription?
For this requirement, you need to have enough permission to create the role assignment for in the subscriptions. The simplest way is that you need to have the Owner role of all the subscriptions. Then you can change the code like this to achieve what you want:
data "azurerm_subscriptions" "example" {}
data "azurerm_role_definition" "example" {
name = "Owner"
}
resource "azurerm_role_assignment" "custom_role_assignment" {
count = length(data.azurerm_subscriptions.example.subscriptions.*.subscription_id)
name = "${var.random_guid}"
scope = "/subscriptions/${element(data.azurerm_subscriptions.example.subscriptions.*.subscription_id, count.index)}"
role_definition_id = "/subscriptions/${element(data.azurerm_subscriptions.example.subscriptions.*.subscription_id, count.index)}${data.azurerm_role_definition.example.id}"
principal_id = azurerm_function_app.app.identity.0.principal_id
}
Here is something different. Use the azurerm_subscriptions instead of azurerm_subscription to get all the subscriptions. But it only gets the GUID of the subscriptions. So we need to complete the resource Id of the subscriptions ourselves. Also for the role definition.

How to attach an existing rbac role to an Azure VM using Terraform?

I'm creating an Azure Virtual Machine using Terraform. But I don't know how to attach an existing rbac role to it. Is there a way to do this without creating a separate resource for role definition/attachment?
If separate resource needed how to do it knowing only the rbac role name?
From your comment, you want to assign an RBAC role to a user with terraform. You can do it in two steps:
step1: Use this data source to access information about an existing Role Definition referring to this.
data "azurerm_subscription" "primary" {} # access an existing subscription
data "azurerm_role_definition" "custom" { # access an existing custom role via role_definition_id
role_definition_id = "${azurerm_role_definition.custom.role_definition_id}"
scope = "${data.azurerm_subscription.primary.id}" # /subscriptions/00000000-0000-0000-0000-000000000000
}
data "azurerm_role_definition" "custom-byname" { # access an existing custom role via name
name = "${azurerm_role_definition.custom.name}"
scope = "${data.azurerm_subscription.primary.id}"
}
data "azurerm_builtin_role_definition" "builtin" { # access an existing builtin role
name = "Contributor"
}
step2: Assign the role to a specific Azure AD user. For example, if you want to assign this role to a user at the resource group level, that is to define the scope with the resource group ID. You should have an existing resource group. You can create it with resource "azurerm_resource_group" block or data "azurerm_resource_group", then assigns a given Principal (User or Application) to a given Role with azurerm_role_assignment.
Example Usage (using a built-in Role)
data "azurerm_subscription" "primary" {}
resource "azurerm_resource_group" "myrg" {
name = "myrg"
location = "West US"
}
resource "azurerm_role_assignment" "test" {
scope = "${azurerm_resource_group.main.id}"
role_definition_name = "Reader" # or "${data.azurerm_role_definition.custom-byname.name}"
principal_id = "xxxx"
}
The principal_id is the Object ID of the user. You can find it via navigate to the Azure Active Directory in the portal -> Users -> search by the user principal name(email address in your case). You could refer to this answer.
What you are looking for I believe is the azurerm_role_definition data source, which allows you to import an already existing role definition into terraform.
See documentation here.
Example:
data "azurerm_subscription" "primary" {}
data "azurerm_role_definition" "my_role" {
### Specify either role_definition_id or name of the existing role
# role_definition_id = "00000000-0000-0000-0000-000000000000"
# name = "MyRoleDefinition"
scope = data.azurerm_subscription.primary.id
}
To assign this role to, for example, a resource group my_rg, set the scope to the resource group id:
resource "azurerm_resource_group" "my_rg" {
name = "myRG"
location = "West US"
}
data "azurerm_client_config" "client_config" {}
resource "azurerm_role_assignment" "my_role_assignment" {
scope = azurerm_resource_group.my_rg.id
role_definition_id = data.azurerm_role_definition.my_role.id
principal_id = data.azurerm_client_config.client_config.service_principal_object_id
}

How can I use Terraform to create a service principal and use that principal in a provider?

I have read the write-ups online but they dont seem to cover this topic completely and was hoping someone who has done it may have some direction for me.
We are setting up a complicated Terraform template to satisfy our IaC requirements relating to our SaaS offering. In doing so we want the template to use the user's credentials at launch to create a new service principal in Azure AD (This part I have no problem doing). Then in the next portion of the template we are using that service principal as the provider. Problem is that it throws errors in the plan/apply because the service principal doesnt exist (aka the id is non existent due to the service provider section not running yet).
So is there a way that I can do this? Create a service principal and then us it in a provider alias that uses that service principal without splitting this into multiple templates?
In the end I want this template to create the service provider using the local user's permissions or MSI, give it RBAC to a subscription, then use that service provider to create assets in that subscription.
main.ts (root)
provider "azurerm" {
alias = "ActiveDirectory"
subscription_id = "${var.subscriptionNucleus}"
}
provider "azurerm" {
alias = "Infrastructure"
subscription_id = "${var.subscriptionInfrastructure}"
}
module "activedirectory" {
providers = { azurerm = "azurerm.ActiveDirectory"
}
source = "./modules/activedirectory"
subscription_id_infrastructure = "${var.subscriptionInfrastructure}"
}
module "infrastructure" {
providers = { azurerm = "azurerm.Infrastructure"}
source = "./modules/infrastructure"
location = "${var.location}"
application_id =
"${module.activedirectory.service_principal_application_id}"
subscription_id = "${var.subscriptionInfrastructure}"
prefix = "${var.prefix}"
}
main.ts (./modules/infrastructure)
data "azurerm_azuread_service_principal" "serviceprincipal" {
application_id = "${var.application_id}"
}
provider "azurerm" {
alias = "InfrastructureSP"
subscription_id = "${var.subscription_id}"
client_id = "${var.application_id}"
client_secret = "secret"
tenant_id =
"${data.azurerm_client_config.clientconfig.tenant_id}"
}
For Azure Service Principal, there are two ways to use the service principal.
First: If you already have a service principal and want to use it in the Terraform. You can make use of the Terraform Data and the test like this:
data "azurerm_azuread_service_principal" "sp" {
application_id = "21f3e1de-54e2-4951-9743-c280ad7bd74a"
}
output "test" {
value = "${data.azurerm_azuread_service_principal.sp.id}"
}
The screenshot of the result is here:
Second: You don't have the service principal and you can just create a service principal in the Terraform like this:
resource "azurerm_azuread_service_principal" "test" {
application_id = "${azurerm_azuread_application.test.application_id}"
}
resource "azurerm_azuread_service_principal_password" "test" {
service_principal_id = "${azurerm_azuread_service_principal.test.id}"
value = "your pasword"
end_date = "2020-01-01T01:02:03Z"
}
Then, no matter which way you choose, there is an important step you should do for most resources. The step is that you need to create the role to give the permission and then assign it to the resource which needs. You can do that like this:
resource "azurerm_role_assignment" "test" {
scope = "yourScope" # the resource id
role_definition_name = "the Role In need" # such as "Contributor"
principal_id = "your service principal id"
}
Hope this will help you.
There is currently no working "depends_on" that works with modules that is not a hack (null_reference). This means that if you are breaking your template into modules(separating concerns) your order of operation is required to be correct to complete this successfully as one module will not know that the data source of service provider has to wait on the previous module to complete. I have had to break this into 2 separate templates where the first creates the service principal and the second has the modular separation that can then use a data source of azurerm_azuread_service_principal.
Once Hashicorp can implement the module depends_on, this will become easier.

Resources