I'm trying to setup my azure infrastructure using Terraform which was pretty successful so far. Our app development team needs to define application specific roles within the AzureAD application's manifest which we currently handling with the Azure Portal by simply modifying the manifest:
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "SurveyCreator",
"id": "1b4f816e-5eaf-48b9-8613-7923830595ad",
"isEnabled": true,
"description": "Creators can create Surveys",
"value": "SurveyCreator"
}
]
Using Terraform I created an azurerm_azuread_application and now want to modify the manifest accordingly.
resource "azurerm_azuread_application" "test" {
name = "APP"
homepage = "http://APPHOMEPAGE"
identifier_uris = ["http://APPHOMEPAGE"]
reply_urls = ["http://APPHOMEPAGE/REPLYURL"]
available_to_other_tenants = false
oauth2_allow_implicit_flow = false
}
Is there a way to achieve this by using Terraform only?
To create the App role, you could refer to azuread_application_app_role.
resource "azuread_application" "example" {
name = "example"
}
resource "azuread_application_app_role" "example" {
application_object_id = azuread_application.example.id
allowed_member_types = ["User"]
description = "Admins can manage roles and perform all task actions"
display_name = "Admin"
is_enabled = true
value = "administer"
}
Related
I have sample code below which creates an IAM role, a policy document, attachment of policy document and then the attachment of that policy to role.
resource "aws_iam_role" "aws_snsANDsqsTeam" {
name = "aws_snsANDsqsTeam"
assume_role_policy = data.aws_iam_policy_document.production-okta-trust-relationship.json
}
data "aws_iam_policy_document" "sns-and-sqs-policy" {
statement {
sid = "AllowToPublishToSns"
effect = "Allow"
actions = [
"sns:Publish",
]
resources = [
data.resource.arn,
]
}
statement {
sid = "AllowToSubscribeFromSqs"
effect = "Allow"
actions = [
"sqs:changeMessageVisibility*",
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:GetQueue*",
"sqs:DeleteMessage",
]
resources = [
data.resource.arn,
]
}
}
resource "aws_iam_policy" "sns-and-sqs" {
name = "sns-and-sqs-policy"
policy = data.aws_iam_policy_document.sns-and-sqs-policy.json
}
resource "aws_iam_role_policy_attachment" "sns-and-sqs-role" {
role = "aws_snsANDsqsTeam"
policy_arn = aws_iam_policy.sns-and-sqs.arn
}
Now below is the directory tree that I am trying to get
Now I want the policy document and policy code to be moved to the developer.tf file under shared/iam folder so it will look like this
data "aws_iam_policy_document" "sns-and-sqs-policy" {
statement {
sid = "AllowToPublishToSns"
effect = "Allow"
actions = [
"sns:Publish",
]
resources = [
data.resource.arn,
]
}
statement {
sid = "AllowToSubscribeFromSqs"
effect = "Allow"
actions = [
"sqs:changeMessageVisibility*",
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:GetQueue*",
"sqs:DeleteMessage",
]
resources = [
data.resource.arn,
]
}
}
resource "aws_iam_policy" "sns-and-sqs" {
name = "sns-and-sqs-policy"
policy = data.aws_iam_policy_document.sns-and-sqs-policy.json
}
and have the role creation and policy attachment code in main.tf file under iam-platform-security folder, so the code will look like this:
resource "aws_iam_role" "aws_snsANDsqsTeam" {
name = "aws_snsANDsqsTeam"
assume_role_policy = data.aws_iam_policy_document.production-okta-trust-relationship.json
}
resource "aws_iam_role_policy_attachment" "sns-and-sqs-role" {
role = "aws_snsANDsqsTeam"
policy_arn = aws_iam_policy.sns-and-sqs.arn
}
My Question is how can I reference a policy which is under shared/iam folder to attach it to a role I created in main.tf file under the folder iam-platform-security. The goal is to create policies separately in the shared/iam folder and roles under team/sub-team folders ( like iam-platform-security, iam-platform-architecture,iam-platform-debug etc etc) and then create attachments so policies remains separately as standalone.
Can somebody help me on this.
How can I reference the policy document in main.tf file in different directory.
You have to use modules so that you can separate your parent TF code from other code, such as your IAM related code in a different folder.
How do I create multiple resource groups with storage accounts for a list of users using list/count in Azure Terraform?
I have two main files (main.tf) and a module (users.tf), and I want to create one resource group and one storage account for each user.
With current setup I'm only able to create a storage account in the main resource group for each user.
Here is a example of what I already achieved:
main.tf
#azterraform {
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "2.81.0"
}
}
}
# Configure the Microsoft Azure Provider
provider "azurerm" {
features {}
}
# main Resource Group
resource "azurerm_resource_group" "main-rg" {
name = "RG-MAIN"
location = "easteurope"
}
# users module
module "users" {
source = "./modules/users"
resource_group = azurerm_resource_group.main-rg
for_each = toset( ["user1", "user2", "user3"] )
resource_unique_id = each.key
}
users.tf
# Create the Users Resource Group
resource "azurerm_resource_group" "users-rg" {
name = upper("RG-${var.resource_unique_id}-${var.config.suffix_nodash}")
location = var.config.location
tags = var.config.tags
}
# Grant contributor access to users ad-group
resource "azurerm_role_assignment" "users-contributor" {
scope = azurerm_resource_group.users-rg.id
role_definition_name = "Contributor"
principal_id = var.config.users-adgroup-id
}
# Grant contributor access to DDA Service principal
resource "azurerm_role_assignment" "DDA-sp-contributor" {
scope = azurerm_resource_group.users-rg.id
role_definition_name = "Contributor"
principal_id = var.config.dda-service-principal-id
}
# Create storage account in Users resource group
resource "azurerm_storage_account" "users-storage-account" {
name = lower("st${var.resource_unique_id}${var.config.suffix_nodash}")
resource_group_name = azurerm_resource_group.users-rg.name
location = azurerm_resource_group.users-rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}
variable.tf file:
variable "config" {
description = "(required) configuration variable value from the root module"
}
variable "resource_group" {
description = "(required) resource group for resources"
}
variable "resource_unique_id" {
description = "(required) resource unique identifier"
}
config.json
{
"config": {
"region": "East Europe",
"suffix": "-eeu",
"suffix_nodash": "eeu",
"resource-acronym": "dda",
"tags": {
"Creator": "abc#hotmail.com;",
"Managedby": "abc#hotmail.com; bcd#hotmail.com; cde#hotmail.com;",
"Project": "DDA",
"Projectversion": "1.0"
},
"location": "easteurope",
"azure_environment": {
"resource_manager_url": "https://management.azure.com/",
"authority": "https://login.microsoftonline.com/"
},
"users-adgroup-id": "abcd-abcd-abcd-abcd-abcd",
"dda-service-principal-id": "xyz-xyz-xyz-xyz-xyz"
}
}
With the following changes, the code works:
Add a variable.tf file at the root module level that defines the "config" variable
Pass the "config" variable to the "users" module in main.tf
Pass config.json to terraform with the '-var-file' option
To help with understandability, consider using native Terraform for your config file rather than JSON - unless you have a strong case for the latter.
Directory structure
config.json
main.tf
variable.tf <<<<<<< Added this file
\modules
\users
users.tf
variable.tf
variable.tf (root module level)
variable "config" {
description = "(required) configuration variable value from the root module"
}
main.tf
# users module
module "users" {
source = "./modules/users"
resource_group = azurerm_resource_group.main-rg
config = var.config <<<<<<<<<<<<<<<<<<<<<<<<<< Added
for_each = toset( ["user1", "user2", "user3"] )
resource_unique_id = each.key
}
Executed from root module directory
terraform init
terraform plan -var-file="./config.json"
terraform apply -var-file="./config.json"
Resource listing (after running terraform apply)
$ az resource list --query "[?contains(name, 'stuser')].{type:type, name:name, resourceGroup:resourceGroup}"
[
{
"name": "stuser1eeu",
"resourceGroup": "RG-USER1-EEU",
"type": "Microsoft.Storage/storageAccounts"
},
{
"name": "stuser2eeu",
"resourceGroup": "RG-USER2-EEU",
"type": "Microsoft.Storage/storageAccounts"
},
{
"name": "stuser3eeu",
"resourceGroup": "RG-USER3-EEU",
"type": "Microsoft.Storage/storageAccounts"
}
]
I am trying to code an Azure Data Factory in Terraform, but I am not sure how to code this REST dataset:
{
"name": "RestResource1",
"properties": {
"linkedServiceName": {
"referenceName": "API_Connection",
"type": "LinkedServiceReference"
},
"annotations": [],
"type": "RestResource",
"schema": []
},
"type": "Microsoft.DataFactory/factories/datasets"
}
I don't see one in the azurerm documentation. Can one instead use an azurerm_data_factory_dataset_http resource instead?
azurerm_data_factory_linked_service_rest - Does not currently exist.
azurerm_data_factory_linked_service_web - This only support a web
table and not a REST API endpoint and can't be used with the Azure
integrated runtime.
As I tried to create the linked service using rest and http it always redirected to create a web table using terraform. Hence, for now the fix for this is to use azurerm_data_factory_linked_custom_service.
Here, is the example: How to create a Custom Linked service :
provider "azurerm" {
features{}
}
data "azurerm_resource_group" "example" {
name = "Your Resource Group"
}
data "azurerm_data_factory" "example" {
name = "vipdashadf"
resource_group_name = data.azurerm_resource_group.example.name
}
resource "azurerm_data_factory_linked_custom_service" "example" {
name = "ipdashlinkedservice"
data_factory_id = data.azurerm_data_factory.example.id
type = "RestService"
description = "test for rest linked"
type_properties_json = <<JSON
{
"url": "http://www.bing.com",
"enableServerCertificateValidation": false,
"authenticationType": "Anonymous"
}
JSON
annotations = []
}
resource "azurerm_data_factory_dataset_http" "example" {
name = "apidataset"
resource_group_name = data.azurerm_resource_group.example.name
data_factory_name = data.azurerm_data_factory.example.name
linked_service_name = azurerm_data_factory_linked_custom_service.example.name
relative_url = "http://www.bing.com"
request_body = "foo=bar"
request_method = "POST"
}
Outputs:
Linked Service - ipdashlinkservice type Rest Connector
Dataset: apidataset
You could find the same stated in the GitHub discussion: Support for Azure Data Factory Linked Service for REST API #9431
Attempting to configure what Azure Role definitions Ids (roles) are allowed to be assigned, via role assignments, utilizing Azure Policy.
The below policies all create with out error, but Azure role assignments are still possible for all roles despite this policy being created / assigned.
I have tried parameter value snytax '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' as well as just the role id 'b24988ac-6180-42a0-ab88-20f7382dd24c'
Nothing seems to matter, when it comes to the policy actually preventing role assignments
I have hard coded values, as well as tried parameters
Policy following this thread, which is not preventing any role assignments:
Azure Policy to restrict role based access control(IAM) to users at Resource group level in Azure
resource "azurerm_policy_definition" "allowedRoleAssignments" {
name = "${var.project_ident}-${var.cs_env_ident}-allowedRoleAssignments"
policy_type = "Custom"
mode = "Indexed"
display_name = "${var.project_ident}-${var.cs_env_ident}-allowedRoleAssignments"
management_group_name = var.mgmtGroupName
metadata = <<METADATA
{
"category": "General"
}
METADATA
policy_rule = <<POLICY_RULE
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Authorization/roleAssignments"
},
{
"not": {
"field": "Microsoft.Authorization/roleAssignments/roleDefinitionId",
"in": "[parameters('roleDefinitionIds')]"
}
}
]
},
"then": {
"effect": "deny"
}
}
POLICY_RULE
parameters = <<PARAMETERS
{
"roleDefinitionIds": {
"type": "Array",
"metadata": {
"displayName": "roleDefinitionIds",
"description": "This policy defines a blacklist of role definitions that cannot be used in IAM, for role assignments"
}
}
}
PARAMETERS
}
Initiative Parameters:
policy_definition_reference {
policy_definition_id = azurerm_policy_definition.allowedRoleAssignments.id
parameters = {
roleDefinitionIds = "[parameters('roleDefinitionIds')]"
}
}
Policy assignment:
resource "azurerm_policy_assignment" "set-assignment-1" {
name = "${var.cs_env_ident}-sec-controls"
scope = var.policy_assignment_scope
description = "policy set definition assignment to specified management groups"
display_name = "${var.project_ident}-${var.cs_env_ident}-sec-controls"
policy_definition_id = var.policy_set_definition_id
identity { type = "SystemAssigned" }
location = var.location
parameters = <<PARAMETERS
{
"roleDefinitionIds": {
"value": ${jsonencode(var.roleDefinitionIds)}
}
}
PARAMETERS
}
Parameters passed in Policy Assignment, via Terraform Variable:
variable "roleDefinitionIds" {
description = "List of allowed role definition Ids"
default = [
"/providers/Microsoft.Authorization/roleDefinitions/1c0163c0-47e6-4577-8991-ea5c82e286e4"
]
}
Policies attempted per other threads which have similar, but different logic to achieve the same goal, but also will not actually prevent role assignments:
Very helpful guide, made by an Azure employee, but again, the Policy does not restrict any role assignments
https://blog.soft-cor.com/empowering-developer-teams-to-manage-their-own-azure-rbac-permissions-in-highly-regulated-industries/
Other similar threads
Azure custom role: authorize role assignment for a specific set of roles
resource "azurerm_policy_definition" "allowedRoleAssignments" {
name = "${var.project_ident}-${var.cs_env_ident}-allowedRoleAssignments"
policy_type = "Custom"
mode = "Indexed"
display_name = "${var.project_ident}-${var.cs_env_ident}-allowedRoleAssignments"
management_group_name = var.mgmtGroupName
metadata = <<METADATA
{
"category": "General"
}
METADATA
policy_rule = <<POLICY_RULE
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Authorization/roleAssignments"
},
{
"value": "[last(split(field('Microsoft.Authorization/roleAssignments/roleDefinitionId'),'/'))]",
"notIn": "[parameters('roleDefinitionIds')]"
}
]
},
"then": {
"effect": "Deny"
}
}
POLICY_RULE
parameters = <<PARAMETERS
{
"roleDefinitionIds": {
"type": "Array",
"metadata": {
"displayName": "roleDefinitionIds",
"description": "This policy defines a blacklist of role definitions that cannot be used in IAM, for role assignments"
}
}
}
PARAMETERS
}
Any help would be much appreciated. The policies do not fail on creation, and seem to logically make sense.
I do not understand why no matter if it is created as a whitelist, or blacklist, role assignments do not fail
I am creating and assigning these policies on a management group, so validate using a subscription / resources within the management group.
I have tried an array of role definition ids, single definitions ids, nothing successfully Denys a matching role assignment per policy.
This has been resolved after much testing and reading.
The issue is that Azure RBAC role definition IDs are not a resource and are not tagged, thus the issue was in the policy Mode:
Tagged resources and locations are processed with Indexed Policy Mode
mode = "Indexed"
To solve this issue, the policy Mode needs to be set to "All'
mode = "All"
Other wise, i have provided a complete working solution within this question!
Cheers
Experts,
I have a situation where I have to grant access on multiple Azure resources to a particular group, and i have to do this using Terraform only.
example:
Azure Group Name: India-group (5-6 users is there in this group)
Azure Subscription name: India
Azure Resource SQL Database: SQL-db-1
Azure Resource Key-Vault: India-key-vlt-1
Azure Resource Storage Account: India-acnt-1
and many more like PostgreSQL, storage account, blob.....
I think you do not need to care about how does the resource group can access the resources. What you need to care about is how to access the resources when it's necessary.
Generally, we use the service principal that assign roles that contain appropriate permission to access the resources. You can take a look at What is role-based access control (RBAC) for Azure resources and Create a service principal via CLI.
In Terraform, I assume you want to get the secrets from the KeyVault. Here is an example:
provider "azurerm" {
features {}
}
resource "azuread_application" "example" {
name = "example"
homepage = "http://homepage"
identifier_uris = ["http://uri"]
reply_urls = ["http://replyurl"]
available_to_other_tenants = false
oauth2_allow_implicit_flow = true
}
resource "azuread_service_principal" "example" {
application_id = azuread_application.example.application_id
app_role_assignment_required = false
tags = ["example", "tags", "here"]
}
resource "azurerm_resource_group" "example" {
name = "resourceGroup1"
location = "West US"
}
resource "azurerm_key_vault" "example" {
name = "testvault"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
enabled_for_disk_encryption = true
tenant_id = var.tenant_id
soft_delete_enabled = true
purge_protection_enabled = false
sku_name = "standard"
access_policy {
tenant_id = var.tenant_id
object_id = azuread_service_principal.example.object_id
key_permissions = [
"get",
]
secret_permissions = [
"get",
]
storage_permissions = [
"get",
]
}
network_acls {
default_action = "Deny"
bypass = "AzureServices"
}
tags = {
environment = "Testing"
}
}
Then you can access the key vault to get the secrets or keys through the service principal. You can also take a look at the example that controls Key Vault via python.
For other resources, you need to learn about the resource itself first, and then you can know how to access it in a suitable way. Finally, you can use Terraform to achieve it.