How to know how many Mount targets to create by Cloudformation - json

I create by cloudformation an EFS in a VPC, this VPC contains few EC2 instances.
I need to create as well mount targets for each subnet.
This cloudformation template can be executed in different AWS accounts.
How to know how meny Mount targets resources to create?
I can have a vpc in account 1 with 3 subnets, another vpc in account 2 with 2 subnets...
How to make the template generic so that it's going according to every account environment ?
"FileSystem" : {
"Type" : "AWS::EFS::FileSystem",
"Properties" : {
"FileSystemTags" : [
{
"Key" : "Name",
"Value" : "FileSystem"
}
]
}
},
"MountTarget1": {
"Type": "AWS::EFS::MountTarget",
"Properties": {
"FileSystemId": { "Ref": "FileSystem" },
"SubnetId": { "Ref": "Subnet1" },
"SecurityGroups": [ { "Ref": "MountTargetSecurityGroup" } ]
}
},
"MountTarget2": {
"Type": "AWS::EFS::MountTarget",
"Properties": {
"FileSystemId": { "Ref": "FileSystem" },
"SubnetId": { "Ref": "Subnet2" },
"SecurityGroups": [ { "Ref": "MountTargetSecurityGroup" } ]
}
},

As I understand, you want to be able to re-use this same template across accounts and create the appropriate number of MountTargets based on the number of subnets required by an account.
There are many variables and conditions that would apply depending on the number of subnets (ACL, Routetables, etc). You could perhaps accomplish it with a large number of conditions and parameters, but the template would get quite messy. Although that's not an elegant solution.
The better approach would be creating your template using Troposphere. Here's an example for EFS to get you started. https://github.com/cloudtools/troposphere/blob/master/examples/EFS.py

Related

Need to apply IAMPass Role to specific environment

I have cloudformation template. Here we have multiple environments(dev,qa,uat) and need to use same template for all environments.
In template Under "Action": ["iam:PassRole"] there are 4 resources, 3 resources are belongs to qa. When am deploying code on dev and uat Env, qa resources are applying to dev and uat environment as well but I need to create these 3 qa resources only for qa environment. I tried some conditions but isn't working. Is there any approach for this.
Please find below template code.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"CloudFormationRole": {
"Type": "AWS::IAM::Role",
"Description": "Service role in IAM for AWS CloudFormation",
"Properties": {
"RoleName": {
"Fn::Sub": "${Environment}-workflow-CloudFormationRole"
},
"Path": "/",
"Policies": [
{
"PolicyName": "WorkerCloudFormationRolePolicy",
"PolicyDocument": {
"Statement": [
{
"Action": [
"lambda:AddPermission",
"lambda:PutFunctionEventInvokeConfig",
"lambda:UpdateFunctionEventInvokeConfig"
],
"Resource": {
"Fn::Sub": "arn:aws:lambda:function:orderser-${Environment}-workflow-*"
},
"Effect": "Allow"
},
{
"Action": [
"iam:PassRole"
],
"Resource": [
{"Fn::Sub": "arn:aws:iam::role/orderser-workflow-*"},
{"Fn::Sub": "arn:aws:iam::role/orderserv-qa-workflowLambdaRole1"},
{"Fn::Sub": "arn:aws:iam::role/orderserv-qa-workflowLambdaRole2"},
{"Fn::Sub": "arn:aws:iam::role/orderserv-qa-workflowLmbdRole3"}
],
"Effect": "Allow"
}
]
}
}
]
}
}
}
}
Parameterize the pass role ARNs and have different set of parameter files for each environment
And, since you have multiple values, use CommaDelimitedList type parameter which can take multiple string values
Ref here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html

CFT Template error: unresolved condition dependency UseDBSnapshot in Fn::If

