مشاركة عبر


تشغيل سريع: إنشاء موارد Azure Functions وتوزيعها باستخدام Bicep

في هذه المقالة، يمكنك استخدام Bicep لإنشاء تطبيق وظائف في خطة Flex Consumption في Azure، إلى جانب موارد Azure المطلوبة. يوفر تطبيق الوظائف سياق تنفيذ بلا خادم لعمليات تنفيذ التعليمات البرمجية للوظيفة. يستخدم التطبيق معرف Microsoft Entra مع الهويات المدارة للاتصال بموارد Azure الأخرى.

يتطلب إكمال هذا التشغيل السريع تكلفة صغيرة تبلغ بضعة سنتات أمريكية أو أقل في حساب Azure الخاص بك.

Bicep هي لغة المجال المخصصة (DSL) التي تستخدم بناء الجملة التعريفي لتوزيع موارد Azure. توفر بناء جملة مختصر، وسلامة موثوقة من النوع، ودعمًا لإعادة استخدام التعليمة البرمجية. تقدم Bicep أفضل تجربة تأليف لحلول البنية الأساسية باعتبارها تعليمة برمجية في Azure.

بعد إنشاء تطبيق الوظيفة، يمكنك نشر التعليمات البرمجية لمشروع Azure Functions إلى هذا التطبيق. تعد خطوة نشر التعليمات البرمجية النهائية خارج نطاق مقالة التشغيل السريع هذه.

المتطلبات الأساسية

حساب Azure

قبل أن تبدأ، يجب أن يكون لديك حساب Azure باشتراك نشط. أنشئ حساباً مجاناً.

مراجعة ملف Bicep

ملف Bicep المستخدم في هذا التشغيل السريع هو من قالب Azure Quickstart.

/* This Bicep file creates a function app running in a Flex Consumption plan 
that connects to Azure Storage by using managed identities with Microsoft Entra ID. */

//********************************************
// Parameters
//********************************************

@description('Primary region for all Azure resources.')
@minLength(1)
param location string = resourceGroup().location 

@description('Language runtime used by the function app.')
@allowed(['dotnet-isolated','python','java', 'node', 'powerShell'])
param functionAppRuntime string = 'dotnet-isolated' //Defaults to .NET isolated worker

@description('Target language version used by the function app.')
@allowed(['3.10','3.11', '7.4', '8.0', '9.0', '10', '11', '17', '20'])
param functionAppRuntimeVersion string = '8.0' //Defaults to .NET 8.

@description('The maximum scale-out instance count limit for the app.')
@minValue(40)
@maxValue(1000)
param maximumInstanceCount int = 100

@description('The memory size of instances used by the app.')
@allowed([2048,4096])
param instanceMemoryMB int = 2048

@description('A unique token used for resource name generation.')
@minLength(3)
param resourceToken string = toLower(uniqueString(subscription().id, location))

@description('A globally unique name for your deployed function app.')
param appName string = 'func-${resourceToken}'

//********************************************
// Variables
//********************************************

// Generates a unique container name for deployments.
var deploymentStorageContainerName = 'app-package-${take(appName, 32)}-${take(resourceToken, 7)}'

// Key access to the storage account is disabled by default 
var storageAccountAllowSharedKeyAccess = false

// Define the IDs of the roles we need to assign to our managed identities.
var storageBlobDataOwnerRoleId  = 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b'
var storageBlobDataContributorRoleId = 'ba92f5b4-2d11-453d-a403-e96b0029c9fe'
var storageQueueDataContributorId = '974c5e8b-45b9-4653-ba55-5f855dd0fb88'
var storageTableDataContributorId = '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3'
var monitoringMetricsPublisherId = '3913510d-42f4-4e42-8a64-420c390055eb'

//********************************************
// Azure resources required by your function app.
//********************************************

resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
  name: 'log-${resourceToken}'
  location: location
  properties: any({
    retentionInDays: 30
    features: {
      searchVersion: 1
    }
    sku: {
      name: 'PerGB2018'
    }
  })
}

resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = {
  name: 'appi-${resourceToken}'
  location: location
  kind: 'web'
  properties: {
    Application_Type: 'web'
    WorkspaceResourceId: logAnalytics.id
    DisableLocalAuth: true
  }
}

resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
  name: 'st${resourceToken}'
  location: location
  kind: 'StorageV2'
  sku: { name: 'Standard_LRS' }
  properties: {
    accessTier: 'Hot'
    allowBlobPublicAccess: false
    allowSharedKeyAccess: storageAccountAllowSharedKeyAccess
    dnsEndpointType: 'Standard'
    minimumTlsVersion: 'TLS1_2'
    networkAcls: {
      bypass: 'AzureServices'
      defaultAction: 'Allow'
    }
    publicNetworkAccess: 'Enabled'
  }
  resource blobServices 'blobServices' = {
    name: 'default'
    properties: {
      deleteRetentionPolicy: {}
    }
    resource deploymentContainer 'containers' = {
      name: deploymentStorageContainerName
      properties: {
        publicAccess: 'None'
      }
    }
  }
}

resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: 'uai-data-owner-${resourceToken}'
  location: location
}

resource roleAssignmentBlobDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, storage.id, userAssignedIdentity.id, 'Storage Blob Data Owner')
  scope: storage
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataOwnerRoleId)
    principalId: userAssignedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource roleAssignmentBlob 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, storage.id, userAssignedIdentity.id, 'Storage Blob Data Contributor')
  scope: storage
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageBlobDataContributorRoleId)
    principalId: userAssignedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource roleAssignmentQueueStorage 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, storage.id, userAssignedIdentity.id, 'Storage Queue Data Contributor')
  scope: storage
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageQueueDataContributorId)
    principalId: userAssignedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource roleAssignmentTableStorage 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, storage.id, userAssignedIdentity.id, 'Storage Table Data Contributor')
  scope: storage
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', storageTableDataContributorId)
    principalId: userAssignedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource roleAssignmentAppInsights 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(subscription().id, applicationInsights.id, userAssignedIdentity.id, 'Monitoring Metrics Publisher')
  scope: applicationInsights
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', monitoringMetricsPublisherId)
    principalId: userAssignedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

//********************************************
// Function app and Flex Consumption plan definitions
//********************************************

resource appServicePlan 'Microsoft.Web/serverfarms@2024-04-01' = {
  name: 'plan-${resourceToken}'
  location: location
  kind: 'functionapp'
  sku: {
    tier: 'FlexConsumption'
    name: 'FC1'
  }
  properties: {
    reserved: true
  }
}

resource functionApp 'Microsoft.Web/sites@2024-04-01' = {
  name: appName
  location: location
  kind: 'functionapp,linux'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${userAssignedIdentity.id}':{}
      }
    }
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    siteConfig: {
      minTlsVersion: '1.2'
    }
    functionAppConfig: {
      deployment: {
        storage: {
          type: 'blobContainer'
          value: '${storage.properties.primaryEndpoints.blob}${deploymentStorageContainerName}'
          authentication: {
            type: 'UserAssignedIdentity'
            userAssignedIdentityResourceId: userAssignedIdentity.id
          }
        }
      }
      scaleAndConcurrency: {
        maximumInstanceCount: maximumInstanceCount
        instanceMemoryMB: instanceMemoryMB
      }
      runtime: { 
        name: functionAppRuntime
        version: functionAppRuntimeVersion
      }
    }
  }
  resource configAppSettings 'config' = {
    name: 'appsettings'
    properties: {
        AzureWebJobsStorage__accountName: storage.name
        AzureWebJobsStorage__credential : 'managedidentity'
        AzureWebJobsStorage__clientId: userAssignedIdentity.properties.clientId
        APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey
        APPLICATIONINSIGHTS_AUTHENTICATION_STRING: 'ClientId=${userAssignedIdentity.properties.clientId};Authorization=AAD'
      }
  }
}

ينشئ ملف التوزيع هذا موارد Azure هذه التي يحتاجها تطبيق الوظائف الذي يتصل بأمان بخدمات Azure:

اعتبارات النشر:

  • يتم استخدام حساب التخزين لتخزين بيانات التطبيق المهمة، بما في ذلك حزمة نشر التعليمات البرمجية للتطبيق. ينشئ هذا النشر حساب تخزين يتم الوصول إليه باستخدام مصادقة معرف Microsoft Entra والهويات المدارة. يتم منح الوصول إلى الهوية على أساس الأذونات الأقل.
  • يتم تعيين ملف Bicep افتراضيا لإنشاء تطبيق C# يستخدم .NET 8 في عملية معزولة. بالنسبة للغات الأخرى، استخدم المعلمات functionAppRuntime و functionAppRuntimeVersion لتحديد اللغة والإصدار المحدد لتشغيل التطبيق عليهما. تأكد من تحديد لغة البرمجة الخاصة بك في أعلى المقالة.

نشر ملف Bicep

  1. احفظ ملف Bicep بالاسم main.bicep على الكمبيوتر المحلي الخاص بك.

  2. بادر بتوزيع ملف Bicep باستخدام Azure CLI أو Azure PowerShell.

    • Azure CLI
    • Azure PowerShell
    az group create --name exampleRG --location <SUPPORTED_REGION>
    az deployment group create --resource-group exampleRG --template-file main.bicep --parameters functionAppRuntime=dotnet-isolated functionAppRuntimeVersion=8.0
    
    az group create --name exampleRG --location <SUPPORTED_REGION>
    az deployment group create --resource-group exampleRG --template-file main.bicep --parameters functionAppRuntime=java functionAppRuntimeVersion=17
    
    az group create --name exampleRG --location <SUPPORTED_REGION>
    az deployment group create --resource-group exampleRG --template-file main.bicep --parameters functionAppRuntime=node functionAppRuntimeVersion=20
    
    az group create --name exampleRG --location <SUPPORTED_REGION>
    az deployment group create --resource-group exampleRG --template-file main.bicep --parameters functionAppRuntime=python functionAppRuntimeVersion=3.11
    
    az group create --name exampleRG --location <SUPPORTED_REGION>
    az deployment group create --resource-group exampleRG --template-file main.bicep --parameters functionAppRuntime=powerShell functionAppRuntimeVersion=7.4
    

    في هذا المثال، استبدل <SUPPORTED_REGION> بمنطقة تدعم خطة الاستهلاك المرن.

    عند الانتهاء من عملية النشر، يجب أن ترى رسالة تشير إلى نجاح عملية النشر.

التحقُّق من صحة عملية النشر

استخدم Azure CLI أو Azure PowerShell للتحقق من صحة التوزيع.

  • Azure CLI
  • Azure PowerShell
az resource list --resource-group exampleRG

زيارة صفحة الترحيب لتطبيق الوظائف

  1. استخدم الإخراج من خطوة التحقق السابقة لاسترداد الاسم الفريد الذي تم إنشاؤه لتطبيق الوظائف.

  2. افتح مستعرضاً وأدخل عنوان URL التالي: <https://<appName.azurewebsites.net>. تأكد من استبدال <\appName> بالاسم الفريد الذي تم إنشاؤه لتطبيق الوظائف.

    عند زيارة عنوان URL، يجب أن تظهر لك صفحة مثل هذه:

    صفحة الترحيب لتطبيق الوظائف

تنظيف الموارد

الآن بعد أن قمت بنشر تطبيق وظائف وموارد ذات صلة إلى Azure، يمكنك المتابعة إلى الخطوة التالية من نشر التعليمات البرمجية للمشروع إلى تطبيقك. وإلا، استخدم هذه الأوامر لحذف الموارد، عندما لا تحتاج إليها.

  • Azure CLI
  • Azure PowerShell
az group delete --name exampleRG

يمكنك أيضا إزالة الموارد باستخدام مدخل Microsoft Azure.

الخطوات التالية

يمكنك الآن نشر مشروع تعليمات برمجية إلى موارد تطبيق الوظائف التي قمت بإنشائها في Azure.

يمكنك إنشاء مشروع تعليمات برمجية والتحقق منه ونشره إلى تطبيق الوظائف الجديد من البيئات المحلية التالية: