I'm trying to execute a sample terraform plan given below.
# Configure the Microsoft Azure Provider
provider "azurerm" {
subscription_id = "..."
client_id = "..."
client_secret = "..."
tenant_id = "..."
}
# Create a resource group
resource "azurerm_resource_group" "production" {
name = "production"
location = "West US"
}
# Create a virtual network in the web_servers resource group
resource "azurerm_virtual_network" "network" {
name = "productionNetwork"
address_space = ["10.0.0.0/16"]
location = "West US"
resource_group_name = "${azurerm_resource_group.production.name}"
subnet {
name = "subnet1"
address_prefix = "10.0.1.0/24"
}
subnet {
name = "subnet2"
address_prefix = "10.0.2.0/24"
}
subnet {
name = "subnet3"
address_prefix = "10.0.3.0/24"
}
}`enter code here`
I followed [1] to generate credentials via creating Active Directory application and used the correct subscription_id, client_id, client_secret, tenant_id in the above plan and executed 'terraform plan' against it. But I'm getting below error.
Error refreshing state: 1 error(s) occurred:
Credentials for acessing the Azure Resource Manager API are likely to be incorrect, or
the service principal does not have permission to use the Azure Service Management
API.
[1] https://azure.microsoft.com/en-us/documentation/articles/resource-group-create-service-principal-portal/
Any idea on this?
It seems like in terraform documentation, they haven't included the step of assigning role to the service principal. Follow these steps and it works.
1) Create the service principal through Azure CLI by following this link https://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal-cli/ which assigns the role as well to the service principal
2) Go to Azure RM portal-->Active Directory -->App registration --> Create the key
3) Use the appropriate values from above in .tf file.
Then run the command terraform plan.
Related
I'm trying to deploy a web app with a database on Azure but can't seem to get it to work despite double/triple checking the credentials for the Tenant in Azure. Tried creating new client secrets but doesn't work regardless.
Unable to list provider registration status, it is possible that this is due to invalid credentials or the service principal does not have permission to use the Resource Manager API, Azure error: resources.ProvidersClient#List: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailed" Message="The client '########-########-########-########-########' with object id '########-########-########-########-########' does not have authorization to perform action 'Microsoft.Resources/subscriptions/providers/read' over scope '/subscriptions/########-########-########-########-########' or the scope is invalid. If access was recently granted, please refresh your credentials."
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.0.0"
}
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription_id
client_id = var.client_id
client_secret = var.client_secret
tenant_id = var.tenant_id
}
resource "azurerm_resource_group" "example" {
name = "azure-tf-bgapp"
location = "West Europe"
}
resource "azurerm_container_group" "example" {
name = "bgapp-tf"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_address_type = "Public"
dns_name_label = "aci-label"
os_type = "Linux"
container {
name = "bgapp-web"
image = "shekeriev/bgapp-web"
cpu = "0.5"
memory = "1.5"
ports {
port = 80
protocol = "TCP"
}
}
container {
name = "bgapp-web"
image = "shekeriev/bgapp-db"
cpu = "0.5"
memory = "1.5"
environment_variables = {
"MYSQL_ROOT_PASSWORD" = "Password1"
}
}
tags = {
environment = "bgapp"
}
}
I tried in my environment and got below results:
Initially I tried the same code and got same error in my environment.
Console:
The above error occurs due to your (Service principal) doesn't has required permission to do that operation (Authorization).
After assigning a role like Owner to the service principal code worked successfully.
Go to portal -> subscription -> Access control (IAM) -> Add role assignments -> owner -> Add your service principal -> review + create.
After I executed code of terraform it executed perfectly.
Console:
Portal:
I have deployed resources on Microsoft Azure using terraform. I'm using azure storage account container to save my terraform states. I tried to configure terraform cloud to automate the deployment but I get this error.
Error: A resource with the ID "/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/msk-stage-keyvault" already exists - to be managed via Terraform this resource needs to be imported into the State. Please see the resource documentation for "azurerm_resource_group" for more information.
with module.keyvault.azurerm_resource_group.msk-keyvault
on ../../modules/az-keyvault/main.tf line 2, in resource "azurerm_resource_group" "msk-keyvault":
resource "azurerm_resource_group" "msk-keyvault" {
It seems that terraform cloud is not using my backend state in my provider.tf. How do I make terraform cloud use my backend state in provider.tf.
My Backend Provider
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.91.0"
}
}
backend "azurerm" {
resource_group_name = "msk-configurations"
storage_account_name = "mskconfigurations"
container_name = "key-vault"
key = "stage.tfstate"
}
}
provider "azurerm" {
features {}
subscription_id = var.subscription
tenant_id = var.ternant_id
}
It looks like your main.tf has already existing keyvault state.
So Initially please check if you have already configured keyvault resource in main.tf file or if you have already imported the state.
If its already present in main.tf file , and if you are again giving it in the backend , please try to remove the one from main.tf file and then execute again.
Also please note that terraform backend needs azure storage account credentials before hand in order to store into the tfstate.
So please avoid creating storage simultaneously all account, container and then the keyvault resource to tfstate.
So if storage account is already created first , then terraform can refer it later in backend.
To preconfigure the storage account and container :
Example:
1. Create storage account and container one after the other instead in the same file:
provider "azurerm" {
features {}
}
data "azurerm_resource_group" "example" {
name = "resourcegroupname"
}
resource "azurerm_storage_account" "example" {
name = "<yourstorageaccountname>"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "example" {
name = "newterraformcont"
storage_account_name = azurerm_storage_account.example.name
container_access_type = "private"
}
Then create the msk-keyvault resource group and store the tfstate in container.
This is my already created state file in terraform (terraform.tf)
provider "azurerm" {
features {}
}
terraform {
# Configure Terraform State Storage
backend "azurerm" {
resource_group_name = "<resourcegroup>"
storage_account_name = "<storage-earliercreated>"
container_name = " newterraformcont "
key = "terraform.tfstate"
}
}
resource "azurerm_resource_group" " msk-keyvault" {
name = "<msk-keyvault>"
location = "west us"
}
Reference:
azurerm_resource_group | Resources | hashicorp/azurerm | Terraform
Registry
https://www.jorgebernhardt.com/terraform-backend
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"
}
Following this article you can link Azure API Management to Users/Groups in Azure Active Directory.
At the moment I am creating the APIM instance with Terraform
resource "azurerm_api_management" "test" {
name = "example-apim"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
publisher_name = "My Company"
publisher_email = "company#terraform.io"
sku {
name = "Developer"
capacity = 1
}
}
How do I add the Active Directory Identity Provider to this?
Terraform added support for this in December 2019
https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/api_management_identity_provider_aad
You can now link it with:
resource "azurerm_api_management_identity_provider_aad" "example" {
resource_group_name = azurerm_resource_group.example.name
api_management_name = azurerm_api_management.example.name
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "00000000000000000000000000000000"
allowed_tenants = ["00000000-0000-0000-0000-000000000000"]
}
This doesn't seem to be possible with terraform, however, it can be added by calling the REST API from the Azure CLI.
az rest -m put -u "https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/my-resource-group/providers/Microsoft.ApiManagement/service/my-apim/identityProviders/aad?api-version=2019-01-01" -b "{'properties':{'clientId':'xxxxx-xxx-xxxx-xxxx-xxxxxxxxxx','clientSecret':'super-secret-password','allowedTenants':['mysite.com']}}"
The body -b is json that has been formatted to a single line.
You need to look up the clientId from active directory and know what the clientSecret is.
You can embedd this command in terraform if you wish:
resource "null_resource" "add-ad-identity-provider" {
provisioner "local-exec" {
command = "az rest -m put -u \"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/my-resource-group/providers/Microsoft.ApiManagement/service/my-apim/identityProviders/aad?api-version=2019-01-01\" -b \"{'properties':{'clientId':'xxxxx-xxx-xxxx-xxxx-xxxxxxxxxx','clientSecret':'super-secret-password','allowedTenants':['mysite.com']}}\""
}
depends_on = ["azurerm_api_management.test"]
}
the original answer from March 4th mostly works. However, a piece is missing. You also need to set up an app registration via https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-aad
That supplies the answers you need (Other than the allowed tenants, which is the tenant-id's to allow).
And that is also missing a piece, which is to, when configurating the app registration, to also go to API Permissions, add a new permission for Azure Active Directory Graph (in supported legacy APIs), create an Application permission, and add Directory.Read.All. Then grant admin consent.
If you combine resources from both azurerm and azuread providers, you can now automate the process of deploying APIM with an app registration and AAD authentication on the developer portal. It covers these two guides from Microsoft:
Quickstart: Create a new Azure API Management service instance by using the Azure portal
Authorize developer accounts by using Azure Active Directory in Azure API Management
Terraform code example:
terraform {
required_version = ">=1.0.9"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.81.0"
}
azuread = {
source = "hashicorp/azuread"
version = "=2.7.0"
}
}
backend "azurerm" {}
}
provider "azurerm" {
features {}
}
provider "azuread" {}
resource "azurerm_api_management" "api_management" {
name = var.api_management_name
location = var.location
resource_group_name = var.resource_group_name
publisher_name = var.publisher_name
publisher_email = var.publisher_email
sku_name = var.api_management_sku
identity {
type = "SystemAssigned"
}
}
resource "azuread_application" "application" {
display_name = var.application_name
web {
redirect_uris = ["${azurerm_api_management.api_management.developer_portal_url}/"]
}
}
resource "azuread_application_password" "password" {
application_object_id = azuread_application.application.object_id
}
resource "azurerm_api_management_identity_provider_aad" "identity_provider_aad" {
resource_group_name = var.resource_group_name
api_management_name = azurerm_api_management.api_management.name
client_id = azuread_application.application.application_id
client_secret = azuread_application_password.password.value
allowed_tenants = var.id_provider_allowed_tenants
}
Trying to assign a service principal to an Azure container registry to no avail. The Azure docs state the following...
You can assign a service principal to your registry, and your application or service can use it for headless authentication
I wish to do this through terraform, but I do not see any sort of service principal option in the Azure provider azurerm_container_registry docs (managed)...
resource "azurerm_resource_group" "rg" {
name = "resourceGroup1"
location = "West US"
}
resource "azurerm_container_registry" "acr" {
name = "containerRegistry1"
resource_group_name = "${azurerm_resource_group.rg.name}"
location = "${azurerm_resource_group.rg.location}"
sku = "Premium"
admin_enabled = false
georeplication_locations = ["East US", "West Europe"]
}
I was hoping to find a service_principal stanza such as what is available in the azurerm_kubernetes_cluster resource...
resource "azurerm_kubernetes_cluster" "test" {
// [...]
service_principal {
client_id = "00000000-0000-0000-0000-000000000000"
client_secret = "00000000000000000000000000000000"
}
}
Am I thinking about this the wrong way? There seems to be a disconnect with a lot of the Azure resources on how service principals play a part. I assume evolving APIs are to blame, but this seems like a simple ask... Perhaps I'm totally off base here. Thoughts?
There is something different between the AKS and ACR with the Service Principal.
For AKS, it needs to create a secret for the pods or services to access the ACR and the secret is set in the yaml file. So it needs both client_id and client_secret at the same time.
But for ACR, you need a Service Principal with specific permission associated with it for others to access it. So you just need to assign the Service Principal to the ACR with the permission setting and client_id. The terraform code would like this:
resource "azurerm_resource_group" "rg" {
name = "resourceGroup1"
location = "West US"
}
resource "azurerm_container_registry" "acr" {
name = "containerRegistry1"
resource_group_name = "${azurerm_resource_group.rg.name}"
location = "${azurerm_resource_group.rg.location}"
sku = "Premium"
admin_enabled = false
georeplication_locations = ["East US", "West Europe"]
}
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 = "VT=uSgbTanZhyz#%nL9Hpd+Tfay_MRV#"
end_date = "2020-01-01T01:02:03Z"
}
resource "azurerm_role_assignment" "test" {
scope = "${azurerm_container_registry.acr.id}"
role_definition_id = "Contributor"
principal_id = "${azurerm_azuread_service_principal_password.test.service_principal_id}"
}
For more details, see azurerm_role_assignment. Hope this will help you. If you need more help please let me know.