تمرين - إضافة معلمات إلى البرامج النصية للتوزيع

مكتمل

الآن بعد أن استخدمت برنامجاً نصياً للنشر لترحيل بعض الأعمال اليدوية إلى قالب Azure Resource Manager (ARM)، طلب فريق تطبيق شريك آخر في مؤسستك بعض المساعدة.

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

في هذا التمرين، ستأخذ قالبك السابق كنقطة بداية وحدث البرنامج النصي PowerShell لاستخدام النموذج من فريق شريكك. ثم ستصمم طريقة لتمكين الشخص الذي ينشر القالب من تحديد ملفات التكوين التي سيتم نشرها (واحد أو أكثر).

أثناء العملية، ستقوم بما يلي:

  • قم بتحديث البرنامج النصي للنشر.
  • أضف متغير بيئة ومعلمة قالب، وقم بتمريرهما إلى البرنامج النصي للنشر.
  • أضف مخرجات إلى البرنامج النصي للنشر.
  • أضف ملف معلمات.
  • انشر القالب وتحقق من النتيجة.

إنشاء قالب البدء

ابدأ بالقالب الذي قمت بإنشائه في التمرين الأخير.

  1. افتح التعليمة البرمجية لـ Visual Studio ثم قم بإنشاء ملف يسمى azuredeploy.js.

  2. انسخ قالب البداية التالي إلى azuredeploy.json.

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
        "contentVersion": "1.0.0.1",
        "apiProfile": "",
        "parameters": {},
        "variables": {
            "storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]",
            "storageBlobContainerName": "config",
            "userAssignedIdentityName": "configDeployer",
            "roleAssignmentName": "[guid(concat(resourceGroup().id, 'contributor'))]",
            "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
            "deploymentScriptName": "CopyConfigScript"
        },
        "functions": [],
        "resources": [
            {
                "name": "[variables('storageAccountName')]",
                "type": "Microsoft.Storage/storageAccounts",
                "apiVersion": "2019-06-01",
                "tags": {
                    "displayName": "[variables('storageAccountName')]"
                },
                "location": "[resourceGroup().location]",
                "kind": "StorageV2",
                "sku": {
                    "name": "Standard_LRS",
                    "tier": "Standard"
                },
                "properties": {
                    "encryption": {
                        "services": {
                            "blob": {
                                "enabled": true
                            }
                        },
                        "keySource": "Microsoft.Storage"
                    },
                    "supportsHttpsTrafficOnly": true
                }
            },
            {
                "type": "Microsoft.Storage/storageAccounts/blobServices",
                "apiVersion": "2019-04-01",
                "name": "[concat(variables('storageAccountName'), '/default')]",
                "dependsOn": [
                    "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
                ]
            },
            {
                "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
                "apiVersion": "2019-04-01",
                "name": "[concat(variables('storageAccountName'),'/default/',variables('storageBlobContainerName'))]",
                "dependsOn": [
                    "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]",
                    "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
                ],
                "properties": {
                    "publicAccess": "Blob"
                }
            },
            {
                "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
                "apiVersion": "2018-11-30",
                "name": "[variables('userAssignedIdentityName')]",
                "location": "[resourceGroup().location]"
            },
            {
                "type": "Microsoft.Authorization/roleAssignments",
                "apiVersion": "2020-04-01-preview",
                "name": "[variables('roleAssignmentName')]",
                "dependsOn": [ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName'))]" ],
                "properties": {
                    "roleDefinitionId": "[variables('contributorRoleDefinitionId')]",
                    "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName')), '2015-08-31-preview').principalId]",
                    "scope": "[resourceGroup().id]",
                    "principalType": "ServicePrincipal"
                }
            },
            {
                "type": "Microsoft.Resources/deploymentScripts",
                "apiVersion": "2020-10-01",
                "name": "[variables('deploymentScriptName')]",
                "location": "[resourceGroup().location]",
                "kind": "AzurePowerShell",
                "dependsOn": [
                    "[resourceId('Microsoft.Authorization/roleAssignments', variables('roleAssignmentName'))]",
                    "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('storageAccountName'), 'default', variables('storageBlobContainerName'))]"
                ],
                "identity": {
                    "type": "UserAssigned",
                    "userAssignedIdentities": {
                        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities',variables('userAssignedIdentityName'))]": {}
                    }
                },
                "properties": {
                    "azPowerShellVersion": "3.0",
                    "scriptContent": "
                        Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/appsettings.json' -OutFile 'appsettings.json'
                        $storageAccount = Get-AzStorageAccount -ResourceGroupName 'learndeploymentscript_exercise_1' | Where-Object { $_.StorageAccountName -like 'storage*' }
                        $blob = Set-AzStorageBlobContent -File 'appsettings.json' -Container 'config' -Blob 'appsettings.json' -Context $StorageAccount.Context
                        $DeploymentScriptOutputs = @{}
                        $DeploymentScriptOutputs['Uri'] = $blob.ICloudBlob.Uri
                        $DeploymentScriptOutputs['StorageUri'] = $blob.ICloudBlob.StorageUri
                    ",
                    "retentionInterval": "P1D"
                }
            }
        ],
        "outputs": {
            "fileUri": {
                "type": "string",
                "value": "[reference(variables('deploymentScriptName')).outputs.Uri]"
            }
        }
    }
    
  3. حفظ القالب.

  1. افتح Visual Studio Code، وقم بإنشاء ملف جديد باسم main.bicep.

  2. انسخ قالب البداية التالي إلى main.bicep.

    var storageAccountName = 'storage${uniqueString(resourceGroup().id)}'
    var storageBlobContainerName = 'config'
    var userAssignedIdentityName = 'configDeployer'
    var roleAssignmentName = guid(resourceGroup().id, 'contributor')
    var contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
    var deploymentScriptName = 'CopyConfigScript'
    
    resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
      name: storageAccountName
      tags: {
        displayName: storageAccountName
      }
      location: resourceGroup().location
      kind: 'StorageV2'
      sku: {
        name: 'Standard_LRS'
        tier: 'Standard'
      }
      properties: {
        encryption: {
          services: {
            blob: {
              enabled: true
            }
          }
          keySource: 'Microsoft.Storage'
        }
        supportsHttpsTrafficOnly: true
      }
    
      resource blobService 'blobServices' existing = {
        name: 'default'
      }
    }
    
    resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-04-01' = {
      parent: storageAccount::blobService
      name: storageBlobContainerName
      properties: {
        publicAccess: 'Blob'
      }
    }
    
    resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
      name: userAssignedIdentityName
      location: resourceGroup().location
    }
    
    resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
      name: roleAssignmentName
      properties: {
        roleDefinitionId: contributorRoleDefinitionId
        principalId: userAssignedIdentity.properties.principalId
        principalType: 'ServicePrincipal'
      }
    }
    
    resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
      name: deploymentScriptName
      location: resourceGroup().location
      kind: 'AzurePowerShell'
      identity: {
        type: 'UserAssigned'
        userAssignedIdentities: {
          '${userAssignedIdentity.id}': {}
        }
      }
      properties: {
        azPowerShellVersion: '3.0'
        scriptContent: '''
          Invoke-RestMethod -Uri 'https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/appsettings.json' -OutFile 'appsettings.json'
          $storageAccount = Get-AzStorageAccount -ResourceGroupName 'learndeploymentscript_exercise_1' | Where-Object { $_.StorageAccountName -like 'storage*' }
          $blob = Set-AzStorageBlobContent -File 'appsettings.json' -Container 'config' -Blob 'appsettings.json' -Context $storageAccount.Context
          $DeploymentScriptOutputs = @{}
          $DeploymentScriptOutputs['Uri'] = $blob.ICloudBlob.Uri
          $DeploymentScriptOutputs['StorageUri'] = $blob.ICloudBlob.StorageUri
        '''
        retentionInterval: 'P1D'
      }
      dependsOn: [
        roleAssignment
        blobContainer
      ]
    }
    
    output fileUri string = deploymentScript.properties.outputs.Uri
    
  3. حفظ القالب.

