Tag: Bicep

Azure Application Gateway using Azure Verified Modules

Creating an Azure Application Gateway Using Bicep Azure Verified Modules

As cloud solutions grow more complex, using infrastructure as code (IaC) has become crucial for managing, deploying, and maintaining resources smoothly. Bicep, Microsoft’s language for Azure, makes it much easier to create templates that simplify resource management. Azure Verified Modules take it a step further by providing reusable, pre-validated components that speed up your development process.

In this post, I’ll walk through creating an Azure Application Gateway using Azure Bicep, leveraging verified modules to keep things simple and efficient.

Why Use Azure Verified Modules?

Azure Verified Modules are prebuilt and tested components that help you create standardized, production-ready Azure infrastructure faster. By using verified modules:

  • You reduce the chances of introducing configuration errors.
  • You can leverage Azure best practices directly within your deployments.
  • Modules are reusable, which promotes consistency and speeds up development.

Getting Started with Bicep

Before diving into the Application Gateway setup, ensure you have the necessary tools:

  • Azure CLI: Install or update Azure CLI to the latest version.
  • Bicep CLI: You can install Bicep via Azure CLI with the command: az bicep install.
  • Visual Studio Code (VS Code): A good code editor will make writing Bicep files easier, especially with the Bicep VS Code extension.

Once your environment is ready, you can begin writing the Bicep code to deploy an Application Gateway.

Step-by-Step: Creating an Application Gateway

Step 1: Import the Azure Verified Module

To create an Azure Application Gateway, you first need to use the Azure Verified module. Microsoft provides verified modules that can be referenced directly in your Bicep file, making your infrastructure code easier to manage.

For example, to add an Application Gateway, you might use the following code:

module createcreateApplicationGatewayppGateway 'br/public:network/application-gateway:1.0.0' = {
  scope: resourceGroup(hubSubscriptionId, hubResourceGroupNameAppGateway)
  name: 'createAppGateway'
  params: {
    name: applicationGatewayName
    location: location
    sku: 'WAF_v2'
    tags: tags
    firewallPolicyResourceId: createApplicationGatewayWafPolicy.outputs.resourceId
    gatewayIPConfigurations: [
      {
        name: 'appGatewayIpConfig'
        properties: {
          subnet: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubNetworkRG}/providers/Microsoft.Network/virtualNetworks/${hubVnetName}/subnets/AppGwSubnet'
          }
        }
      }
    ]
    frontendIPConfigurations: [
      {
        name: 'appGwPublicFrontendIp'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: {
            id: createPublicIPAddressForAppGateway.outputs.resourceId
          }
        }
      }
    ]
    frontendPorts: [
      {
        name: 'port_80'
        properties: {
          port: 80
        }
      }
    ]
    backendAddressPools: [
      {
        name: 'vm1BackendPool'
        properties: {
          backendAddresses: [
            {
              ipAddress: '10.0.1.4' <- dont hardcode like this use variables
            }
          ]
        }
      }
    ]
    backendHttpSettingsCollection: [
      {
        name: 'defaultHTTPSetting'
        properties: {
          port: 8080
          protocol: 'Http'
          cookieBasedAffinity: 'Disabled'
          pickHostNameFromBackendAddress: false
          requestTimeout: 20
          probe: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubResourceGroupNameAppGateway}/providers/Microsoft.Network/applicationGateways/${applicationGatewayName}/probes/HttpProbe'
          }
        }
      }
    ]
    httpListeners: [
      {
        name: 'http'
        properties: {
          frontendIPConfiguration: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubResourceGroupNameAppGateway}/providers/Microsoft.Network/applicationGateways/${applicationGatewayName}/frontendIPConfigurations/appGwPublicFrontendIp'
          }
          frontendPort: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubResourceGroupNameAppGateway}/providers/Microsoft.Network/applicationGateways/${applicationGatewayName}/frontendPorts/port_80'
          }
          protocol: 'Http'
          requireServerNameIndication: false
          hostNames: []
          customErrorConfigurations: []
        }
      }
    ]
    requestRoutingRules: [
      {
        name: 'routingRule1'
        properties: {
          ruleType: 'Basic'
          priority: 100
          httpListener: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubResourceGroupNameAppGateway}/providers/Microsoft.Network/applicationGateways/${applicationGatewayName}/httpListeners/http'
          }
          backendAddressPool: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubResourceGroupNameAppGateway}/providers/Microsoft.Network/applicationGateways/${applicationGatewayName}/backendAddressPools/vm1BackendPool'
          }
          backendHttpSettings: {
            id: '/subscriptions/${hubSubscriptionId}/resourceGroups/${hubResourceGroupNameAppGateway}/providers/Microsoft.Network/applicationGateways/${applicationGatewayName}/backendHttpSettingsCollection/defaultHTTPSetting'
          }
        }
      }
    ]
    probes: [
      {
        name: 'HttpProbe'
        properties: {
          protocol: 'Http'
          host: '10.0.1.4' <- dont hardcode like this use variables
          path: '/api/doc/'
          interval: 30
          timeout: 30
          unhealthyThreshold: 3
          pickHostNameFromBackendHttpSettings: false
          minServers: 0
          match: {
            statusCodes: [
              '200-399'
            ]
          }
        }
      }
    ]
    enableHttp2: false
    urlPathMaps: []
    autoscaleMaxCapacity: 10
    autoscaleMinCapacity: 1
  }
  dependsOn: [
    createPublicIPAddressForAppGateway
  ]
}

