Azure VM custom script extension SAS token support - azure

I am trying to deploy add a custom script extension to an Azure VM using an ARM template, and I want to have it download files from a storage account using a SAS token.
Here is the template (simplified):
{
"name": "CustomScriptExtension"
"type": "Microsoft.Compute/virtualMachines/extensions",
"location": "eastus",
"properties": {
"publisher": "Microsoft.Compute",
"type": "CustomScriptExtension",
"typeHandlerVersion": "1.8",
"settings": {
"fileUris": [
"https://{storage-account}.blob.core.windows.net/installers/{installer}.msi?sv=2015-04-05&sig={signature}&st=2017-05-03T05:18:28Z&se=2017-05-10T05:18:28Z&srt=o&ss=b&sp=r"
],
"commandToExecute": "start /wait msiexec /package {installer}.msi /quiet"
},
}
}
And deploying it results in this error:
{
"name": "CustomScriptExtension",
"type": "Microsoft.Compute.CustomScriptExtension",
"typeHandlerVersion": "1.8",
"statuses": [
{
"code": "ProvisioningState/failed/3",
"level": "Error",
"displayStatus": "Provisioning failed",
"message": "Failed to download all specified files. Exiting. Error Message: Missing mandatory parameters for valid Shared Access Signature"
}
]
}
If I hit the URL with the SAS token directly it pulls down the file just fine so I know the SAS token is correct. Does the custom script extension not support URLs with SAS tokens?

I figured it out, this must be a bug in the custom script extension which causes it to not support storage account level SAS tokens. If I add &sr=b on the the end of the SAS token (which isn't part of the storage account level SAS token spec) it starts working.
I found this info here:
https://azureoperations.wordpress.com/2016/11/21/first-blog-post/

As #4c74356b41 said. Now, customer script extension template does not support SAS tokens. If you want to download file from a private storage account, you could use storage account key. Please refer to this example.
{
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(variables('vmName'),'/', variables('extensionName'))]",
"apiVersion": "[variables('apiVersion')]",
"location": "[resourceGroup().location]",
"dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]"
],
"properties": {
"publisher": "Microsoft.Azure.Extensions",
"type": "CustomScript",
"typeHandlerVersion": "2.0",
"autoUpgradeMinorVersion": true,
"settings": {
"fileUris": "[split(parameters('fileUris'), ' ')]",
"commandToExecute": "[parameters('commandToExecute')]"
},
"protectedSettings": {
"storageAccountName": "[parameters('customScriptStorageAccountName')]",
"storageAccountKey": "[parameters('customScriptStorageAccountKey')]"
}
}
}

Currently, there is support for SAS token in VM Extension

No, it does not support SAS tokens. Refer to this feedback item:
https://github.com/Azure/azure-linux-extensions/issues/105

Related

installing app from a Storageacct via template.json file... we need the storageName and Key to be passed through a parameter so its not hardcoded

ive been banging my head against the wall about this one as i cannot figure out how to get the StorageAccountName and Key to be passed to the template from a parameters file.
i am struggling to find any clear guide or examples of this...
what were trying to achieve is this.
we need Sophos AV to be installed as part of a VM deployment via the arm template.
when the storage account details are hardcoded into the template file the arm template works and sophos.ps1 (which holds the install stuff runs)
im very close to finishing a custom deployment script for us but im stuck on this thing as I dont want sensitive information like the storage account key hardcoded into the template.
the snippet of the template file below has the storageaccountname and key hardcoded into the template.
updated
added defaultvalue for storageAccountResourceGroupName
added storageAccountResourceGroupName to storageAccountName
updated storageAccountName
template.json
"parameters": {
"storageAccountResourceGroupName":{
"type": "string",
"defaultValue": "RSG2"
"storageAccountName": {
"type": "string",
"defaultValue": "filesstgacct"
{
"type": "Microsoft.Compute/virtualMachines/extensions",
"name": "[concat(parameters('virtualMachineName'), '/MyCustomScriptExtension')]",
"apiVersion": "2015-05-01-preview",
"location": "[parameters('location')]",
"dependsOn": [ "[concat('Microsoft.Compute/virtualMachines/',parameters('virtualMachineName'))]" ],
"tags": "[parameters('tags')]",
"properties": { "publisher": "Microsoft.Compute",
"type": "CustomScriptExtension", "typeHandlerVersion": "1.3", "autoUpgradeMinorVersion": true,
"settings": {
"fileUris": [ "https://storage-account-name.blob.core.windows.net/scripts/sophos.ps1"
]
},
"protectedSettings": {
"commandToExecute": "powershell.exe -ExecutionPolicy Unrestricted -File sophos.ps1",
"storageAccountName": "[parameters('storageAccountName')]",
"storageAccountKey": "[listKeys(resourceid(parameters['storageAccountResourceGroupName'], 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]"
}
}
}
can the storageaccountname and key be stored in the parameters file?
if yes then how do i link the 2 together?
Yes. I believe the reason you're not able to read the value is because you're not specifying the parameters properly. Please try something like:
"parameters": {
"storageAccountName": {
"type": "string",
"defaultValue": "storage-account-name"
},
"storageAccountKey": {
"type": "string",
"defaultValue": "storage-account-key"
}
}
alternatively is it possible to refences the storage account key
directly from the template/parameter file meaning the key is never
hardcoded?
Actually this is the recommended way that you do not hardcode the storage account key in your template file. What you can do is fetch the key dynamically using listKeys template function. All you have to do is provide the complete resource id of your storage account and API version.
You would probably use something like:
"storageAccountKey": "[listKeys(resourceid(resourceGroup().name, 'Microsoft.Storage/storageAccounts', parameters('storageAccountName')), providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]).keys[0].value]"
and ARM template processor will automatically fetch the account key for your storage account.

ARM template for Azure Function ignores preWarmedInstanceCount setting

I'm deploying Azure function into Premium Function Plan (Elastic) using Azure powershell script:
New-AzResourceGroupDeployment -ResourceGroupName $RESOURCE_GROUP -TemplateFile "function-app.json" -TemplateParameterObject $params -Name $APP_SERVICE_NAME -Mode Incremental > $null
And deployment ignores my preWarmedInstanceCount setting. Newly created function has Always Ready Instances = 0 (see screenshot)
ARM template of function:
{
"apiVersion": "2020-06-01",
"name": "[parameters('siteName')]",
"type": "Microsoft.Web/sites",
"identity": {
"type": "systemAssigned"
},
"kind": "functionapp",
"location": "[resourceGroup().location]",
"properties": {
"name": "[parameters('siteName')]",
"serverFarmId": "[resourceId(parameters('appServicePlanRg'),'Microsoft.Web/serverfarms',parameters('appServicePlanName'))]",
"clientAffinityEnabled": false,
"siteConfig": {
"use32BitWorkerProcess": false,
"preWarmedInstanceCount": 2,
"appSettings": [
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~3"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "DefaultEndpointsProtocol=https;AccountName=xxx..."
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "asggas"
}
]
}
}
}
It seems to me few days ago it did worked properly and I managed to set that value via arm template and now I can only update it via Azure portal.
Here is ARM template of my hosting plan:
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2018-02-01",
"name": "[parameters('appServicePlanName')]",
"location": "[resourceGroup().location]",
"properties": {
"name": "[parameters('appServicePlanName')]",
"workerSize": "1",
"numberOfWorkers": "1",
"maximumElasticWorkerCount": 20
},
"sku": {
"Tier": "ElasticPremium",
"Name": "EP2"
}
}
After some investigation I've come to "Activity Log" (after manual update on portal) screen and was surprised that my desired property is named as "minimumElasticInstanceCount" (it is not documented anywhere in either ARM API version https://learn.microsoft.com/en-us/azure/templates/microsoft.web/sites)
Then I added this field to my ARM template and all looks good for now. Also some explanation and difference between "Always Ready Instances"(minimumElasticInstanceCount) and "Pre-warmed instances"(preWarmedInstanceCount) are posted here: https://learn.microsoft.com/uk-ua/azure/azure-functions/functions-premium-plan
So in Azure Portal Pre-warmed instance setting is not displayed. And I was looking for another setting.
I have tried to replicate the issue and was able to figure out the issue here.
Instead of "preWarmedInstanceCount" try "reservedInstanceCount".
You can check from the Azure portal itself. Once you have updated the reserved instance count from the portal you can export the template :
Then compare your previous template with the exported one, you will be able to see the difference.

Azure Storage blob container assign RBAC using ARM

We currently have ARM templates that create storage accounts and containers in a solution however I can't seem to manage to assign the RBAC access to the container in the ARM template. I have tried using Erik's solution here
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2017-09-01",
"name": "[concat(parameters('storageAccountName'),'/default/filedrop/Microsoft.Authorization/{NEW GUID}')]",
"properties": {
"roleDefinitionId": "ba92f5b4-2d11-453d-a403-e96b0029c9fe",
"principalId": "[parameters('ServicePrincipalId')]"
}
The error I get is "error": {
"code": "BadRequestFormat",
"message": "The request was incorrectly formatted."
}
Anyone see where I'm going wrong?
Here is what I used: https://github.com/juunas11/managedidentity-filesharing/blob/8410ed3f3d4061de7d40531c025bf6e474489135/Joonasw.ManagedIdentityFileSharingDemo.ARM/azuredeploy.json#L223-L236
{
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
"apiVersion": "2018-01-01-preview",
"name": "[concat(parameters('storageAccountName'), '/default/', parameters('storageContainerName'), '/Microsoft.Authorization/', guid(resourceGroup().id, 'webAppFilesAccess'))]",
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]",
"[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('storageContainerName'))]",
"[resourceId('Microsoft.Web/sites', parameters('webAppName'))]"
],
"properties": {
"principalId": "[reference(resourceId('Microsoft.Web/sites', parameters('webAppName')), '2016-08-01', 'Full').identity.principalId]",
"roleDefinitionId": "[variables('storageBlobContributorRoleId')]"
}
}
The main difference I can see is that I have a higher API version + I use parameters for a lot of things.
The guid() function is pretty handy since you can give it some text, and if the text is same, it'll give the same GUID every time.

