recently I started learning/working with ARM templates and JSON so I'm a complete newbie to this. I've been asked to make a template that creates a virtual machine selecting an existing virtual network and subnet within a subscription.
Everything works fine, except that whenever I make the deployment, the template creates a new vnet and subnet with randomized names instead of letting me pick from an existing one (the VM creates correctly though).
I used https://github.com/Azure/azure-quickstart-templates/blob/master/101-vm-simple-rhel/azuredeploy.json quickstart template as a base and added a few lines (which I will put below) to let me type the name of my vnet and subnet as it does with the VM name, but it keeps creating new ones even though I type the name correctly.
The lines I added to the code in the Parameters section are:
"virtualNetworkName": {
"type": "string",
"metadata": {
"description": "VNet to which the VM will connect."
}
},
"subnetName": {
"type": "string",
"metadata": {
"description": "Subnet to which the VM will connect."
}
}
Thank you in advance for your time!
To create a VM with the existing VNet base on the quickstart template you used, you only need to delete the virtual network resource in the resources block and the dependency on it and all the variables about the VNet and subnet except the variable subnetRef, then change this variable with your parameters like this if the VNet in the same resource group with the VM:
"subnetRef": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]",
If the existing VNet in another resource group but in the same subscription, then the variable subnetRef should be changed like this:
"subnetRef": "[resourceId('otherResourceGroup', 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]",
According to the changes, the template will use the existing VNet and subnet instead of creating new ones.
Take a look at this sample:
https://github.com/Azure/azure-quickstart-templates/tree/master/100-marketplace-sample
It shows how you can use a pattern for new/existing/none on resources in a template.
Related
I am trying to build a packer image on azure. How do i Define Vnet and VM IP Ranges for Temporary packer Resource group(Which packer temporarily creates and destroys(Vnet,vm))to take a snapshot of the vm? I wanted to make sure that the default IP's Used by packer will not overlap with my exisiting IP's in the azure subscription
You can specify the options inside of your packer template to use an existing resource group and virtual network. See example below
{
"builders": [{
"type": "azure-arm",
"client_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx",
"client_secret": "ppppppp-pppp-pppp-pppp-ppppppppppp",
"tenant_id": "zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz",
"subscription_id": "yyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyy",
// These will allow you to select existing resources
"build_resource_group_name": "my-existing-rg" // name of existing resource group
"virtual_network_name": "my-existing-vnet" // name of existing vnet
"virtual_network_subnet_name": "mySubnetName" // name of subnet inside vnet
"private_virtual_network_with_public_ip": true // apply public IP to VM (optional)
// Continue with rest of configuration
}
See https://www.packer.io/docs/builders/azure.html for reference
I live in an enterprise environment where most of our production domains are currently non-routable (e.g. .local).
I tried extending the schema but since the non-routable cannot be verified and the default .onmicrosoft I don't think could either. My enterprise allows me to easily create subdomains so I attached it and verified for testing purposes and ran into the same verified domain error.
Per the documentation, I should be able to either us the ID of my domain name or just the scheme name and get 8 random-alpha-chars added. Neither approach works in this case.
POST: https://graph.microsoft.com/v1.0/schemaExtensions
{
"id": "idmdomain.sub.domain.net_Owners",
"description": "Owners of the group",
"targetTypes": [
"Group"
],
"properties": [{
"name": "PrimaryOwners",
"type": "String"
},
{
"name": "SecondaryOwners",
"type": "String"
}
]
}
Message Received:
{
"code": "BadRequest",
"message": "Your organization must own the namespace idmdomain.sub.domain.net as a part of one of the verified domains.",
"request-id": "1c7363f9-d54b-408a-8b29-2c0d2a94280a",
"date": "2018-03-22T21:47:22"
}
From the documentation:
If you already have a vanity .com,.net, .gov, .edu or a .org domain that you have verified with your tenant, you can use the domain name along with the schema name to define a unique name, in this format {domainName}_{schemaName}.
For example, if your vanity domain is contoso.com, you can define an id of, contoso_mySchema. This is the preferred option.
So in your example, idmdomain.sub.domain.net_Owners should simply be domain_Owners. It shouldn't include idmdomain, sub, net or any ..
Thank you Marc for pointing me in the correct direction. Even though my app had the correct delegated permissions (Directory.AccessAsUser.All) I now understand that I needed to execute this change in the user context instead of application as application is not supported.
For those that come behind me {domainName}_{schemaName} works if you validate your domain, if dont and you just leave schemename then the generated guid works as documented. I recommended reviewing the two links below as they were what finally unlocked the puzzle for me.
Helped me understand how this is working (authentication vs authorization)
https://developer.microsoft.com/en-us/graph/docs/concepts/rest
Helped me setup postman to quickly validate
https://blogs.msdn.microsoft.com/softwaresimian/2017/10/05/using-postman-to-call-the-graph-api-using-azure-active-directory-aad/
I should add for the postman route, a few changes...
Auth URL
https://login.microsoftonline.com/yourtennantid/oauth2/authorize?resource=https%3A%2F%2Fgraph.microsoft.com
Access Token URL
https://login.microsoftonline.com/yourtennantid/oauth2/token
Scope = Directory.AccessAsUser.All
Is there a way to reference a VM name that is deployed in VM Scale Set? I'm using custom script extension that runs install script when each VM in scale set is deployed and for one of the parameters of the script I want to use a name of the VM. For single instance it was easy:
"commandToExecute": "[concat('sh ap-cluster-setup.sh -h=',parameters('virtualMachineName'),' -s=',parameters('subnetAddressPrefix'),'')]"
but since VM name in scale set is created dynamically when it is deployed I can't think of a way to reference it in JSON. The VM naming convention is vmssTemplate_0, vmssTemplate_1, etc. where vmssTemplate is parameters('virtualMachineScaleSets_name') in VMSS template.
There's no way to do this in the template itself since the same parameters get passed to each VM on script execution. However, You should be able to figure out the VM name from within the script. For instance, on a Linux machine you could use the hostname command, which will return the VM name. Does this meet your needs?
where vmssTemplate is parameters('virtualMachineScaleSets_name') in
VMSS template.
We can find where vmss template define the Virtual machine's name.
computernameprefix:
"osProfile": {
"computerNamePrefix": "[variables('namingInfix')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
and the 'namingInfix' define here:
"namingInfix": "[toLower(substring(concat(parameters('vmssName'), uniqueString(resourceGroup().id)), 1, 9))]",
By default the VMSS with autoscale settings, and the vm's name should be create automatically.
If you want to get the VMs' name, we can use powershell to get it:
PS C:\windows> Get-AzureRmVmssVM -ResourceGroupName YOUR_RESOURCE_NAME -VMScaleSetName YOUR_VMSS_NAME
Also you can add some script (to get the name of your VMs) in your script, then upload the script to custom script extension.
We deploy azure resources using an ARM template as part of our build process before deploying the actual application.
So far all our application resources are self contained within a resource group. e.g. A web app that requires a sql server and a storage account are clubbed into one resource group.
However we have come across a scenario/need where we need to share a resource eg. storage account across resource groups. Resource Group A has the storage account and Resource Group B's web app requires the connection string/app keys pertaining to the storage account in its appconfig.json/web.config.
Question
How do I build the connection string for the app in resource group B to connect to a resource in resource group A as I need to obtain the Id of the resource group A in B
Here is how i build the connection string if they are in the same resource group
"variables"
{
"storageAccounts_id": "[concat(**resourceGroupA**().id,'/providers/Microsoft.Storage/storageAccounts/', variables('storageAccntName'))]",
},
"resources": [
{
"apiVersion": "2015-08-01",
"type": "config",
"name": "connectionstrings",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('MyWebSiteName'))]"
],
"properties": {
"AzureWebJobsDashboard": {
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccntName'),';AccountKey=',concat(listKeys(variables('storageAccounts_id'),'2015-05-01-preview').key1))]",
"type": "Custom"
},
}
}
]
Notes:
I did go through this site https://azure.microsoft.com/en-us/documentation/articles/resource-group-linked-templates/ about linked templates, but it does not suit our current build process which uses Octo (unless there is something I may be missing) which deployes the ARM first then the application (web).
For this scenario where the storage account name is known and does not depend on the resource group (eg, uniqueString(resourceGroup().id)), then you can simply use the longer form for resourceId(). The full form looks like:
resourceId([subscriptionId], [resourceGroupName], resourceType, resourceName1, [resourceName2]...)
so we can optionally supply subscriptionId and resourceGroupName.
listKeys(resourceId(parameters('ResourceGroupAName'), 'Microsoft.Storage/storageAccounts', variables('ccPaymentStorageName'))
If it was in a different subscription, you could also specify the subscription.
listKeys(resourceId(parameters('SubscriptionId'), parameters('ResourceGroupAName'), 'Microsoft.Storage/storageAccounts', variables('ccPaymentStorageName'))
If your storage account name depends on the resource group like
"storageName": "[concat('mystorage', uniqueString(resourceGroup().id))]" // in Resource Group A
then you'll either need to always run the template that creates this account and output the storageName and the resourceGroup or find a way to reference the other resource group to get it's id so that the name can be re-created.
I have been able to use something like this to "re-create" the resource group id so I can generate the proper name of the storage account.
"otherResourceGroupId": "[concat(subscription().id, '/resourceGroups/', parameters('ResourceGroupName'))]"
Then I can use that to generate the name appropriately:
"storageAccountName": "[concat('mystorage', uniqueString(variables('otherResourceGroupId')))]"
You can try below(for an example ):-
"subNetId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('virtualNetworkNameRG'), '/providers/', 'Microsoft.Network/virtualNetworks/',parameters('virtualNetworkName'),'/subnets/', parameters('subnetName'))]",
refer https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-template-functions
I have more than enough CPUs and memory to launch 100 instances but only 30 external IP addresses is there a way I can launch more instances despite that?
Chances are you don't need that many IPs at all. Only in very specific scenarios you would need all your nodes to be publicly accessible.
If you need that many instances, simply create them without public IPs. Then, create a NAT Gateway so your instances can use that to access outside your private network.
You will be able to accomplish 99% of usage scenarios this way. If you really need more IPs and you have used all of your ephemeral IPs you can request in the Form.
I guess it depends what you want to do, but the gcloud compute instances create tool has a flag --no-address which will allow you to launch an instance with no external IP address. Have a look at gcloud compute instances create --help to see if you think that would be useful.
If you wanted to use the API or instance templates, I think just leaving out the accessConfigs part of the networking section of the request body will do what you need. Compare this:
"networkInterfaces": [
{
"network": "https://www.googleapis.com/compute/v1/projects/your-project-here/global/networks/default",
"accessConfigs": [
{
"name": "External NAT",
"type": "ONE_TO_ONE_NAT"
}
]
}
Where I used the default option of "Ephemeral" for the external IP in the Google Cloud Developers console, with this:
"networkInterfaces": [
{
"network": "https://www.googleapis.com/compute/v1/projects/your-project-here/global/networks/default"
}
]
Where I selected "None" as the External IP.
To look at what the API body would look like, there is a link "View Equivalent REST" just below the Create button, it can be really useful for templates and things.