0

I am giving my first steps with bicep, but I feel completely stuck :-/

I want to get the public ips from a App Service, and after that, I want to limit with them the access to an storage account. I face two problems:

  • The first one is I cannot iterate the "for" loop. It says about "allowedIPAddress" that "This expression is being used in the for-expression, which requires a value that can be calculated at the start of the deployment. you are referencing a variable which cannot be calculated"
  • The second one, how can I update the IpRules of the storage after I have obtained the rules with the IPs??

Here is my code:

////// FIRST PART: TO GET THE APP SERVICE IP
resource sitewww 'Microsoft.Web/sites@2022-03-01' existing = {
name: 'mywebapp'
}

//Here I get the list of IPs
var ipSalidaString = string(sitewww.properties.outboundIpAddresses)

//I split the IPs list to an Array String, so I can use it
var allowedIpAddresses  = split(ipSalidaString,',')

/// THIS FOR LOOP DOES NOT WORK AND I DO NOT KNOW WHY
var additionalIpSecurityRestrictions = [for ip in allowedIpAddresses: {
  action: 'Allow'
  value: ip
 }]


//////  Second Part: Update the IpRules of the Storage Account 

resource almacenamiento 'Microsoft.Storage/storageAccounts@2022-09-01'{
  
  name: 'teststorage'
  location:localizacion
   properties:{
    publicNetworkAccess: 'Enabled'  
     networkAcls:{
      defaultAction:'Deny'
      ipRules: [{   /// MUST BE UPDATED 
        action: 'Allow'
        value: '20.26.196.151'
       
      }
    ]
    }           
}
}

I tried severals ways to iterate the for loop, but always says "this expression is being used in the for-expression, which requires a value that can be calculated at the start of the deployment. you are referencing a variable which cannot be calculated"

I expect to create a object with the IpRules for my storage account

Thomas
  • 24,234
  • 6
  • 81
  • 125
Marta SL
  • 5
  • 2
  • have you considered joining your AppService to a VNet and then just using Service Endpoints to limit access to your storage? https://robertsmit.wordpress.com/2019/05/29/configure-azure-service-endpoints-for-web-applications-azure-ase-endpoints-azureserviceendpoints-webapp-azuredevops/ – silent Mar 22 '23 at 13:37

2 Answers2

0

I would recommend to join that AppService to a VNet instead and then using service endpoints to restrict the access on the Storage Account. The public IPs of the App Service Plan might change, this way you wont have to bother updating the rules.


param location string = resourceGroup().location

// Create a virtual network
resource vnet 'Microsoft.Network/virtualNetworks@2022-07-01' = {
  name: 'myVnet'
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'mySubnet'
        properties: {
          addressPrefix: '10.0.1.0/24'
          // Enable service endpoint for Microsoft.Storage
          serviceEndpoints: [
            {
              service: 'Microsoft.Storage'
              locations: [
                location
              ]
            }
          ]
        }
      }
    ]
  }
}

// Create a storage account
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: 'mystorage${uniqueString(resourceGroup().id)}'
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    // Restrict access to the storage account from the subnet only
    networkAcls: {
      bypass: 'None'
      defaultAction: 'Deny'
      virtualNetworkRules: [
        {
          id: vnet.properties.subnets[0].id // Reference the subnet id
          action: 'Allow'
        }
      ]
    }
    supportsHttpsTrafficOnly: true
  }
}

// Create an app service plan
resource appServicePlan 'Microsoft.Web/serverfarms@2021-02-01' = {
  name: 'myAppServicePlan'
  location: location
  sku: {
    name: 'S1'
    tier: 'Standard'
    size: 'S1'
    family: 'S'
    capacity: 1
  }
}

// Create an app service 
resource appService 'Microsoft.Web/sites@2022-03-01' = {
  name: 'myAppService${uniqueString(resourceGroup().id)}'
  location: location
  kind: 'app'
  properties: {
    serverFarmId: appServicePlan.id
    siteConfig: {
      alwaysOn: true
      http20Enabled: true
      webSocketsEnabled: true

    }
    virtualNetworkSubnetId: vnet.properties.subnets[0].id // Reference the subnet id
  }
}

silent
  • 14,494
  • 4
  • 46
  • 86
  • Hi! thank you very much for your answers, I will really consider using service endpoints and change the design. But, what if I want update and use resources which are already created using bicep? How can I load them in the script before the execution? I think my loop problem is that the outbound ip are not loaded before that happens, independently if I should use a private connection for best practises. – Marta SL Mar 22 '23 at 15:21
  • the `existing` key word is to reference resources which have been provisioning outside of your current template – silent Mar 22 '23 at 15:26
  • What if I want to integrate severals appService in the same subnet? Do I have to create a subnet for each appService? Must they be in the same App plan? In that case, do I have to create a subnet for each of them? – Marta SL Mar 27 '23 at 10:03
  • @MartaSantiagoLópez I think if the subnet is large enough, you can have multiple app services in the same. But I would advise to just create one subnet per app service plan, that will give you a much cleaner separation. – silent Mar 27 '23 at 14:01
  • It does not work. I add my first app service app1 to my empty "Frontend" subnet, which is /24. After that, I try to add a second app service app2 in the same "Frontend" subnet , but it says "Not available for intergration. The subnet must be empty and not already delegated". Both app service are in the same region but in different plans. – Marta SL Mar 27 '23 at 15:16
  • Ah sorry, yes. One subnet per ASP. But multiple apps in the same ASP can be in the same subnet https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration#limitations – silent Mar 27 '23 at 15:19
0

As suggested by @silent, you should definitely use VNET integration to restrict traffic to the storage account rather than adding the outbound Ips of the webApp.

The issue you're seeing can be fixed by creating the storage account in its own module:

// storage-account.bicep
param location string = resourceGroup().location
param storageAccountName string
param ips array

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: storageAccountName
  location: location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
  }
  properties: {
    publicNetworkAccess: 'Enabled'
    networkAcls: {
      defaultAction: 'Deny'
      ipRules: [for ip in ips: {
        action: 'Allow'
        value: ip
      }]
    }
  }
}

You can then call this module from the parent bicep:

// main.bicep
param location string = resourceGroup().location
param webAppName string = 'mywebapp'
param storageAccountName string = 'teststorage'

// Get a reference to the existing webapp
resource webApp 'Microsoft.Web/sites@2022-03-01' existing = {
  name: webAppName
}

// Create the storage with the IP rules
module storageAccount 'storage-account.bicep' = {
  name: 'storage-account'
  params: {
    location: location
    storageAccountName: storageAccountName
    ips: split(webApp.properties.outboundIpAddresses, ',')
    
  }
}
Thomas
  • 24,234
  • 6
  • 81
  • 125
  • Thank you very much! it is not just the design, which I definitely I am going to change, but just how I could solve this problem using bicep – Marta SL Mar 25 '23 at 10:55