Blog Post

Azure Database Support Blog
4 MIN READ

How to automate the deployment of an Elastic Job Agent with a Private Endpoint via Bicep

vittosmsit's avatar
vittosmsit
Icon for Microsoft rankMicrosoft
Sep 30, 2025

Environment Overview 

Recently, I worked on a case where our customer encountered an issue during the deployment of an Elastic Job Agent (EJA) and a Private Endpoint (PEP) using a Bicep deployment script. The process resulted in an endless deployment loop.
In this article, I will guide you step by step through creating an EJA with a PEP and demonstrate how to automatically approve the connection using a Bicep deployment script.

 

Technical Issue 

The main challenge is that the EJA PEP is fully managed by Microsoft, which means you cannot specify a name during the script authorization process—the system assigns one randomly. To address this, we will use deploymentScripts in combination with PowerShell to monitor and authorize the process automatically. For more details, please refer to the following resources: 

References

 

The Idea

Since it's not possible to specify the EJA PEP name as a variable for direct approval—unlike standard PEPs—the approach was to intercept the auto-generated PEP by its ID or name and then authorize it automatically. (Remember to always follow the Principle of Least Privilege (PoLP)); in my example, I used Contributor permissions for simplicity.

******************************************************************************************************************************************************************************************************
/*Disclaimer: 
******************************************************************************************************************************************************************************************************
This script is not supported under any Microsoft standard support program or service. This script is provided AS IS without warranty of any kind. 
Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. 
The entire risk arising out of the use or performance of the script and documentation remains with you. 
In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the script be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business   
information, or other pecuniary loss) arising out of the use of or inability to use the current script or documentation, even if Microsoft has been advised of the possibility of such damages.
I’m not a programmer or a Bicep expert. I simply put together what I was able to reason through in order to meet our client’s request.
Any suggestions or modifications are welcome.
******************************************************************************************************************************************************************************************************
This article was written in August 2025; code and modules may change over time.
******************************************************************************************************************************************************************************************************

*/
param prefix string = 'bicep'
param environment string = 'test'
param sqlServerName string = 'bicep-test-sqleja-sqlsrv'
param location string = 'westeurope'
param forceUpdateTag string = utcNow()

//let's check that the previously manually created server is present
resource sqlServer 'Microsoft.Sql/servers@2021-11-01' existing = {
  name: sqlServerName
}

//create the database
resource sqlDb 'Microsoft.Sql/servers/databases@2021-11-01' = {
  parent: sqlServer
  name: '${prefix}-${environment}-sqleja-sqldb'
  location: location
  properties: {
    requestedBackupStorageRedundancy: 'Local'
    maxSizeBytes: 5368709120
  }
  sku: {
    name: 'S3'
  }
}
//create the Job Agent
resource sqlJob 'Microsoft.Sql/servers/jobAgents@2024-05-01-preview' = {
  parent: sqlServer
  location: location
  name: '${prefix}-${environment}-sqleja-sqljobagent'
  properties: {
    databaseId: sqlDb.id
  }
    sku: {
    name: 'JA100'    
  }
}

//defines a Target Group within a Job Agent
resource targetGroup 'Microsoft.Sql/servers/jobAgents/targetGroups@2024-05-01-preview' = {
  parent: sqlJob
  name: '${prefix}-${environment}-sqleja-targetgroup'
  properties: {
    members: [
      {
        membershipType: 'Include'
        serverName: sqlServer.name
        type: 'SqlServer'
      }
    ]
  }
}

//generate an UMI to auth and deploy the PS script
resource scriptIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: '${prefix}-${environment}-deployscript-umi'
  location: location
}

//assign the role to the script UMI
//https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles
resource scriptRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(scriptIdentity.id, 'script-role-assignment')
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor
    principalId: scriptIdentity.properties.principalId
  }
    dependsOn: [
    delayScript
  ]
}

//let's generate a delay to allow Azure AD permissions propagation
resource delayScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
  name: 'sleep-60s-bash'
  location: location
  kind: 'AzureCLI'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${scriptIdentity.id}': {}
    }
  }
  properties: {
    azCliVersion: '2.76.0'
    scriptContent: '''
      echo "Sleeping for 60 seconds..."
      sleep 60
      echo "Done sleeping."
    '''
    retentionInterval: 'P1D'
    cleanupPreference: 'Always'
    timeout: 'PT5M'
    forceUpdateTag: forceUpdateTag
  }
  dependsOn: [
    scriptIdentity
  ]
}

//create the eja pep
resource privateEndpoint 'Microsoft.Sql/servers/jobAgents/privateEndpoints@2024-05-01-preview' = {
  parent: sqlJob
  name: '${prefix}-${environment}-sqleja-pep'
  properties: {
    targetServerAzureResourceId: sqlServer.id
  }
}

//PS script to approve the MS fully managed PEP
resource approvePrivateEndpointConnection 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
  name: 'ps-to-approve-eja-pep-by-script'
  location: location
  kind: 'AzurePowerShell'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${scriptIdentity.id}': {}
    }
  }
  properties: {
    azPowerShellVersion: '11.0'
    scriptContent: '''
    Connect-AzAccount -Identity

	$SqlServerResourceIdForPrivateLink = '/subscriptions/<yourSubID>/resourceGroups/bicep-rg/providers/Microsoft.Sql/servers/bicep-test-sqleja-sqlsrv'
    $SQLprivateEndpoints = Get-AzPrivateEndpointConnection -PrivateLinkResourceId $SqlServerResourceIdForPrivateLink

    foreach ($privateEndpoint in $SQLprivateEndpoints) 
	{
        if ($privateEndpoint.PrivateLinkServiceConnectionState.Status -eq "Pending") 
		{
            $id = $privateEndpoint.id
            Write-Output "Approving Private Endpoint Connection: $id"
            Approve-AzPrivateEndpointConnection -ResourceId "${id}" -Description "Approved by Bicep Deployment Script"
		}
  }
 '''
	retentionInterval: 'P1D' 
    cleanupPreference: 'Always'
    timeout: 'PT30M'
    forceUpdateTag: forceUpdateTag
}
  dependsOn: [
     sqlJob
    scriptRoleAssignment
  ]
}

 

Deployment Details

Below is an overview of the resources created by the automation

 

 

Conclusion

With this article, I’ve wanted to share how to automate the creation of an Elastic Job Agent Private Endpoint without requiring manual approval.
Kindly note that the products and options presented in this article are subject to change, this article reflects for Azure SQL Database in September 2025. Please also make sure to review the code carefully. It is provided as-is, with no guarantees, warranties, or support. 
Any suggestions or improvements are welcome.


I hope you find this guide useful and that it inspires you to experiment freely with your deployments.

 

Thank you.

Best Regards,

Vittorio

Updated Sep 30, 2025
Version 1.0
No CommentsBe the first to comment