This response is an edited reply by AWS support to my similar inquiry as the OP's question.
This feature is currently not available and AWS's CloudFormation developer team is aware of this issue and there is a feature request in place.
As a workaround, you can leverage a Lambda backed custom resource to get the security ID and pass it to the custom resource so that it can be accessed in the CF stack.
In this approach, you will create a Lambda function that can take the security group name and VPC-id as input and give the security group-id as output. The custom resource created is the piece of code which will signal the Lambda function with a group name and VPC-id. Lambda returns the security group-id to this custom resource, you can get the sg-id as shown below:
{ "Fn::GetAtt" : ["CustomResouce", "security_group_id"] }
A sample template and a sample Lambda function (for acquiring the security group-id) are included at the end of this message. In the template, the function has been used in the output of the code, but you can use the same in the client's security_group as shown below:
"SourceSecurityGroupId" : { "Fn::GetAtt" : ["CustomResouce", "security_group_id"] },
"SourceSecurityGroupName" : { "Fn::Sub": [ "${Alias}_controllers", { "Alias": {"Ref" : "Alias" }} ]},
Custom Lambda function, customresouce.py (to be placed in a S3 bucket where Lambda can access it):
import json
import boto3
import time
from botocore.vendored import requests
def lambda_handler(event, context):
print event['RequestType']
try:
if event['RequestType'] == 'Delete':
print "delete"
responseData = {'response': 'Delete'}
responseStatus = 'SUCCESS'
elif event['RequestType'] == 'Create':
print "blabla"
ec2 = boto3.resource('ec2')
vpc = ec2.Vpc(event['ResourceProperties']['vpc'])
security_group_iterator = vpc.security_groups.filter(GroupNames = [event['ResourceProperties']['security_group']])
sg_id = list(security_group_iterator.filter(GroupNames = [event['ResourceProperties']['security_group']]))[0].id
print sg_id
responseData = {'security_group_id': sg_id}
elif event['RequestType'] == 'Update':
print "update"
responseData = {'response': 'Update'}
responseStatus = 'SUCCESS'
responseStatus = 'SUCCESS'
except:
responseStatus = 'FAILED'
responseData = {'FAILED': 'Something bad happened.'}
sendResponse(event, context, responseStatus, responseData)
def sendResponse(event, context, responseStatus, responseData, reason=None, physical_resource_id=None):
responseBody = {'Status': responseStatus,
'Reason': 'See the details in CloudWatch Log Stream: ' + context.log_stream_name,
'PhysicalResourceId': physical_resource_id or context.log_stream_name,
'StackId': event['StackId'],
'RequestId': event['RequestId'],
'LogicalResourceId': event['LogicalResourceId'],
'Data': responseData}
print 'RESPONSE BODY:n' + json.dumps(responseBody)
responseUrl = event['ResponseURL']
json_responseBody = json.dumps(responseBody)
headers = {
'content-type' : '',
'content-length' : str(len(json_responseBody))
}
try:
response = requests.put(responseUrl,
data=json_responseBody,
headers=headers)
print "Status code: " + response.reason
except Exception as e:
print "send(..) failed executing requests.put(..): " + str(e)
A sample template that utilizes the custom Lambda function:
{
"Resources": {
"myDirectory" : {
"Type" : "AWS::DirectoryService::SimpleAD",
"Properties" : {
"Name" : "corp.example.com",
"Password" : "P@ssword",
"Size" : "Small",
"VpcSettings" : {
"SubnetIds" : [ "subnet_value-1", "subnet_value-2" ],
"VpcId" : "your_vpc-id"
}
}
},
"CustomResouce": {
"DependsOn": "myDirectory",
"Type": "Custom::GettingsecuritygroupId",
"Version" : "1.0",
"Properties" : {
"ServiceToken": {"Fn::GetAtt" : ["Mylambda","Arn"]},
"vpc" : "vpc-b0ee43c9",
"security_group" : { "Fn::Sub": [ "${Alias}_controllers", {"Alias":{"Fn::GetAtt" : ["myDirectory","Alias"]} }]}
}
},
"Mylambda":{
"Type" : "AWS::Lambda::Function",
"Properties" : {
"Code" : {
"S3Bucket": "Your_s3_bucket_name",
"S3Key": "customresource.py.zip"
},
"Handler" : "customresource.lambda_handler",
"Role" : "Role_whic_has_permissions_ec2:*",
"Runtime" :"python2.7",
"Timeout" : "60"
}
}
},
"Outputs":{
"SGID" : {
"Value" : { "Fn::GetAtt" : ["CustomResouce", "security_group_id"] }
}
}
}
Rather kludgy, but seems like it might work as an interim solution until AWS gets around to implementing the functionality into their APIs/CloudFormation/Hosted AD. NOTE: I have not had the opportunity to test the above yet, but I'm posting it here for the OP and anyone else who might be looking for a solution to this issue.
References:
Custom Resource Reference
AWS Lambda-backed Custom Resources
AWS::Lambda::Function