15

As the terraform azurerm provider misses support for azure webapp access restrictions (see github issue). We use a null_resource with local-exec to apply a access restriction:


  provisioner "local-exec" {
    command = <<COMMAND
      az webapp config access-restriction add --subscription ${self.triggers.subscription_id} --resource-group ${self.triggers.resource_group} \
        --name ${self.triggers.web_app_name} --rule-name 'allow application gateway' --action Allow --vnet-name ${self.triggers.vnet_name} \
        --subnet ${self.triggers.subnet_name} --priority 100
    COMMAND
  }

Our terraform code is then later run by an azure DevOps Pipeline, which uses a Service Connection (with Service Principal) to authenticate with Azure. The following task is trying to apply the terraform resources:

  - task: TerraformCLI@0
    displayName: "Terraform apply"
    inputs:
      command: 'apply'
      commandOptions: '--var-file="./environments/${{ parameters.environment }}.tfvars"'
      workingDirectory: '$(System.DefaultWorkingDirectory)/${{ parameters.projectFolder }}'
      environmentServiceName: 'shared-${{ parameters.environment }}-001'

which results in the following Error:

Error: Error running command '      az webapp config access-restriction remove --subscription shared-staging-001 --resource-group rg-hub-network-staging \
        --name landing-webapp-hub --rule-name 'allow application gateway'
': exit status 1. Output: Subscription 'shared-staging-001' not recognized.
Command group 'webapp config access-restriction' is in preview. It may be changed/removed in a future release.
Please run 'az login' to setup account.

No we tried to replace the TerraformCLI@0 Task with either a plain bash script or a AzureCLI@2 Task.

We could not get az login to work in a plain bash script due to the missing Infos. The approach described here does not work either.

Running the terraform commands inside a AzureCLI@2 Task looks promissing but causes some strange errors related to the service principal login:

  - task: AzureCLI@2
    displayName: "Terraform init"
    inputs:
      azureSubscription: shared-${{ parameters.environment }}-001
      scriptType: bash
      scriptLocation: inlineScript
      inlineScript: |
        terraform init --backend-config="./environments/${{ parameters.environment }}_backend.tfvars"

This causes the following error:

Initializing modules...
- app-gateway in modules/app-gateway
- dummy1 in modules/BRZ365-AppService
- dummy2 in modules/BRZ365-AppService
- hub-network in modules/hub-network
- landing_zone_app in modules/BRZ365-AppService
- squad-area in modules/squad-area

Initializing the backend...

Error: Error building ARM Config: Authenticating using the Azure CLI is only supported as a User (not a Service Principal).

To authenticate to Azure using a Service Principal, you can use the separate 'Authenticate using a Service Principal'
auth method - instructions for which can be found here: 

Alternatively you can authenticate using the Azure CLI by using a User Account.
3
  • Can you try to remove --subscription shared-staging-001 from local-exec? I'm not sure of this is really needed here. Commented Apr 17, 2020 at 10:28
  • @KrzysztofMadej I get the same error without the unkown subscription when removing the subscription: Error: Error running command ' az webapp config access-restriction remove --resource-group rg-hub-network-staging \ --name dummy2-webapp-hub --rule-name 'allow application gateway' ': exit status 1. Output: Command group 'webapp config access-restriction' is in preview. It may be changed/removed in a future release. Please run 'az login' to setup account. Commented Apr 17, 2020 at 10:55
  • Can you try pass the subscription id and not the name Commented Apr 17, 2020 at 12:02

5 Answers 5

29

I finally got this to work with the AzureCLI approach I described in the first post. I use addSpnToEnvironment (it adds the service provider credentials to the environment, as described in the documentation) and set the required parameters as described by terraform.

      - task: AzureCLI@2
        displayName: "Terraform"
        inputs:
          azureSubscription:  shared-${{ parameters.environment }}-001
          scriptType: bash
          addSpnToEnvironment: true
          scriptLocation: inlineScript
          inlineScript: |
            export ARM_CLIENT_ID=$servicePrincipalId
            export ARM_CLIENT_SECRET=$servicePrincipalKey
            export ARM_TENANT_ID=$tenantId

            terraform init .....

         
