Repository: zalando-stups/senza

Issue:
Lookup SecurityGroups by the tag "Name" rather than GroupName
Both AWS API and CloudFormation allow to refer to a security group by its name if the operation runs in EC2 Classic or the default VPC. Unfortunately it uses the `GroupName` attribute that is automatically generated by AWS if the SG is a part of CloudFormation stack.

It would be a good idea to extend Senza to lookup SG during the CF template generation phase and to use the _tag_ `Name` instead. The tag can be set by another ("system") Senza stack definition, thus allowing the cross-stack references.

Another option would be to support the new cross-stack references that are recently introduced by Amazon:  https://aws.amazon.com/blogs/aws/aws-cloudformation-update-yaml-cross-stack-references-simplified-substitution/

Discussion:

Patch:
diff --git a/senza/aws.py b/senza/aws.py
index 1284ba3..c7f0468 100644
--- a/senza/aws.py
+++ b/senza/aws.py
@@ -55,13 +55,12 @@ def is_status_complete(status: str):
 def get_security_group(region: str, sg_name: str):
     ec2 = boto3.resource('ec2', region)
     try:
-        sec_groups = list(ec2.security_groups.filter(
-            Filters=[{'Name': 'group-name', 'Values': [sg_name]}]
-        ))
-        if not sec_groups:
-            return None
-        # FIXME: What if we have 2 VPC, with a SG with the same name?!
-        return sec_groups[0]
+        # first try by tag name then by group-name (cannot be changed)
+        for _filter in [{'Name': 'tag:Name', 'Values': [sg_name]}, {'Name': 'group-name', 'Values': [sg_name]}]:
+            sec_groups = list(ec2.security_groups.filter(Filters=[_filter]))
+            if sec_groups:
+                # FIXME: What if we have 2 VPC, with a SG with the same name?!
+                return sec_groups[0]
     except ClientError as e:
         error_code = extract_client_error_code(e)
         if error_code == 'InvalidGroup.NotFound':


Test patch:
diff --git a/tests/test_aws.py b/tests/test_aws.py
index 4ca762a..8dd5b44 100644
--- a/tests/test_aws.py
+++ b/tests/test_aws.py
@@ -14,6 +14,21 @@ def test_get_security_group(monkeypatch):
     assert results == get_security_group('myregion', 'group_inexistant')
 
 
+def test_get_security_group_by_tag_name(monkeypatch):
+
+    def mock_filter(Filters):
+        if Filters[0]['Name'] == 'tag:Name' and Filters[0]['Values'] == ['my-sg']:
+            sg = MagicMock()
+            sg.id = 'sg-123'
+            return [sg]
+
+    ec2 = MagicMock()
+    ec2.security_groups.filter = mock_filter
+    monkeypatch.setattr('boto3.resource', MagicMock(return_value=ec2))
+
+    assert get_security_group('myregion', 'my-sg').id == 'sg-123'
+
+
 def test_resolve_security_groups(monkeypatch):
     ec2 = MagicMock()
     ec2.security_groups.filter = MagicMock(side_effect=[