I am trying to build an Azure VNET via .json template.
I am trying to use an inline conditional statement to either create a 2nd subnet or skip creating the 2nd subnet. I do not think I am using json('null') correctly, or if this is even possible. My understanding is if json('null') is chosen, nothing is chosen.
Any help is appreciated!
"apiVersion": "2016-06-01",
"type": "Microsoft.Network/virtualNetworks",
"name": "My-VNET",
"location": "[resourceGroup().location]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[parameters('virtualNetworkCIDR')]"
]
},
"subnets": [{
"name": "[parameters('firstSubnetName')]",
"properties": {
"addressPrefix": "10.10.1.0/24"
}
}, {
"name": "[if(equals(parameters('createSecondSubnet'), 'Yes'), parameters('secondSubnetName'), json('null'))]",
"properties": {
"addressPrefix": "10.10.2.0/24"
}
}
]
}
I'm sure that you managed to get some kind of solution to this a while ago ... however, I have a solution that works well for this sort of thing ... it doesn't use conditional statements though.
In PowerShell create some hash-tables like so ...
# Resource group Hashtables.
$rgDev = #{
Name = "DEV-RG"
SubscriptionId = $subNonProd
Location = "desiredregion"
}
$rgUat = #{
Name = "UAT-RG"
SubscriptionId = $subNonProd
Location = "desiredregion"
}
#Vnet Hashtables
$vnetDev = #{
ResourceGroup = $rgDev
VnetName = "Dev-vnet"
CIDR = #('x.x.x.x/27')
Subnets = #(
#{
Name = "Dev-Web-subnet"
CIDR = "y.y.y.y/28"
},
#{
Name = "Dev-DB-subnet"
CIDR = "z.z.z.z/28"
})
}
$vnetUat = #{
ResourceGroup = $rgUat
VnetName = "UAT-vnet"
CIDR = #('f.f.f.f/27')
Subnets = #(
#{
Name = "UAT-Web-subnet"
CIDR = "g.g.g.g/28"
},
#{
Name = "UAT-DB-subnet"
CIDR = "h.h.h.h/28"
})
}
Next I lump the hashtables into an array and foreach over the lot. I have a little script that switches my context so that I can deploy to multiple subscriptions in the one bootstrap type script.
$vnets = #($vnetDev, $vnetUat)
ForEach ($vn in $vnets) {
$deploymentName = $vn.VnetName + "_Deployment."
.\SwitchSubscription.ps1 -subName $vn.ResourceGroup.SubscriptionName -subId $vn.ResourceGroup.SubscriptionId
New-AzureRmResourceGroupDeployment -Name $deploymentName `
-ResourceGroupName $vn.ResourceGroup.Name `
-TemplateFile .\JSONFiles\Vnets.json `
-vnet $vn
}
The Parameters section of the ARM template looks like this ...
"parameters": {
"vnet": {
"type": "object",
}
},
then the Resource Section looks like this ...
{
"name": "[concat(parameters('vnet').VnetName)]",
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2017-10-01",
"location": "[resourceGroup().location]",
"tags": "[parameters('vnet').tags]",
"properties": {
"addressSpace": {
"addressPrefixes": "[parameters('vnet').CIDR]"
},
"copy": [
{
"name": "subnets",
"count": "[length(parameters('vnet').Subnets)]",
"input": {
"name": "[parameters('vnet').Subnets[copyIndex('Subnets')].Name]",
"properties": {
"addressPrefix": "[parameters('vnet').Subnets[copyIndex('Subnets')].CIDR]"
}
}
}
]
}
}
]
So what this all does is pass an object through to the ARM template that can have one Vnet with one or more subnets and create them all.
Hopefully this will help someone else out when / if they find this when googling.
Cheers,
Dave.
:)
Normally to conditionally create resources in a template, you can use the "Condition" property: https://learn.microsoft.com/en-us/azure/architecture/building-blocks/extending-templates/conditional-deploy
if you would like to create multiple of a single type of resource, you can use the "Copy" property: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-multiple#resource-iteration
Unfortunately, subnets do not have a condition or a copy property, because they are a sub-resource of the "Virtual Network" resource. Because of this, the entire VNET will need to be conditional, and you can specify multiple VNETS with only 1 of them deploying. The VNETS can also not have the same name, so you will need to specify multiple VNETS in your template.
For Example:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"NumberofSubnets": {
"type": "string",
"allowedValues": ["1","2"],
"metadata": {
"description": "would you like to deploy 1 or 2 subnets?"
}
}
},
"resources": [
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2016-06-01",
"name": "My-VNET1",
"location": "[resourceGroup().location]",
"condition": "[equals(parameters('NumberofSubnets'), 1)]",
"properties": {
"addressSpace": {
"addressPrefixes": ["10.10.0.0/23"]
},
"subnets": [{
"name": "Subnet1",
"properties": {
"addressPrefix": "10.10.0.0/24"
}
}
]
}
},
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2016-06-01",
"name": "My-VNET2",
"location": "[resourceGroup().location]",
"condition": "[equals(parameters('NumberofSubnets'), 2)]",
"properties": {
"addressSpace": {
"addressPrefixes": ["10.10.0.0/23"]
},
"subnets": [{
"name": "Subnet1",
"properties": {
"addressPrefix": "10.10.0.0/24"
}
},
{
"name": "Subnet2",
"properties": {
"addressPrefix": "10.10.1.0/24"
}
}
]
}
}
]
}
This will solve your problem, but given a large amount of Subnets, you can see how a template can get very tedious.
The Best but most complicated way is to use linked templates. This Repository shows how you can create a dynamic number of subnets using linked templates
Related
I have the following code which I'm trying to read in CMake.
{
"demo": [
{
"name": "foo0",
"url": "url1",
"verify_ssl": true
},
{
"name": "foo1",
"url": "url1",
"verify_ssl": true
},
{
"name": "foo2",
"url": "url2",
"verify_ssl": true
}
]
}
I'm trying to access a member from the list above, for example demo[0].name without success, what I'm doing wrong?
file(READ "${CONAN_CACHE}/demo.json" MY_JSON_STRING)
string(JSON CUR_NAME GET ${MY_JSON_STRING} demo[0].name)
One at a time.
string(JSON CUR_NAME GET ${MY_JSON_STRING} demo 0 name)
I have following code in terraform state file( pasted just part of state file which I consider relevant for this question), which is result of running terraform code pasted below as well:
"mode": "managed",
"type": "azurerm_vpn_gateway",
"name": "azure_vpngw",
"provider": "provider[\"registry.terraform.io/hashicorp/azurerm\"].azure_clusters",
"instances": [
{
"schema_version": 0,
"attributes": {
"bgp_settings": [
{
"asn": 65515,
"bgp_peering_address": "",
"instance_0_bgp_peering_address": [
{
"custom_ips": [
"169.254.21.1"
],
"default_ips": [
"10.255.176.12"
],
"ip_configuration_id": "Instance0",
"tunnel_ips": [
"10.255.176.4",
"20.184.79.231"
]
Relevant terraform code:
resource "azurerm_vpn_gateway" "azure_vpngw" {
provider = azurerm.azure_clusters
name = "azure_vpngw"
location = azurerm_resource_group.azure_networking.location
resource_group_name = azurerm_resource_group.azure_networking.name
virtual_hub_id = azurerm_virtual_hub.azure_hub.id
bgp_settings {
asn = 65515
peer_weight = 50
instance_0_bgp_peering_address {
custom_ips = ["169.254.21.1"]
}
instance_1_bgp_peering_address {
custom_ips = ["169.254.22.1"]
}
}
}
And I need to accesss last element in tunnel_ips list, so 20.184.79.231.
I have been trying diff things like
azurerm_vpn_gateway.azure_vpngw.bgp_settings[0].instance_0_bgp_peering_address[0].tunnel_ips[0][1]
but it did not work. So, issue is in last part - tunnel_ips[0][1] I believe. If anybody has idea, it would be very wellcome.
Since your code is not a valid TF code, I modified it to actually be such, and this is how you can access your ip:
locals {
t = <<EOL
{
"mode": "managed",
"type": "azurerm_vpn_gateway",
"name": "azure_vpngw",
"provider": "provider[azure_clusters",
"instances": [{
"schema_version": 0,
"attributes": {
"bgp_settings": [{
"asn": 65515,
"bgp_peering_address": "",
"instance_0_bgp_peering_address": [{
"custom_ips": [
"169.254.21.1"
],
"default_ips": [
"10.255.176.12"
],
"ip_configuration_id": "Instance0",
"tunnel_ips": [
"10.255.176.4",
"20.184.79.231"
]
}]
}]
}
}]
}
EOL
v = jsondecode(local.t)
}
output "test" {
value = local.v["instances"][0]["attributes"]["bgp_settings"][0]["instance_0_bgp_peering_address"][0]["tunnel_ips"][1]
}
gives:
test = "20.184.79.231"
I figure it out. So, to fetch IP and use it in another resource definition I did:
ip_address = sort(azurerm_vpn_gateway.azure_vpngw.bgp_settings[0].instance_0_bgp_peering_address[0].tunnel_ips)[1]
I'm setting up a Firmware-Framework, where I unforutunately have to add historically Sensor Values. But I also need the creationDate and the modificationDate for other usecases.
Therefore I add the Attribute "Metadata" with the variable "TimeInstant". Then I create an Entity, create an Orion-Subscription for that Entity and update the Entity with my old Sensor-Valses.
The Json-File I send to the Orion-Context Broker to update the Attribute looks like this:
{
"metadata": {
"TimeInstant": {
"type": "DateTime",
"value": "2015-02-02T11:35:25.0000Z"
}
},
"type": "Number",
"value": 0.0132361 }
The Output in my Mongo-DB like this:
"_id": {
"id": "urn:ngsi-ld:SensorB-K1200____",
"type": "Sensor",
"servicePath": "/test/servicepath"
},
"attrNames": [
"Sensor_value"
],
"attrs": {
"Sensor_value": {
"value": 0.01632361,
"type": "Number",
"md": {
"TimeInstant": {
"type": "DateTime",
"value": 1422876989
}
},
"mdNames": [
"TimeInstant"
],
"creDate": 1568712813,
"modDate": 1568735930
}
},
"creDate": 1568712813,
"modDate": 1568735930,
"lastCorrelator": "0a129232-d964-11e9-8e5a-0242ac130009" }
But my Crate-DB only has the columns:
entity_id entity_type fiware_servicepath sensor_value time_index
My Subscription File looks like this:
{
"expires": "2019-12-24T18:00:00",
"notification": {
"http": {
"url": "http://quantumleap:8668/v2/notify"
},
"metadata": [
"dateCreated",
"dateModified",
"TimeInstant"
]
},
"subject": {
"entities": [
{
"id": "urn:ngsi-ld:SensorB-K1200____",
"type": "Sensor"
}
]
},
"throttling": 0 }
I've tried changing the "Metadata" Attributes in the Subscription-File, also tried restartig Crate-DB, ContextBroker e.g..
I excpect the CrateDb to show all three values: "dateCreated", "dateModified" and "TimeInstant".
Did you check what's the message notification actually sent by Orion to QuantumLeap?
As regards the payload I would try as follow:
{
"TimeInstant": {
"type": "DateTime",
"value": "2015-02-02T11:35:25.0000Z"
},
"type": "Number",
"value": 0.0132361
}
Internally we usually use as attribute name for this type of scenario dateObserved, but it would not make any difference w.r.t. TimeInstant.
I am not actually sure you can attach metadata to the root of NGSI message, I believe they are supposed to be attached only to attributes.
Anyhow, QuantumLeaps does not support NGSI metadata (i.e. metadata attached to NGSI attributes). Still it support time indexing based on them.
The way Quantum Leap handles TimeInstant metadata and other time metadata is via time_index. See documentation here: https://quantumleap.readthedocs.io/en/latest/user/#time-index
I have a cloudformation template in which I send a JSON parameters file over. It was not a real problem, as my parameters file used to look like this:
[
"InstanceType=t2.medium",
"AmiStack=amilookup-stack"
]
However, I want to add a list to the parameters file, something like this:
[
"InstanceType=t2.medium",
"AmiStack=amilookup-stack",
[
"CertificateArn1=arn:aws:acm:us-east-1:xxx",
"CertificateArn2=arn:aws:acm:us-east-1:xxy",
]
]
What is the best way to express this in my parameters json file, and how to express this in the cloudformation template itself?enter code here
This is a known problem with Cloudformation. You can use comma demlimited list as parameter type.
Cloudformation Template (test.json)
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"Name": {
"Type": "String",
"Description": "SubnetGroup Name"
},
"Subnets": {
"Type": "CommaDelimitedList",
"Description": "The list of SubnetIds in your Virtual Private Cloud (VPC)"
}
},
"Resources": {
"myDBSubnetGroup": {
"Type": "AWS::RDS::DBSubnetGroup",
"Properties": {
"DBSubnetGroupDescription": "description",
"DBSubnetGroupName": {
"Ref": "Name"
},
"SubnetIds": {
"Ref": "Subnets"
}
}
}
}
}
Parameter.json
[
{
"ParameterKey": "Name",
"ParameterValue": "testName"
},
{
"ParameterKey": "Subnets",
"ParameterValue": "subnet-abcdefg,subnet-abcdef1,subnet-abcdef2"
}
]
aws cloudformation create-stack --stack-name testStack --template-body file://test.json --parameters file://parameter.json --profile yourawsprofile
I need to protect a DSC configuration parameter ([pscredential]RegistrationKey), so I have put it under "settings.protectedSettings.configurationData" thus:
"protectedSettings": {
"configurationArguments": {
"RegistrationKey": {
"UserName": "PLACEHOLDER_DONOTUSE",
"Password": "[parameters('dscAutomationRegistrationKey')]"
}
},
"configurationUrlSasToken": "[parameters('artifactsLocationSasToken')]"
}
I get the error:
"VM has reported a failure when processing extension 'Microsoft.Powershell.DSC'. Error message: \"The DSC Extension failed to execute: Mandatory
parameter RegistrationKey is missing.
If I move RegistrationKey out of "settings.protectedSettings.configurationArguments", into "settings.configurationArguments", it works, therefore, I assume there is nothing wrong with the syntax, so I believe it is to do with PsDscAllowPlainTextPassword = $true that wasn't included in the DSC configuration.
(I tried to include the configuration block in the PS1 file, but this threw an error, suggesting this can't be done)
I have now written a configurationdata .psd1 file, containing the following:
$ConfigData = #{
AllNodes = #(
#{
NodeName = "*"
PsDscAllowPlainTextPassword = $true
}
)
}
and referenced it in settings.configurationdata.url.
This now results in the same error as before: VM has reported a failure...
The ARM template is called from PowerShell:
$oAutomationAccount = Get-AzureRmAutomationAccount -ResourceGroupName $AAresourceGroupName -Name $AutomationAccountName
$RegistrationInfo = $oAutomationAccount | Get-AzureRmAutomationRegistrationInfo
$DscRegKeyString = $RegistrationInfo.PrimaryKey
$ssDscAutomationRegistrationKey = (ConvertTo-SecureString -string $DscRegKeyString -AsPlainText -Force)
#Automation Account EndPoint Uri
$DscRegistrationUrl = $RegistrationInfo.Endpoint
$params = #{
artifactsLocationSasToken = $TemplateSas
vmName = "XYZ"
dscAutomationRegistrationKey = $ssDscAutomationRegistrationKey
dscAutomationRegistrationUrl = $DscRegistrationUrl
dscNodeConfigurationName = "CreateAFolder.localhost"
dscTimeStamp = (Get-Date -f "MM/dd/yyyy H:mm:ss tt") #"MM/dd/yyyy H:mm:ss tt"
dscResourceUrl = $DscResourceUrl
dscConfigurationUrl = $DscConfigurationUrl
dscResourceScript = $DscResourceScriptName
dscResourceFunction = "ConfigureLCMforAAPull"
#sequenceId = $sequenceId
}
New-AzureRmResourceGroupDeployment #params `
-Name "$TemplateInstance-$branch" `
-ResourceGroupName $DeploymentResourceGroup.ResourceGroupName `
-Mode Incremental `
-DeploymentDebugLogLevel All `
-TemplateUri $TemplateUri `
-Verbose
Where I believe the parameters are being passed as the correct types.
What am I doing wrong?
Reference template: https://github.com/Azure/azure-quickstart-templates/blob/master/dsc-extension-azure-automation-pullserver/azuredeploy.json
Updated to use a newer DSC schema:https://blogs.msdn.microsoft.com/powershell/2016/02/26/arm-dsc-extension-settings/
this is the template I've been using for node onboarding:
{
"name": "xxx",
"type": "Microsoft.Compute/virtualMachines/extensions",
"location": "[parameters('location')]",
"apiVersion": "2015-06-15",
"dependsOn": [
"xxx"
],
"properties": {
"publisher": "Microsoft.Powershell",
"type": "DSC",
"typeHandlerVersion": "2.22",
"autoUpgradeMinorVersion": false,
"protectedSettings": {
"Items": {
"registrationKeyPrivate": "[parameters('registrationData')]"
}
},
"settings": {
"ModulesUrl": "https://github.com/Azure/azure-quickstart-templates/raw/master/dsc-extension-azure-automation-pullserver/UpdateLCMforAAPull.zip",
"SasToken": "",
"ConfigurationFunction": "UpdateLCMforAAPull.ps1\\ConfigureLCMforAAPull",
"Properties": [
{
"Name": "RegistrationKey",
"Value": {
"UserName": "PLACEHOLDER_DONOTUSE",
"Password": "PrivateSettingsRef:registrationKeyPrivate"
},
"TypeName": "System.Management.Automation.PSCredential"
},
{
"Name": "RegistrationUrl",
"Value": "xxx",
"TypeName": "System.String"
},
{
"Name": "NodeConfigurationName",
"Value": "xxx",
"TypeName": "System.String"
},
{
"Name": "ConfigurationMode",
"Value": "ApplyAndMonitor",
"TypeName": "System.String"
},
{
"Name": "ConfigurationModeFrequencyMins",
"Value": 15,
"TypeName": "System.Int32"
},
{
"Name": "RefreshFrequencyMins",
"Value": 30,
"TypeName": "System.Int32"
},
{
"Name": "RebootNodeIfNeeded",
"Value": true,
"TypeName": "System.Boolean"
},
{
"Name": "ActionAfterReboot",
"Value": "ContinueConfiguration",
"TypeName": "System.String"
},
{
"Name": "AllowModuleOverwrite",
"Value": true,
"TypeName": "System.Boolean"
},
{
"Name": "Timestamp",
"Value": "MM/dd/yyyy H:mm:ss tt",
"TypeName": "System.String"
}
]
}
}
}
I know its using an old format, but that works so, meh.