How do I use Azure Key Vault secret in linked template

I'm trying to create automation variable off KeyVault secret. I assume I can probably do the same thing what is currently done in main template for retrieving windows password but it fails with non-descriptive error below. Not sure what shall be done next to troubleshoot.
Error
{
"code": "BadRequest",
"message": "{\"Message\":\"The request is invalid.\",\"ModelState\":{\"variable.properties.value\":[\"An error has occurred.\"]}}"
}
Template
{
"name": "mystring",
"type": "variables",
"apiVersion": "2015-10-31",
"dependsOn": [
"[concat('Microsoft.Automation/automationAccounts/', parameters('AutomationAccountName'))]"
],
"properties": {
"value": {
"reference": {
"keyVault": {
"id": "[resourceId(subscription().subscriptionId, 'Utility-RG', 'Microsoft.KeyVault/vaults', 'MyKeyVault')]"
},
"secretName": "WindowsPasswordSecret"
}
},
"description": "test var",
"isEncrypted": false
}
}
That error is indeed helpful, while I have no idea what went wrong there, I can tell you how to work around that, you need to pass the data from the KV to the template (as input parameter) not to the resource. And in the template use parameter to assign value to the object in question.
Reference: https://github.com/4c74356b41/bbbb-is-the-word/blob/master/_arm/parent.json#L151

ARM template - website deployment failure

I’m attempting to use Azure Resource manager (ARM) template files to deploy as ASP.net website and am hitting a roadblock. This is a nascent feature of Azure so there isn’t much know-how out on the web about it, hoping someone here can help instead.
I can successfully create a new site (i.e. a Microsoft.Web/sites resource) in a new resource group i.e. it works when I define a website in the ARM template like so:
{
"apiVersion": "2014-06-01",
"name": "[parameters('siteName')]",
"type": "Microsoft.Web/sites",
"location": "[parameters('siteLocation')]",
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "Website"
},
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"name": "[parameters('siteName')]",
"serverFarm": "[parameters('hostingPlanName')]"
}
}
My problem comes when I try to deploy an ASP.net website into it. Here’s what I have added to my ARM template:
{
"apiVersion": "2014-06-01",
"name": "[parameters('siteName')]",
"type": "Microsoft.Web/sites",
"location": "[parameters('siteLocation')]",
"tags": {
"[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]": "Resource",
"displayName": "Website"
},
"dependsOn": [
"[concat('Microsoft.Web/serverfarms/', parameters('hostingPlanName'))]"
],
"properties": {
"name": "[parameters('siteName')]",
"serverFarm": "[parameters('hostingPlanName')]"
},
"resources": [
{
"apiVersion": "2014-06-01",
"type": "extensions",
"name": "MSDeploy",
"dependsOn": [ "[concat('Microsoft.Web/sites/', parameters('siteName'))]" ],
"properties": {
"connectionString": "",
"dbType": "",
"packageUri": "file:///D:/svn/dh.PSP.Conductor/dh.PSP.Conductor.AzureResourceGroup/obj/Release/ProjectReferences/dh.PSP.Conductor.Api/package.zip"
}
}
]
}
I’m deploying from PowerShell and it fails with:
New-AzureResourceGroup : 16:00:35 - Resource
Microsoft.Web/sites/extensions 'ARMTest20150604/MSDeploy' failed with
message 'The resource operation completed with terminal provisioning
state 'Failed'.'
If I look in the portal I see a slightly more useful error:
statusCode:Conflict
statusMessage:{"status":"Failed","error":{"code":"ResourceDeploymentFailure","message":"The
resource operation completed with terminal provisioning state
'Failed'."}}
I’m none the wiser as to why this is failing however. Can anyone suggest how I might investigate further?
Fault is mine (as you might expect). Its not possible to reference a local file for the packageUri property, the file needs to be uploaded to blob storage first.
Something else useful I've found out, a deployment log is available by browsing to https://websitename.scm.azurewebsites.net/DebugConsole, "cd logfiles\siteextensions\msdeploy", open appManagerLog.xml. Much more useful information in there. In my case:
<entry time="2015-06-04T15:28:12.0718158+00:00" type="Error">
<message>AppGallery Deploy Failed: 'System.UriFormatException: Invalid URI: The URI is empty.
at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
at System.Uri..ctor(String uriString)
at Microsoft.Web.Deployment.WebApi.AppGalleryPackage.IsPremiumApp()
at Microsoft.Web.Deployment.WebApi.DeploymentController.CheckCanDeployIfAppIsPremium(AppGalleryPackageInfo packageInfo, Boolean&amp; isPremium)'</message>
</entry>
<entry time="2015-06-04T15:28:12.1186872Z" type="Message">
<message>Downloading package path 'D:\svn\dh.PSP.Conductor\dh.PSP.Conductor.AzureResourceGroup\obj\Release\ProjectReferences\dh.PSP.Conductor.Api\package.zip' from blob ''</message>
</entry>
<entry time="2015-06-04T15:28:12.1186872Z" type="Error">
<message>Failed to download package.</message>
</entry>

Resources