تحديث برنامج PowerShell النصي

نظراً لأن الفريق الآخر قد قام بالعمل الشاق في إنشاء برنامج PowerShell نصي لنسخ ملفات متعددة، قررت استخدام هذا البرنامج النصي في القالب الخاص بك.

تحرير scriptContent في قسم properties لتضمين النص الذي قدمه فريق الشريك.

param([string]$File)
$fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
$storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
$count = 0
$DeploymentScriptOutputs = @{}
foreach ($fileName in $fileList) {
    Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\"
    Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName
    $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
    $DeploymentScriptOutputs[$fileName] = @{}
    $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
    $DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri
    $count++
}
Write-Host \"Finished copying $count files.\"
param([string]$File)
$fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
$storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
$count = 0
$DeploymentScriptOutputs = @{}
foreach ($fileName in $fileList) {
    Write-Host "Copying $fileName to $env:StorageContainerName in $env:StorageAccountName."
    Invoke-RestMethod -Uri "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName" -OutFile $fileName
    $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
    $DeploymentScriptOutputs[$fileName] = @{}
    $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
    $DeploymentScriptOutputs[$fileName]['StorageUri'] = $blob.ICloudBlob.StorageUri
    $count++
}
Write-Host "Finished copying $count files."

إضافة متغير بيئة

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

  1. أضف خاصية environmentVariables إلى قسم properties في البرنامج النصي للنشر.

    "environmentVariables": [
    ],
    
  2. أضف متغير بيئة لـ ResourceGroupName.

    "environmentVariables": [
        {
            "name": "ResourceGroupName",
            "value": "[resourceGroup().name]"
        }
    ],
    
  3. أضف متغير بيئة لـ StorageAccountName.

    "environmentVariables": [
        {
            "name": "ResourceGroupName",
            "value": "[resourceGroup().name]"
        },
        {
            "name": "StorageAccountName",
            "value": "[variables('storageAccountName')]"
        }
    ],
    
  4. أضف متغير بيئة لـ StorageContainerName.

    "environmentVariables": [
        {
            "name": "ResourceGroupName",
            "value": "[resourceGroup().name]"
        },
        {
            "name": "StorageAccountName",
            "value": "[variables('storageAccountName')]"
        },
        {
            "name": "StorageContainerName",
            "value": "[variables('storageBlobContainerName')]"
        }
    ],
    