Trying to create a CFT for RDS which can handle both the scenarios
creating a new RDS Aurora MySQL cluster and
create a RDS cluster with a existing DB Cluster Snapshot
Here is what I tried,
I have provide the below conditions section of the template
"UseDbSnapshot" : {
"Fn::Not" : [
{
"Fn::Equals":[
{"Ref": "DBSnapshotName"},
""
]
}
]
}
and referenced in Resource section as below
"RDSCluster1": {
"Type": "AWS::RDS::DBCluster",
"Condition": "isResourceCreate",
"Properties": {
"Engine": "aurora",
"DBSubnetGroupName": {
"Ref": "DBSubnetGroup"
},
"DBClusterParameterGroupName": {
"Ref": "RDSDBClusterParameterGroup"
},
"DBSnapshotIdentifier" : {
"Fn::If" : [
"UseDBSnapshot",
{"Ref" : "DBSnapshotName"},
{"Ref" : "AWS::NoValue"}
]
},
"MasterUsername": {
"Ref": "DbUser"
},
"MasterUserPassword": {
"Ref": "MasterUserPassword"
},
"StorageEncrypted" : true,
"KmsKeyId" : {
"Ref": "KmsKeyId"
},
"VpcSecurityGroupIds": [
{
"Fn::GetAtt": [
"DBAccessSecurityGroup",
"GroupId"
]
}
],
"Port": "3306",
"BackupRetentionPeriod": "1"
},
"DeletionPolicy": "Snapshot"
}
The condition "isResourceCreate" is satisfied but I am getting below error
Template error: unresolved condition dependency UseDBSnapshot in Fn::If
Could you please help me here.
Have looked up online link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-sample-templates.html
and created this CFT.
Let me know if you require any more details.
If you are restoring DB from snapshot, you can't provide MasterUsername and MasterUserPassword. These values will be inherited from the snapshot, so you have to make them optional.
If you specify the SourceDBInstanceIdentifier or DBSnapshotIdentifier property, don't specify this property. The value is inherited from the source DB instance or snapshot.

How to check if name already exists? Azure Ressource Manager Template

is it possible to check, in an ARM Template, if the name for my Virtual Machine already exists?
I am developing a Solution Template for the Azure Marketplace. Maybe it is possible to set a paramter in the UiDefinition uniqe?
The goal is to reproduce this green Hook
A couple notes...
VM Names only need to be unique within a resourceGroup, not within the subscription
Solution Templates must be deployed to empty resourceGroups, so collisions with existing resources aren't possible
For solution templates the preference is that you simply name the VMs for the user, rather than asking - use something that is appropriate for the workload (e.g. jumpbox) - not all solutions do this but we're trying to improve that experience
Given that it's not likely we'll ever build a control that checks for naming collisions on resources without globally unique constraints.
That help?
This looks impossible, according to the documentation.
There are no validation scenarious.
I assume that you should be using the Microsoft.Common.TextBox UI element in your createUiDefinition.json.
I have tried to reproduce a green check by creating a simple createUiDefinition.json as below with a Microsoft.Common.TextBox UI element as shown below.
{
"$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json",
"handler": "Microsoft.Compute.MultiVm",
"version": "0.1.2-preview",
"parameters": {
"basics": [
{
"name": "textBoxA",
"type": "Microsoft.Common.TextBox",
"label": "VM Name",
"defaultValue": "",
"toolTip": "Please enter a VM name",
"constraints": {
"required": true
},
"visible": true
}
],
"steps": [],
"outputs": {}
}
}
I am able to reproduce the green check beside the VM Name textbox as shown below:
However, this green check DOES NOT imply the VM Name is Available.
This is because based on my testing, even if I use an existing VM Name in the same subscription, it is still showing the green check.
Based on the official documented constraints that are supported by the Microsoft.Common.TextBox UI element, it DOES NOT VALIDATE Name Availability.
Hope this helps!
While bmoore's point is correct that it's unlikely you would ever need this for a VM (nor is there an API for it), there are other compute resources that do have global naming requirements.
As of 2022 this concept is possible now with the use of the ArmApiControl UI element. It allows you to call ARM apis as part of validation in the createUiDefinition.json. Here is an example using the check name API for an Azure App service.
{
"$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#",
"handler": "Microsoft.Azure.CreateUIDef",
"version": "0.1.2-preview",
"parameters": {
"basics": [
{}
],
"steps": [
{
"name": "domain",
"label": "Domain Names",
"elements": [
{
"name": "domainInfo",
"type": "Microsoft.Common.InfoBox",
"visible": true,
"options": {
"icon": "Info",
"text": "Pick the domain name that you want to use for your app."
}
},
{
"name": "appServiceAvailabilityApi",
"type": "Microsoft.Solutions.ArmApiControl",
"request": {
"method": "POST",
"path": "[concat(subscription().id, '/providers/Microsoft.Web/checknameavailability?api-version=2021-02-01')]",
"body": "[parse(concat('{\"name\":\"', concat('', steps('domain').domainName), '\", \"type\": \"Microsoft.Web/sites\"}'))]"
}
},
{
"name": "domainName",
"type": "Microsoft.Common.TextBox",
"label": "Domain Name Word",
"toolTip": "The name of your app service",
"placeholder": "yourcompanyname",
"constraints": {
"validations": [
{
"regex": "^[a-zA-Z0-9]{4,30}$",
"message": "Alphanumeric, between 4 and 30 characters."
},
{
"isValid": "[not(equals(steps('domain').appServiceAvailabilityApi.nameAvailable, false))]",
"message": "[concat('Error with the url: ', steps('domain').domainName, '. Reason: ', steps('domain').appServiceAvailabilityApi.reason)]"
},
{
"isValid": "[greater(length(steps('domain').domainName), 4)]",
"message": "The unique domain suffix should be longer than 4 characters."
},
{
"isValid": "[less(length(steps('domain').domainName), 30)]",
"message": "The unique domain suffix should be shorter than 30 characters."
}
]
}
},
{
"name": "section1",
"type": "Microsoft.Common.Section",
"label": "URLs to be created:",
"elements": [
{
"name": "domainExamplePortal",
"type": "Microsoft.Common.TextBlock",
"visible": true,
"options": {
"text": "[concat('https://', steps('domain').domainName, '.azurewebsites.net - The main app service URL')]"
}
}
],
"visible": true
}
]
}
],
"outputs": {
"desiredDomainName": "[steps('domain').domainName]"
}
}
}
You can copy the above code and test it in the createUiDefinition.json sandbox azure provides.

