7

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.
Promise Preston
  • 24,334
  • 12
  • 145
  • 143
quadroid
  • 8,444
  • 6
  • 49
  • 82
  • Can you try to remove --subscription shared-staging-001 from local-exec? I'm not sure of this is really needed here. – Krzysztof Madej Apr 17 '20 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. – quadroid Apr 17 '20 at 10:55
  • Can you try pass the subscription id and not the name – Amit Baranes Apr 17 '20 at 12:02

3 Answers3

17

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 .....

         
quadroid
  • 8,444
  • 6
  • 49
  • 82
  • 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 Jan 26 '22 at 19:14
  • 1
    @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. – Ajay Meda Mar 08 '22 at 18:18
  • 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``` – Pieter Sap Jun 09 '22 at 19:13
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.

Krzysztof Madej
  • 32,704
  • 10
  • 78
  • 107
0

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": "john.doe@my-domain.com",
      "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": "john.doe@my-domain.com",
      "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

halfer
  • 19,824
  • 17
  • 99
  • 186
Promise Preston
  • 24,334
  • 12
  • 145
  • 143
  • 2
    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". – Adam Winter Jul 15 '22 at 23:36