This code snippet shows how you can use the verified module to create an Application Gateway, specifying various configurations such as frontend ports, IP configurations, and request routing rules.

Step 2: Define Dependencies

In the example above, you’ll need to ensure other dependent resources (like the Virtual Network, Subnet, and Public IP) are available before deploying the Application Gateway. You can define these resources in the same Bicep file to create a complete setup, or reference existing resources.

For example, adding a Virtual Network might look like this:

resource myVNet 'Microsoft.Network/virtualNetworks@2021-02-01' = {
  name: 'myVNet'
  location: resourceGroup().location
  properties: {
    addressSpace: {
      addressPrefixes: [
        '10.0.0.0/16'
      ]
    }
    subnets: [
      {
        name: 'appGatewaySubnet'
        properties: {
          addressPrefix: '10.0.1.0/24'
        }
      }
    ]
  }
}

Step 3: Deploy the Bicep Template

Once your Bicep template is complete, you can deploy it using the Azure CLI. Run the following command to deploy:

az deployment group create --resource-group <your-resource-group> --template-file main.bicep

This command will take care of provisioning the resources defined in your Bicep file.

Best Practices for Application Gateway Deployment

Summary

In summary, using Azure Bicep with verified modules simplifies the process of creating and managing Azure infrastructure. Application Gateway helps you efficiently manage incoming traffic, while Bicep makes deployment clean and straightforward. Explore other verified modules in Azure’s public registry to quickly build reliable infrastructure following best practices.

Have you tried using Bicep for your infrastructure projects? Share your experiences or questions in the comments below!




Understanding and Implementing Privileged Identity Management (PIM) Using BICEP

Introduction

In today’s digital landscape, managing privileged identities has become paramount for enterprises aiming to safeguard their IT environments against unauthorized access and potential breaches. Privileged Identity Management (PIM) solutions are vital for controlling, managing, and auditing privileged access across all parts of an IT environment. In this blog post, I will delve into how to implement PIM using BICEP and the necessity of a P2 Entra license.

What is Privileged Identity Management (PIM)?

Privileged Identity Management (PIM) refers to the control and monitoring of access and rights for users with elevated permissions who have the authority to make critical changes within Azure. PIM solutions help to prevent security breaches by ensuring that only authorized users can access sensitive systems and data.

Why BICEP?

BICEP is a domain-specific language developed by Microsoft, used primarily for deploying Azure resources declaratively. It simplifies the management of infrastructure as code (IaC), offering a cleaner and more concise syntax compared to traditional ARM templates. Using BICEP to implement PIM can streamline the deployment of Azure resources that are specifically configured for enhanced security protocols.

The Role of P2 Entra Licensing