Sign up to request clarification or add additional context in comments.

4 Comments

I copied and pasted your script, substituting my subscription id, of course. I always get the error Error building ARM Config: obtain subscription() from Azure CLI: parsing json result from the Azure CLI: waiting for the Azure CLI: exit status 1: ERROR: Please run 'az login' to setup account. I even tried putting 'az login' in the inline script. Nothing works. Do you know what I could be doing wrong, or can you verify your solution still works?
@user658182 I think you need to put the "service connection name" in the "azureSubscription" field and not the actual SubscriptionID. That way works for me.
Excellent answer. In case you are using powershell core (by setting scriptType to pscore), the syntax is like this: $env:ARM_CLIENT_ID = $env:servicePrincipalId and $env:ARM_CLIENT_SECRET = $env:servicePrincipalKey and $env:ARM_TENANT_ID = $env:tenantId
I also had to provide a subscription ID, like so: export ARM_SUBSCRIPTION_ID=$(az account show | jq -r .id)
4

I got through this with local-exec.

provisioner "local-exec" {
    command = <<COMMAND
      az login --service-principal --username #{APP_ID}# --password #{SP_PASSWORD}# --tenant #{TENANT_ID}#
      az webapp config access-restriction add --resource-group ${azurerm_resource_group.example.name} --name ${azurerm_app_service.example.name} --rule-name developers --action Allow --ip-address 130.220.0.0/27 --priority 200
    COMMAND

    interpreter = ["PowerShell", "-Command"]
  }

Unfortunately I had to create another service principal for this purpose as I didn't want to reset the one used by Azure DevOps (but you can give it a try and reuse this one).

I used these commands:

az ad sp create-for-rbac --name sp-for-cli

az role assignment create --assignee APP_ID --role Contributor

As next I declared variables APP_ID, SP_PASSWORD and TENANT_ID on my release pipeline with values given by command above.

As last step I added token replace step:

steps:
- task: qetza.replacetokens.replacetokens-task.replacetokens@3
  displayName: 'Replace tokens in main.tf'
  inputs:
    rootDirectory: '$(System.DefaultWorkingDirectory)/terraform/drop'
    targetFiles: main.tf

Now when I run az webapp config access-restriction show --resource-group example-resources --name example-app-service-for-cli I get:

"ipSecurityRestrictions": [
    {
      "action": "Allow",
      "additional_properties": {},
      "description": null,
      "ip_address": "130.220.0.0/27",
      "name": "developers",
      "priority": 200,
      "subnet_mask": null,
      "subnet_traffic_tag": null,
      "tag": "Default",
      "vnet_subnet_resource_id": null,
      "vnet_traffic_tag": null
    },

The whole code you can find here.

1 Comment

I could not test your approach (as I got a different approach to work). But this looks fine. :)
4

I had a similar issue when working with Terraform to setup resources on Azure.

When I run terraform plan I get the error:

│ Error: building AzureRM Client: Authenticating using the Azure CLI is only supported as a User (not a Service Principal).
│ 
│ To authenticate to Azure using a Service Principal, you can use the separate 'Authenticate using a Service Principal'
│ auth method - instructions for which can be found here: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret
│ 
│ Alternatively you can authenticate using the Azure CLI by using a User Account.
│ 
│   with provider["registry.terraform.io/hashicorp/azurerm"],
│   on main.tf line 18, in provider "azurerm":
│   18: provider "azurerm" {

Here's how I solved it.

The problem was that I was authenticating Azure CLI with Terraform the wrong way. I was using the command below to authenticate it with a service principal:

az login --service-principal --username $SERVICE_PRINCIPAL_APP_ID --password $SERVICE_PRINCIPAL_PASSWORD --tenant $SERVICE_PRINCIPAL_TENANT_ID

The issue is that the command above does not authenticate you as a user, but as a service principal, which is not a supported way to authenticate using service principal by Terraform.

[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "f8256ac9-e164-4ac6-a487-0f6e3dc17532",
    "id": "bec123cd-bead-43ba-90c6-5235cririe903",
    "isDefault": true,
    "managedByTenants": [],
    "name": "my-subscription-1",
    "state": "Enabled",
    "tenantId": "f8556ac9-e159-4ac6-a487-8r9erhwjw",
    "user": {
      "name": "r673reb62-bbf7-40e1-ab10-ry45640484",
      "type": "servicePrincipal"
    }
  }
]