تلميح

استخدم وظائف القالب للوصول إلى القيم المشتركة مثل [resourceGroup().name] و[variables()].

يتطلب البرنامج النصي الذي اعتمدته بعض متغيرات البيئة. يمكنك تحديدها مباشرة في القالب، ولكن سيكون استخدام متغيرات Bicep للحصول على بعض القيم أكثر مرونة.

  1. أضف خاصية environmentVariables إلى قسم properties في البرنامج النصي للنشر.

    environmentVariables: [
    ]
    
  2. أضف متغير بيئة لـ ResourceGroupName.

    environmentVariables: [
      {
        name: 'ResourceGroupName'
        value: resourceGroup().name
      }
    ]
    
  3. أضف متغير بيئة لـ StorageAccountName.

    environmentVariables: [
      {
        name: 'ResourceGroupName'
        value: resourceGroup().name
      }
      {
        name: 'StorageAccountName'
        value: storageAccountName
      }
    ]
    
  4. أضف متغير بيئة لـ StorageContainerName.

    environmentVariables: [
      {
        name: 'ResourceGroupName'
        value: resourceGroup().name
      }
      {
        name: 'StorageAccountName'
        value: storageAccountName
      }
      {
        name: 'StorageContainerName'
        value: storageBlobContainerName
      }
    ]
    

إضافة معلمة قالب

لتسهيل استخدام الفريقين للقالب، يمكنك إضافة معلمة إلى القالب بحيث يمكن لكل فريق تحديد الملفات التي يريد نسخها.

أضف معلمة إلى القالب لأخذ صفيف من أسماء الملفات.

"parameters": {
    "filesToCopy": {
        "type": "array",
        "metadata": {
            "description": "List of files to copy to application storage account."
        }
    }
},

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

أضف معلمة إلى القالب لأخذ صفيف من أسماء الملفات.

@description('List of files to copy to application storage account.')
param filesToCopy array

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

إضافة وسيطة لتمرير الملفات المراد نسخها

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

تلميح