Chef use node_name variable in JSON role

Is there a way to use NODE_NAME value in a role ?
In fact, I'm writing a base role that is setting nodes hostnames and I wish to use NODE_NAME as "short_hostname".
{
"name": "Chef-RHEL",
"description": "Chef-RHEL role",
"json_class": "Chef::Role",
"default_attributes": {
"set_fqdn": "*.example.com",
"system": {
"timezone": "Europe/Paris",
"short_hostname": "{NODE_NAME}",
"domain_name": "example.com"
}
},
"override_attributes": {
},
"chef_type": "role",
"run_list": [
"recipe[system::default]"
],
"env_run_lists": {
}
}
NODE_NAME is correctly configured in /etc/chef/client.rb.
Thanks a lot !!! :)
This is not possible directly, roles are static JSON data only. You can use some kind of string replacement in the recipe code but that requires changes in the cookbook consuming the attribute first.

Assigning Elastic IPs to Secondary Private IPs via SecondaryPrivateIpAddresses List Attribute

Below is a snippet of my CloudFormation template used to associate an Elastic IP Address with the primary IP of a network interface:
"MyInterfaceSelfEipAssociation": {
"Properties": {
"AllocationId": "eipalloc-XXXXX",
"NetworkInterfaceId": {
"Ref": "MyInterface"
},
"PrivateIpAddress": {
"Fn::GetAtt": [
"MyInterface",
"PrimaryPrivateIpAddress"
]
}
},
"Type": "AWS::EC2::EIPAssociation"
I want to do the same thing for the secondary IPs on this interface, of which there are two (particular IPs I have given in a list, not assigned by AWS). i.e. the "PrivateIpAddresses" block of the interface looks like this:
"PrivateIpAddresses": [
{
"PrivateIpAddress": "10.X.X.XX",
"Primary": "true"
},
{
"PrivateIpAddress": "10.X.X.XX",
"Primary": "false"
},
{
"PrivateIpAddress": "10.X.X.XX",
"Primary": "false"
}
],
I know I can access the list of secondary private IPs with the Fn:GetAtt attribute call "SecondaryPrivateIpAddresses" which will return the two secondary private IPs above to me as a list. My question is, how can I address this list in JSON - by index?
For example, if I wanted to assign a private IP to the second element in the list of secondary IPs, is it valid to do something like:
"PrivateIpAddress": {
"Fn::GetAtt": [
"Bigip1subnet1Az1Interface",
"SecondaryPrivateIpAddresses[1]"
]
}
How can I achieve this? I feel like it should be straightforward but I'm not clear on how to do this in JSON.
Use the Fn::Select intrinsic function to return a single object from a list of objects by index:
"PrivateIpAddress": {
"Fn::Select": [
1,
{ "Fn::GetAtt": [
"Bigip1subnet1Az1Interface",
"SecondaryPrivateIpAddresses"
]}
]
}