The right way to do this was to simply run the command below:

az login

Then the Azure CLI will open up a browser window where I will log in to my Azure account on the portal and get authenticated as a user. If you have multiple subscriptions, it will select your currently logged-in subscription as your default subscription.

[
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "f8256ac9-e164-4ac6-a487-0f6e3dc17532",
    "id": "bec123cd-bead-43ba-90c6-5235cririe903",
    "isDefault": true,
    "managedByTenants": [],
    "name": "my-subscription-1",
    "state": "Enabled",
    "tenantId": "f8556ac9-e159-4ac6-a487-8r9erhwjw",
    "user": {
      "name": "[email protected]",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "ghfjg6ac9-e164-7466-a487-0f6e3dc17hjr",
    "id": "rtkrt7f3d-51b2-7587-88b7-d29ftheruofw",
    "isDefault": false,
    "managedByTenants": [],
    "name": "my-subscription-2",
    "state": "Enabled",
    "tenantId": "f8556ac9-e159-4ac6-a487-8r9erhwjw",
    "user": {
      "name": "[email protected]",
      "type": "user"
    }
  }
]

OR if you desire to authenticate as service principal, then you will export the following variables using the provided variables names:

export ARM_CLIENT_ID="your-service-principal-appid"
export ARM_CLIENT_SECRET="your-service-principal-password"
export ARM_SUBSCRIPTION_ID="your-current-subscription-id"
export ARM_TENANT_ID="your-tenant-id"

Now, you can run your terraform plan and everything will work fine.

Resources: Configuring the Service Principal in Terraform

1 Comment

This is not a solution for the problem presented, as it is in a pipeline context and specifically for a service principal. The last part, that you just copied from the Terraform site, does not just simply "work fine".
0

Update November 2023

I made it work by using the ARM prefix, and changing my azurerm provider to 3.0

Comments

0

The accepted answer by @quadroid is perfect, which is so helpful and save my time a lot. set addSpnToEnvironment and use export commands to set Azure Environment Variables are keys to resolve the issue.

But it doesn't support management group (if you know AWS, it is similar as AWS cross account assume role) , only works with current subscription.

Understand scopes for Azure RBAC

  • management group
  • subscription
  • resource group
  • resource

enter image description here

So the error I got is

Error: building account: unable to configure ResourceManagerAccount: subscription ID could not be determined and was not specified

I finally worked it out with management group and I can run terraform/terragrunt command to any subscriptions under this management group.

  • use addSpnToEnvironment and set it to true (it adds the service provider credentials to the environment, as described in the documentation)
  • set Azure enironment variables as described by terraform.
# these variables are automatically generated by `addSpnToEnvironment` set to `true`
export ARM_CLIENT_ID=$servicePrincipalId
export ARM_CLIENT_SECRET=$servicePrincipalKey
export ARM_TENANT_ID=$tenantId

# this variable need be input by pipeline's parameters or hardcode if you know its subscription id
export ARM_SUBSCRIPTION_ID="20000000-0000-0000-0000-000000000000"
or
export ARM_SUBSCRIPTION_ID="$(subscription_id)"

The final pipeline for your reference

parameters:
- name: subscription_id
  displayName: 'Azure Subscription ID'
  type: string

stages:
- stage: TerraformDeployment
  jobs:
  - job: TerraformDeployment
    displayName: TerraformDeployment
    steps:
      - task: AzureCLI@2
        displayName: "Terraform"
        inputs:
          azureSubscription:  <replace_with_service_account_with_management_group_permission>
          scriptType: bash
          addSpnToEnvironment: true
          scriptLocation: inlineScript
          inlineScript: |
            export ARM_CLIENT_ID=$servicePrincipalId
            export ARM_CLIENT_SECRET=$servicePrincipalKey
            export ARM_TENANT_ID=$tenantId
            # it need be set via Environment variable, or get from parameters
            export ARM_SUBSCRIPTION_ID="$(subscription_id)"

            terraform init .....

         

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.