Microsoft Entra, formerly known as Azure Active Directory (Azure AD), offers comprehensive identity and access management solutions, with P2 licensing providing advanced protection features critical for PIM. A P2 license is essential for accessing premium PIM capabilities in Azure, such as just-in-time (JIT) privileged access, risk-based conditional access policies, and detailed auditing and reporting features.


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// This script is used to elevate a group or a user to a built-in role in Azure using Privileged Identity Management (PIM)
//// The script uses the roleEligibilityScheduleRequests API to elevate the user or group to the specified role
//// The script supports the following built-in roles: GlobalAdmin, Owner, Contributor, Reader
//// The script requires the subscription ID, the principal ID of the user or group to elevate, and the built-in role to assign
//// The script also requires the start date and time for the eligibility schedule and the duration for which the eligibility is valid
//// The script creates a roleEligibilityScheduleRequests resource for each built-in role to assign
//// The script uses the subscription scope to assign the role to the user or group
//// The script uses the role definition IDs for each built-in role to assign
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

targetScope = 'subscription'

param subscriptionId string

// Set the subscription scope using the subscription ID
var subscriptionScope = '/subscriptions/${subscriptionId}'

@description('The start date and time for the eligibility schedule in ISO 8601 format')
param startDateTime string = utcNow()

@description('The duration for which the eligibility is valid in ISO 8601 format (e.g., P90D for 90 days)')
param duration string = 'P90D'

@allowed([
  'GlobalAdmin'
  'Owner'
  'Contributor'
  'Reader'
 ])
@description('Built-in role to assign')
param builtInRoleType string

var role = {
  GlobalAdmin: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/62e90394-69f5-4237-9190-012177145e10'
  Owner: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
  Contributor: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c'
  Reader: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
}

param principalIdToElevate string  // The principal ID of the user or group to elevate

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//// Deployment starts here
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


@description('Elevate User to Reader')
resource pimAssignment 'Microsoft.Authorization/roleEligibilityScheduleRequests@2022-04-01-preview' = if (builtInRoleType == 'Reader') {
  name: guid(subscriptionScope, principalIdToElevate, role.Reader)
  scope: subscription()
  properties: {
    principalId: principalIdToElevate
    requestType: 'AdminAssign'
    roleDefinitionId: role[builtInRoleType]
    scheduleInfo: {
      expiration: {
        duration: duration
        type: 'AfterDuration'
      }
      startDateTime: startDateTime
    }
  }
}

@description('Elevate User to Contributor')
resource pimAssignment2 'Microsoft.Authorization/roleEligibilityScheduleRequests@2022-04-01-preview' = if (builtInRoleType == 'Contributor') {
  name: guid(subscriptionScope, principalIdToElevate, role.Contributor)
  scope: subscription()
  properties: {
    principalId: principalIdToElevate
    requestType: 'AdminAssign'
    roleDefinitionId: role[builtInRoleType]
    scheduleInfo: {
      expiration: {
        duration: duration
        type: 'AfterDuration'
      }
      startDateTime: startDateTime
    }
  }
}

@description('Elevate User to Owner')
resource pimAssignment3 'Microsoft.Authorization/roleEligibilityScheduleRequests@2022-04-01-preview' = if (builtInRoleType == 'Owner') {
  name: guid(subscriptionScope, principalIdToElevate, role.Owner)
  scope: subscription()
  properties: {
    principalId: principalIdToElevate
    requestType: 'AdminAssign'
    roleDefinitionId: role[builtInRoleType]
    scheduleInfo: {
      expiration: {
        duration: duration
        type: 'AfterDuration'
      }
      startDateTime: startDateTime
    }
  }
}

@description('Elevate User to Global Admin')
resource pimAssignment4 'Microsoft.Authorization/roleEligibilityScheduleRequests@2022-04-01-preview' = if (builtInRoleType == 'GlobalAdmin') {
  name: guid(subscriptionScope, principalIdToElevate, role.GlobalAdmin)
  scope: subscription()
  properties: {
    principalId: principalIdToElevate
    requestType: 'AdminAssign'
    roleDefinitionId: role[builtInRoleType]
    scheduleInfo: {
      expiration: {
        duration: duration
        type: 'AfterDuration'
      }
      startDateTime: startDateTime
    }
  }
}

To call this script you can use the below code

 az deployment sub create `
        --name $deploymentID `
        --location $location `
        --template-file ./PIM.bicep `
        --parameters subscriptionId=$subscriptionID builtInRoleType=$builtInRoleType principalIdToElevate=$principalIdToElevate `
        --confirm-with-what-if `
        --output none

I hope you find this script useful, let me know if you have any feedback on this post.