استخدم وظائف القالب للوصول إلى الوظائف الشائعة مثل [string()] لتحويل القيم من نوع واحد إلى سلسلة.

  1. أضف خاصية arguments إلى البرنامج النصي للنشر. يأخذ برنامج PowerShell النصي معلمة تسمى File، وهي سلسلة من أسماء الملفات التي يجب أن تأتي من معلمة القالب filesToCopy. تأكد من وجود علامات اقتباس حول الوسيطة بأكملها حتى يتم تمريرها بشكل صحيح.

    تنبيه

    تعد خاصية arguments هذه غير صالحة. إذا كنت تستخدم ملحق Azure Resource Manager في Visual Studio Code، فقد يضع علامة على هذا السطر. ستعمل على إصلاح هذه المشكلة في الخطوات التالية.

    "arguments": "[concat( '-File '', string(parameters('filesToCopy')), ''' )]",
    

    تلميح

    قد يكون وضع علامات اقتباس على الأشياء في JSON أمراً صعباً، خاصةً عند تمرير وسيطات سطر الأوامر. يمكنك استخدام متغير قالب لتمثيل حرف يصعب إلغاؤه.

  2. أضف متغير قالب لتمثيل حرف علامة الاقتباس الفردي.

    "variables": {
        "singleQuote": "'",
        "storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]",
        "storageBlobContainerName": "config",
        "userAssignedIdentityName": "configDeployer",
        "roleAssignmentName": "[guid(concat(resourceGroup().id, 'contributor'))]",
        "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
        "deploymentScriptName": "CopyConfigScript"
    },
    
  3. استبدل علامات الاقتباس الفردية في الخاصية arguments بالمتغير الذي حددته للتو.

    "arguments": "[concat( '-File ', variables('singleQuote'), string(parameters('filesToCopy')), variables('singleQuote'))]",
    

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

أضف خاصية arguments إلى البرنامج النصي للنشر. يأخذ برنامج PowerShell النصي معلمة تسمى File، وهي سلسلة من أسماء الملفات التي يجب أن تأتي من معلمة القالب filesToCopy.

arguments: '-File \'${string(filesToCopy)}\''

