Skip to content

Commit 66e7f91

Browse files
authored
feat: ec2 sec-groups to describe security groups (#492)
``` aec ec2 sec-groups GroupId GroupName Description VpcId ─────────────────────────────────────────────────────────────────────────────────────── sg-25d2fabfa34eaa065 default default VPC security group vpc-b1f00f09091896c57 default default default vpc-b1f00f09091896c57 ```
1 parent 6bdd1bc commit 66e7f91

File tree

6 files changed

+119
-24
lines changed

6 files changed

+119
-24
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
include *.mk
22

33
## generate docs
4+
.PHONY: docs
45
docs: $(venv)
56
$(venv)/bin/cog -r docs/*.md
67
# trim trailing whitespace so hooks are happy

docs/ami.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ cog.out(f"```\n{docs('aec ami describe', ami.describe(config, owner='09972010947
3636
]]] -->
3737
```
3838
aec ami describe
39-
39+
4040
Name ImageId CreationDate RootDeviceName Size
41-
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
42-
ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-20170727 ami-1e749f67 2024-01-24T10:50:11.000Z /dev/sda1 15
43-
ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20170721 ami-785db401 2024-01-24T10:50:11.000Z /dev/sda1 15
41+
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
42+
ubuntu/images/hvm-ssd/ubuntu-trusty-14.04-amd64-server-20170727 ami-1e749f67 2025-04-01T09:46:15.000Z /dev/sda1 15
43+
ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20170721 ami-785db401 2025-04-01T09:46:15.000Z /dev/sda1 15
4444
```
4545
<!-- [[[end]]] -->
4646

docs/ec2.md

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,22 @@ from aec.main import build_parser
1515
cog.out(f"```\n{build_parser()._subparsers._actions[1].choices['ec2'].format_help()}```")
1616
]]] -->
1717
```
18-
usage: aec ec2 [-h] {create-key-pair,describe,launch,logs,modify,start,stop,subnets,rename,tag,tags,status,templates,terminate,user-data} ...
18+
usage: aec ec2 [-h]
19+
{create-key-pair,describe,launch,logs,modify,start,stop,sec-groups,subnets,rename,tag,tags,status,templates,terminate,user-data} ...
1920
2021
optional arguments:
2122
-h, --help show this help message and exit
2223
2324
subcommands:
24-
{create-key-pair,describe,launch,logs,modify,start,stop,subnets,rename,tag,tags,status,templates,terminate,user-data}
25+
{create-key-pair,describe,launch,logs,modify,start,stop,sec-groups,subnets,rename,tag,tags,status,templates,terminate,user-data}
2526
create-key-pair Create a key pair.
2627
describe List EC2 instances in the region.
2728
launch Launch a tagged EC2 instance with an EBS volume.
2829
logs Show the system logs.
2930
modify Change an instance's type.
3031
start Start EC2 instance.
3132
stop Stop EC2 instance.
33+
sec-groups Describe security groups in the region, optionally filtered by VPC ID.
3234
subnets Describe subnets.
3335
rename Rename EC2 instance(s).
3436
tag Tag EC2 instance(s).
@@ -77,11 +79,11 @@ cog.out(f"```\n{docs('aec ec2 describe', ec2.describe(config))}\n```")
7779
]]] -->
7880
```
7981
aec ec2 describe
80-
81-
InstanceId State Name Type DnsName LaunchTime ImageId
82-
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
83-
i-b39b1ea60119e503e running alice t3.small ec2-54-214-187-100.compute-1.amazonaws.com 2024-01-24 10:50:11+00:00 ami-03cf127a
84-
i-52d4b17a9a8586a31 running sam t3.small ec2-54-214-105-52.compute-1.amazonaws.com 2024-01-24 10:50:11+00:00 ami-03cf127a
82+
83+
InstanceId State Name Type DnsName LaunchTime ImageId
84+
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
85+
i-d11c784672f583196 running alice t3.small ec2-54-214-8-190.compute-1.amazonaws.com 2025-04-01 09:46:15+00:00 ami-03cf127a
86+
i-919d8e2adf1445b8d running sam t3.small ec2-54-214-106-199.compute-1.amazonaws.com 2025-04-01 09:46:16+00:00 ami-03cf127a
8587
```
8688
<!-- [[[end]]] -->
8789

@@ -116,11 +118,11 @@ cog.out(f"```\n{docs('aec ec2 describe -c Name,SubnetId,Volumes,Image.CreationDa
116118
]]] -->
117119
```
118120
aec ec2 describe -c Name,SubnetId,Volumes,Image.CreationDate
119-
120-
Name SubnetId Volumes Image.CreationDate
121-
──────────────────────────────────────────────────────────────────────
122-
alice subnet-8ffb733b ['Size=15 GiB'] 2024-01-24T10:50:11.000Z
123-
sam subnet-8ffb733b ['Size=15 GiB'] 2024-01-24T10:50:11.000Z
121+
122+
Name SubnetId Volumes Image.CreationDate
123+
───────────────────────────────────────────────────────────────────────────────
124+
alice subnet-3c34502dfd1bc971e ['Size=15 GiB'] 2025-04-01T09:46:15.000Z
125+
sam subnet-3c34502dfd1bc971e ['Size=15 GiB'] 2025-04-01T09:46:15.000Z
124126
```
125127
<!-- [[[end]]] -->
126128

@@ -212,15 +214,30 @@ cog.out(f"```\n{docs('aec ec2 subnets', ec2.subnets(config))}\n```")
212214
]]] -->
213215
```
214216
aec ec2 subnets
217+
218+
SubnetId VpcId AvailabilityZone CidrBlock Name
219+
─────────────────────────────────────────────────────────────────────────────────────────────
220+
subnet-3c34502dfd1bc971e vpc-b1f00f09091896c57 us-east-1a 172.31.0.0/20
221+
subnet-569837d96526af096 vpc-b1f00f09091896c57 us-east-1b 172.31.16.0/20
222+
subnet-8a3b095072e46bf09 vpc-b1f00f09091896c57 us-east-1c 172.31.32.0/20
223+
subnet-c3e0557e8334ec7b5 vpc-b1f00f09091896c57 us-east-1d 172.31.48.0/20
224+
subnet-67ffe1d070fd23148 vpc-b1f00f09091896c57 us-east-1e 172.31.64.0/20
225+
subnet-fbfe5e577853e5221 vpc-b1f00f09091896c57 us-east-1f 172.31.80.0/20
226+
```
227+
<!-- [[[end]]] -->
215228

216-
SubnetId VpcId AvailabilityZone CidrBlock Name
217-
───────────────────────────────────────────────────────────────────────────
218-
subnet-8ffb733b vpc-df045ae9 us-east-1a 172.31.0.0/20
219-
subnet-50f11bb4 vpc-df045ae9 us-east-1b 172.31.16.0/20
220-
subnet-93811557 vpc-df045ae9 us-east-1c 172.31.32.0/20
221-
subnet-f17e6261 vpc-df045ae9 us-east-1d 172.31.48.0/20
222-
subnet-1a5d6685 vpc-df045ae9 us-east-1e 172.31.64.0/20
223-
subnet-b12557cf vpc-df045ae9 us-east-1f 172.31.80.0/20
229+
Describe security groups:
230+
231+
<!-- [[[cog
232+
cog.out(f"```\n{docs('aec ec2 sec-groups', ec2.sec_groups(config))}\n```")
233+
]]] -->
234+
```
235+
aec ec2 sec-groups
236+
237+
GroupId GroupName Description VpcId
238+
───────────────────────────────────────────────────────────────────────────────────────
239+
sg-25d2fabfa34eaa065 default default VPC security group vpc-b1f00f09091896c57
240+
default default default vpc-b1f00f09091896c57
224241
```
225242
<!-- [[[end]]] -->
226243

src/aec/command/ec2.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,38 @@ def status_text(summary: InstanceStatusSummaryTypeDef, key: str = "reachability"
617617
)
618618

619619

620+
def sec_groups(config: Config, vpc_id: str | None = None) -> list[dict[str, str | None]]:
621+
"""
622+
Describe security groups in the region, optionally filtered by VPC ID.
623+
"""
624+
625+
ec2_client = boto3.client("ec2", region_name=config.get("region", None))
626+
627+
# Prepare filters
628+
filters: list[FilterTypeDef] = []
629+
if vpc_id:
630+
filters.append({"Name": "vpc-id", "Values": [vpc_id]})
631+
632+
paginator = ec2_client.get_paginator("describe_security_groups")
633+
pages = paginator.paginate(Filters=filters)
634+
635+
groups_info: list[dict[str, str | None]] = []
636+
637+
for page in pages:
638+
groups_info.extend(
639+
{
640+
"GroupId": sg["GroupId"],
641+
"GroupName": sg.get("GroupName"),
642+
"Description": sg.get("Description"),
643+
"VpcId": sg.get("VpcId"),
644+
}
645+
for sg in page["SecurityGroups"]
646+
)
647+
648+
# Sort the results, e.g., by VpcId then GroupName
649+
return sorted(groups_info, key=lambda g: (g["VpcId"], g["GroupName"]))
650+
651+
620652
def subnets(config: Config, vpc_id: str | None = None) -> list[dict[str, Any]]:
621653
"""Describe subnets."""
622654

src/aec/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ def tag_arg_checker(tag: str) -> str:
8686
config_arg,
8787
Arg("ident", type=str, help="Name tag of instance or instance id")
8888
]),
89+
Cmd(ec2.sec_groups, [
90+
config_arg,
91+
Arg("-v", "--vpc-id", help="Filter to these VPCs"),
92+
]),
8993
Cmd(ec2.subnets, [
9094
config_arg,
9195
Arg("-v", "--vpc-id", help="Filter to these VPCs"),

tests/test_ec2.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
logs,
2121
modify,
2222
rename,
23+
sec_groups,
2324
start,
2425
status,
2526
stop,
@@ -467,3 +468,43 @@ def test_user_data_missing(mock_aws_config: Config):
467468

468469
data = user_data(config=mock_aws_config, ident="no_userdata")
469470
assert not data
471+
472+
473+
def test_sec_groups_no_filter(mock_aws_config: Config):
474+
"""Test retrieving security groups without filters."""
475+
# Default VPC should have a default security group
476+
groups = sec_groups(mock_aws_config)
477+
478+
assert len(groups) >= 1
479+
480+
group = groups[0]
481+
assert "GroupId" in group
482+
assert "GroupName" in group
483+
assert "Description" in group
484+
assert "VpcId" in group
485+
486+
487+
def test_sec_groups_with_vpc_filter(mock_aws_config: Config):
488+
"""Test retrieving security groups with VPC filter."""
489+
ec2_client = boto3.client("ec2", region_name=mock_aws_config["region"])
490+
491+
# Create a new VPC in addition to the default VPC
492+
# Will come with a default security group
493+
vpc_response = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")
494+
vpc_id = vpc_response["Vpc"]["VpcId"]
495+
496+
try:
497+
# Get all security groups
498+
all_groups = sec_groups(mock_aws_config)
499+
500+
# Get security groups for the specific VPC
501+
vpc_groups = sec_groups(mock_aws_config, vpc_id=vpc_id)
502+
503+
# Verify filtering works
504+
assert len(vpc_groups) >= 1
505+
assert all(group["VpcId"] == vpc_id for group in vpc_groups)
506+
507+
# Verify total count is higher (includes the default VPC's groups)
508+
assert len(all_groups) > len(vpc_groups)
509+
finally:
510+
ec2_client.delete_vpc(VpcId=vpc_id)

0 commit comments

Comments
 (0)