skip to Main Content

Currently, I have a job called create-rg that creates a resource group used by azure/arm-deploy@v2 to deploy resources into at the resourcegroup scope.

I don’t like this approach, as I want the resource group to be part of Bicep IaC. At the same time, I would like to deploy the resource group with the resources in one operation, with resources "depending" on the resource group. Is this possible?

GitHub Actions (deploy.yml)

...
  create-rg:
    name: Create resource group
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Login to Azure
        uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
      - uses: azure/cli@v2
        name: Create resource group
        with:
          inlineScript: |
            az group create 
              --name ${{ inputs.resourceGroupName }} 
              --location ${{ vars.AZURE_DEFAULT_LOCATION }}
...

  deploy-infra:
    name: Deploy infrastructure
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    outputs:
      appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }}
      appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Login to Azure
        uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
      - name: Deploy infrastructure
        uses: azure/arm-deploy@v2
        id: deploy
        with:
          deploymentName: ${{ github.run_number }}
          scope: resourcegroup
          resourceGroupName: ${{ inputs.resourceGroupName }}
          template: ./infra/main.bicep
          parameters: >
            environment=${{ inputs.environment }}
          deploymentMode: Complete
          failOnStdErr: false
    needs: [validate-infra]

Bicep (main.bicep):

@description('Location for all resources.')
param location string = resourceGroup().location

@description('Select the type of environment you want to provision. Allowed values are "production" and "test".')
@allowed([
  'prod'
  'staging'
  'dev'
])
param environment string

@description('The name of the Azure Function app.')
param functionAppName string = 'func-${uniqueString(resourceGroup().id)}-${environment}'

@description('Storage Account type')
@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Standard_RAGRS'
])
param storageAccountType string = 'Standard_LRS'

var hostingPlanName = 'plan-${uniqueString(resourceGroup().id)}-${environment}'
var applicationInsightsName = 'ai-${uniqueString(resourceGroup().id)}-${environment}'
var storageAccountName = 'st${uniqueString(resourceGroup().id)}-${environment}'
var logAnalyticsName = 'log-${uniqueString(resourceGroup().id)}-${environment}'

// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#storage-blob-data-owner
var storageBlobDataOwnerRoleId = subscriptionResourceId(
  'Microsoft.Authorization/roleDefinitions',
  'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
)
var storageFunctionRoleAssignment = guid(resourceGroup().id, storageBlobDataOwnerRoleId)

// https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#azure-event-hubs-data-receiver
var eventHubDataReceiverRoleId = subscriptionResourceId(
  'Microsoft.Authorization/roleDefinitions',
  'a638d3c7-ab3a-418d-83e6-5f17a39d4fde'
)
var eventHubFunctionRoleAssignment = guid(resourceGroup().id, eventHubDataReceiverRoleId)

resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
  name: logAnalyticsName
  location: location
  properties: {
    sku: {
      name: 'PerGB2018'
    }
  }
}

resource applicationInsight 'Microsoft.Insights/components@2020-02-02' = {
  name: applicationInsightsName
  location: location
  kind: 'web'
  properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logAnalyticsWorkspace.id
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'Storage'
  properties: {
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
    allowBlobPublicAccess: false
  }
}

resource hostingPlan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: hostingPlanName
  location: location
  sku: {
    name: 'Y1'
    tier: 'Dynamic'
    size: 'Y1'
    family: 'Y'
  }
  properties: {
    reserved: true
  }
}

resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
  name: functionAppName
  location: location
  kind: 'functionapp,linux'
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    reserved: true
    serverFarmId: hostingPlan.id
    httpsOnly: true
    siteConfig: {
      linuxFxVersion: 'DOTNETCORE|8.0'
      appSettings: [
        {
          name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
          value: applicationInsight.properties.ConnectionString
        }
        {
          name: 'AzureWebJobsStorage__accountName'
          value: storageAccountName
        }
        {
          name: 'FUNCTIONS_EXTENSION_VERSION'
          value: '~4'
        }
        {
          name: 'FUNCTIONS_WORKER_RUNTIME'
          value: 'dotnet-isolated'
        }
        {
          name: 'WEBSITE_RUN_FROM_PACKAGE'
          value: '1'
        }
      ]
    }
  }

  resource config 'config' = {
    name: 'web'
    properties: {
      ftpsState: 'Disabled'
      minTlsVersion: '1.2'
    }
  }
}

resource storageRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: storageFunctionRoleAssignment
  properties: {
    principalId: functionApp.identity.principalId
    roleDefinitionId: storageBlobDataOwnerRoleId
  }
  scope: storageAccount
}

resource eventHubRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: eventHubFunctionRoleAssignment
  properties: {
    principalId: functionApp.identity.principalId
    roleDefinitionId: eventHubDataReceiverRoleId
  }
}

2

Answers


  1. You can run your main deploy task at subscription level:

    scope: subscription
    

    and then create a new resource group and deploy all other resources at new created resource group scope.

    for example:

    resource myResourceGroup 'Microsoft.Resources/resourceGroups@2021-01-01' = {
        name: 'myResourceGroupDeployment'
        location: location
      }
    
      module myResources './rgResources.bicep' = {
        name: 'myResourcesDeploymnet'
        scope: myResourceGroup
        params: {
        }
      }
    
    Login or Signup to reply.
  2. Here is the Bicep code to create a resource group and deploy resources within it. Follow the MS Doc for more details.

    File structure of below code.

    bicep  
     └──venkat.bicep 
     └──storage.bicep
    

    venkat.bicep

    targetScope='subscription'
    
    param resourceGroupName string = 'biceprg'
    param resourceGroupLocation string = 'eastus'
    param storageName string = 'venaktstorage'
    
    resource newRG 'Microsoft.Resources/resourceGroups@2024-03-01' = {
      name: resourceGroupName
      location: resourceGroupLocation
    }
    
    module storageAcct 'storage.bicep' = {
      name: 'storageModule'
      scope: newRG
      params: {
        storageLocation: resourceGroupLocation
        storageName: storageName
      }
    }
    

    storage.bicep

    param storageLocation string
    param storageName string
    
    resource storageAcct 'Microsoft.Storage/storageAccounts@2023-04-01' = {
      name: storageName
      location: storageLocation
      sku: {
        name: 'Standard_LRS'
      }
      kind: 'Storage'
      properties: {}
    }
    

    Bicep deployment command

    az deployment sub create --location eastus --template-file ./venkat.bicep
    

    Output

    enter image description here

    Reference: az deployment sub create

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search