لاحظ أن ذلك يستخدم العديد من ميزات Bicep:

  • استنتاج السلسلة، لدمج السلاسل.
  • نستخدم حرف الإلغاء \ للسماح لنا بتضمين حرف اقتباس واحد (') داخل السلسلة، لأن اقتباسا واحدا هو عادة حرف محجوز في Bicep.
  • نستخدم الدالة string() لتحويل filesToCopy الصفيف إلى سلسلة.

تحديث إخراج القالب

نظراً لأنك تقوم بتغيير البرنامج النصي للنشر لنشر ملف واحد أو أكثر، فأنت بحاجة إلى تحديث إخراج القالب لتوفير كافة المعلومات الضرورية.

  1. قم بتحديث outputs في القالب لإرجاع العنصر بأكمله، والذي سيكون له معرف موارد منتظم لكل ملف.

    $DeploymentScriptOutputs = @{}
    foreach ($fileName in $fileList) {
        Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\"
        Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName
        $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
        $DeploymentScriptOutputs[$fileName] = @{}
        $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
        $DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri
        $count++
    }
    
  2. أضف إخراجاً آخر باسم حساب التخزين (الذي له معرّف عشوائي). ستستخدمه لاحقاً للتحقق من أن البرنامج النصي للنشر قام بما كنت تتوقعه.

    $DeploymentScriptOutputs = @{}
    foreach ($fileName in $fileList) {
        Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\"
        Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName
        $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
        $DeploymentScriptOutputs[$fileName] = @{}
        $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
        $DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri
        $count++
    }
    
  1. قم بتحديث المخرجات في القالب لإرجاع العنصر بأكمله، والذي سيكون له معرف موارد منتظم لكل ملف.

    output fileUri object = deploymentScript.properties.outputs
    
  2. أضف إخراجاً آخر باسم حساب التخزين (الذي له معرّف عشوائي). ستستخدمه لاحقاً للتحقق من أن البرنامج النصي للنشر قام بما كنت تتوقعه.

    output storageAccountName string = storageAccountName
    

التحقق من القالب

يجب أن يبدو القالب مشابهاً لما يلي:

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.1",
    "apiProfile": "",
    "parameters": {
        "filesToCopy": {
            "type": "array",
            "defaultValue": [ "appsettings.json" ],
            "metadata": {
                "description": "List of files to copy to application storage account."
            }
        }
    },
    "variables": {
        "singleQuote": "'",
        "storageAccountName": "[concat('storage', uniqueString(resourceGroup().id))]",
        "storageBlobContainerName": "config",
        "userAssignedIdentityName": "configDeployer",
        "roleAssignmentName": "[guid(concat(resourceGroup().id, 'contributor'))]",
        "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
        "deploymentScriptName": "CopyConfigScript"
    },
    "functions": [],
    "resources": [
        {
            "name": "[variables('storageAccountName')]",
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-06-01",
            "tags": {
                "displayName": "[variables('storageAccountName')]"
            },
            "location": "[resourceGroup().location]",
            "kind": "StorageV2",
            "sku": {
                "name": "Standard_LRS",
                "tier": "Standard"
            },
            "properties": {
                "encryption": {
                    "services": {
                        "blob": {
                            "enabled": true
                        }
                    },
                    "keySource": "Microsoft.Storage"
                },
                "supportsHttpsTrafficOnly": true
            }
        },
        {
            "type": "Microsoft.Storage/storageAccounts/blobServices",
            "apiVersion": "2019-04-01",
            "name": "[concat(variables('storageAccountName'), '/default')]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
            ]
        },
        {
            "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
            "apiVersion": "2019-04-01",
            "name": "[concat(variables('storageAccountName'),'/default/',variables('storageBlobContainerName'))]",
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('storageAccountName'), 'default')]",
                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
            ],
            "properties": {
                "publicAccess": "Blob"
            }
        },
        {
            "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
            "apiVersion": "2018-11-30",
            "name": "[variables('userAssignedIdentityName')]",
            "location": "[resourceGroup().location]"
        },
        {
            "type": "Microsoft.Authorization/roleAssignments",
            "apiVersion": "2020-04-01-preview",
            "name": "[variables('roleAssignmentName')]",
            "dependsOn": [ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName'))]" ],
            "properties": {
                "roleDefinitionId": "[variables('contributorRoleDefinitionId')]",
                "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('userAssignedIdentityName')), '2015-08-31-preview').principalId]",
                "scope": "[resourceGroup().id]",
                "principalType": "ServicePrincipal"
            }
        },
        {
            "type": "Microsoft.Resources/deploymentScripts",
            "apiVersion": "2020-10-01",
            "name": "[variables('deploymentScriptName')]",
            "location": "[resourceGroup().location]",
            "kind": "AzurePowerShell",
            "dependsOn": [
                "[resourceId('Microsoft.Authorization/roleAssignments', variables('roleAssignmentName'))]",
                "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('storageAccountName'), 'default', variables('storageBlobContainerName'))]"
            ],
            "identity": {
                "type": "UserAssigned",
                "userAssignedIdentities": {
                    "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities',variables('userAssignedIdentityName'))]": {}
                }
            },
            "properties": {
                "arguments": "[concat( '-File ', variables('singleQuote'), string(parameters('filesToCopy')), variables('singleQuote'))]",
                "environmentVariables": [
                    {
                        "name": "ResourceGroupName",
                        "value": "[resourceGroup().name]"
                    },
                    {
                        "name": "StorageAccountName",
                        "value": "[variables('storageAccountName')]"
                    },
                    {
                        "name": "StorageContainerName",
                        "value": "[variables('storageBlobContainerName')]"
                    }
                ],
                "azPowerShellVersion": "3.0",
                "scriptContent": "
                    param([string]$File)
                    $fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
                    $storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
                    $count = 0
                    $DeploymentScriptOutputs = @{}
                    foreach ($fileName in $fileList) {
                        Write-Host \"Copying $fileName to $env:StorageContainerName in $env:StorageAccountName.\"
                        Invoke-RestMethod -Uri \"https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName\" -OutFile $fileName
                        $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
                        $DeploymentScriptOutputs[$fileName] = @{}
                        $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
                        $DeploymentScriptOutputs[$fileName]['StorageUri'] =$blob.ICloudBlob.StorageUri
                        $count++
                    }
                    Write-Host \"Finished copying $count files.\"                    
                ",
                "retentionInterval": "P1D"
            }
        }
    ],
    "outputs": {
        "fileUri": {
            "type": "object",
            "value": "[reference(variables('deploymentScriptName')).outputs]"
        },
        "storageAccountName": {
            "type": "string",
            "value": "[variables('storageAccountName')]"
        }
    }
}
@description('List of files to copy to application storage account.')
param filesToCopy array = [
  'appsettings.json'
]

var storageAccountName = 'storage${uniqueString(resourceGroup().id)}'
var storageBlobContainerName = 'config'
var userAssignedIdentityName = 'configDeployer'
var roleAssignmentName = guid(resourceGroup().id, 'contributor')
var contributorRoleDefinitionId = resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
var deploymentScriptName = 'CopyConfigScript'

resource storageAccount 'Microsoft.Storage/storageAccounts@2019-06-01' = {
  name: storageAccountName
  tags: {
    displayName: storageAccountName
  }
  location: resourceGroup().location
  kind: 'StorageV2'
  sku: {
    name: 'Standard_LRS'
    tier: 'Standard'
  }
  properties: {
    encryption: {
      services: {
        blob: {
          enabled: true
        }
      }
      keySource: 'Microsoft.Storage'
    }
    supportsHttpsTrafficOnly: true
  }

  resource blobService 'blobServices' existing = {
    name: 'default'
  }
}

resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2019-04-01' = {
  parent: storageAccount::blobService
  name: storageBlobContainerName
  properties: {
    publicAccess: 'Blob'
  }
}

resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
  name: userAssignedIdentityName
  location: resourceGroup().location
}

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = {
  name: roleAssignmentName
  properties: {
    roleDefinitionId: contributorRoleDefinitionId
    principalId: userAssignedIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
  name: deploymentScriptName
  location: resourceGroup().location
  kind: 'AzurePowerShell'
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${userAssignedIdentity.id}': {}
    }
  }
  properties: {
    arguments: '-File \'${string(filesToCopy)}\''
    environmentVariables: [
      {
        name: 'ResourceGroupName'
        value: resourceGroup().name
      }
      {
        name: 'StorageAccountName'
        value: storageAccountName
      }
      {
        name: 'StorageContainerName'
        value: storageBlobContainerName
      }
    ]
    azPowerShellVersion: '3.0'
    scriptContent: '''
      param([string]$File)
      $fileList = $File -replace '(\[|\])' -split ',' | ForEach-Object { $_.trim() }
      $storageAccount = Get-AzStorageAccount -ResourceGroupName $env:ResourceGroupName -Name $env:StorageAccountName -Verbose
      $count = 0
      $DeploymentScriptOutputs = @{}
      foreach ($fileName in $fileList) {
          Write-Host "Copying $fileName to $env:StorageContainerName in $env:StorageAccountName."
          Invoke-RestMethod -Uri "https://raw.githubusercontent.com/Azure/azure-docs-json-samples/master/mslearn-arm-deploymentscripts-sample/$fileName" -OutFile $fileName
          $blob = Set-AzStorageBlobContent -File $fileName -Container $env:StorageContainerName -Blob $fileName -Context $storageAccount.Context
          $DeploymentScriptOutputs[$fileName] = @{}
          $DeploymentScriptOutputs[$fileName]['Uri'] = $blob.ICloudBlob.Uri
          $DeploymentScriptOutputs[$fileName]['StorageUri'] = $blob.ICloudBlob.StorageUri
          $count++
      }
      Write-Host "Finished copying $count files."
    '''
    retentionInterval: 'P1D'
  }
  dependsOn: [
    roleAssignment
    blobContainer
  ]
}

output fileUri object = deploymentScript.properties.outputs
output storageAccountName string = storageAccountName

إذا لم يحدث ذلك، إما أن تنسخ المثال أو تضبط القالب لمطابقة المثال.

إنشاء ملف معلمات

الآن بعد أن حصلت على مجموعة القوالب، يمكنك التحقق من صحة البرنامج النصي للنشر الجديد باستخدام ملف معلمات مع تحديد ملفات جديدة.

  1. أنشئ ملف azuredeploy.parameters.json يدوياً أو استخدم ملحق VS Code للقيام بذلك.

  2. تحرير الملف ليتم تحديد اثنين من filesToCopy:

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "filesToCopy": {
                "value": [
                    "swagger.Staging.json",
                    "appsettings.Staging.json"
                ]
            }
        }
    }
    
  1. أنشئ ملف azuredeploy.parameters.json.

  2. تحرير الملف ليتم تحديد اثنين من filesToCopy:

    {
        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
        "contentVersion": "1.0.0.0",
        "parameters": {
            "filesToCopy": {
                "value": [
                    "swagger.Staging.json",
                    "appsettings.Staging.json"
                ]
            }
        }
    }
    

نشر القالب

إنشاء مجموعة موارد للتمرين

يجب إنشاء مجموعة موارد لاحتواء الموارد التي ستنشئها كجزء من هذا التمرين. باستخدام مجموعة موارد جديدة، ستجعل عملية التنظيف بعد التمرين أمراً أسهل.

من الوحدة الطرفية في Visual Studio Code، قم بتشغيل هذا الأمر لإنشاء مجموعة الموارد لهذا التمرين.

resourceGroupName="learndeploymentscript_exercise_2"
az group create --location eastus --name $resourceGroupName
$resourceGroupName = 'learndeploymentscript_exercise_2'
New-AzResourceGroup -Location eastus -Name $resourceGroupName

نشر القالب إلى Azure

نشر القالب باستخدام أوامر CLI Azure في محطة Visual Studio Code.

templateFile="azuredeploy.json"
templateParameterFile="azuredeploy.parameters.json"
today=$(date +"%d-%b-%Y")
deploymentName="deploymentscript-"$today

az deployment group create \
    --resource-group $resourceGroupName \
    --name $deploymentName \
    --template-file $templateFile \
    --parameters $templateParameterFile

نشر القالب إلى Azure

نشر القالب باستخدام أوامر Azure PowerShell في الوحدة الطرفية.

$templateFile = 'azuredeploy.json'
$templateParameterFile = 'azuredeploy.parameters.json'
$today = Get-Date -Format 'MM-dd-yyyy'
$deploymentName = "deploymentscript-$today"

New-AzResourceGroupDeployment `
    -ResourceGroupName $resourceGroupName `
    -Name $deploymentName `
    -TemplateFile $templateFile `
    -TemplateParameterFile $templateParameterFile

نشر القالب إلى Azure

نشر القالب باستخدام أوامر CLI Azure في محطة Visual Studio Code.

templateFile="main.bicep"
templateParameterFile="azuredeploy.parameters.json"
today=$(date +"%d-%b-%Y")
deploymentName="deploymentscript-"$today

az deployment group create \
    --resource-group $resourceGroupName \
    --name $deploymentName \
    --template-file $templateFile \
    --parameters $templateParameterFile

نشر القالب إلى Azure

نشر القالب باستخدام أوامر Azure PowerShell في الوحدة الطرفية.

$templateFile = 'main.bicep'
$templateParameterFile = 'azuredeploy.parameters.json'
$today = Get-Date -Format 'MM-dd-yyyy'
$deploymentName = "deploymentscript-$today"

New-AzResourceGroupDeployment `
    -ResourceGroupName $resourceGroupName `
    -Name $deploymentName `
    -TemplateFile $templateFile `
    -TemplateParameterFile $templateParameterFile

مراجعة نتيجة القالب

بعد اكتمال النشر، يمكنك التحقق من نسخ كلا الملفين إلى حساب التخزين الخاص بك عن طريق سرد محتويات حاوية الكائنات الثنائية كبيرة الحجم.

  1. سرد محتويات حاوية الكائنات الثنائية كبيرة الحجم.

    storageAccountName=$(az deployment group show --resource-group $resourceGroupName --name $deploymentName --query 'properties.outputs.storageAccountName.value' --output tsv)
    az storage blob list --account-name $storageAccountName --container-name config --query '[].name'
    

    يقوم الأمر بإرجاع التعليمات البرمجية التالية:

    [
      "swagger.Staging.json",
      "appsettings.Staging.json"
    ]
    
  2. يمكنك أيضًا مراجعة السجلات (والتفاصيل الأخرى حول التوزيع) من مدخل Azure أو باستخدام الأمر التالي.

    az deployment-scripts show-log --resource-group $resourceGroupName --name CopyConfigScript
    
  1. سرد محتويات حاوية الكائنات الثنائية كبيرة الحجم.

    $storageAccountName = (Get-AzResourceGroupDeployment -ResourceGroupName $resourceGroupName -Name $deploymentName).Outputs.storageAccountName.Value
    $storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroupName
    Get-AzStorageBlob -Context $storageAccount.Context -Container config |
        Select-Object Name
    

    يقوم الأمر بإرجاع التعليمات البرمجية التالية:

    Name
    ----
    swagger.Staging.json
    appsettings.Staging.json
    
  2. يمكنك أيضًا مراجعة السجلات (والتفاصيل الأخرى حول التوزيع) من مدخل Azure أو باستخدام الأمر التالي.

    Get-AzDeploymentScriptLog -ResourceGroupName $resourceGroupName -Name CopyConfigScript
    

تنظيف مجموعة الموارد

لقد نجحت في نشر قالب ARM من خلال برنامج نصي للنشر واستخدمت أساليب مختلفة لتمرير البيانات لتخصيص سلوكه. يمكنك إزالة مجموعة الموارد التي تحتوي على جميع الموارد وتعيينات الأدوار التي قمت بإنشائها.

az group delete --name $resourceGroupName
Remove-AzResourceGroup -Name $resourceGroupName