This message was deleted.
# python
s
This message was deleted.
m
What is
InboundSecurityGroupRules
?
Ah, yes — what tenwit says is true: resource names are plain strings; they can’t use Outputs.
b
Copy code
from typing import List, Mapping, Optional, Sequence

import pulumi
import pulumi_aws as aws

from . import VPC_ID


class SecurityGroup(aws.ec2.SecurityGroup):
    """Creates a security group"""

    def __init__(
        self,
        name: str,
        description: str,
        vpc_id: str = VPC_ID,
        ingress: Optional[Sequence[aws.ec2.SecurityGroupIngressArgs]] = None,
        egress: Optional[Sequence[aws.ec2.SecurityGroupEgressArgs]] = None,
        tags: Optional[Mapping[str, str]] = None,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:

        # Append "-sg" to name if not already present
        if name.endswith("-sg"):
            sg_name = name
        else:
            sg_name = f"{name}-sg"

        super().__init__(
            resource_name=sg_name,
            name=sg_name,
            description=description,
            vpc_id=vpc_id,
            ingress=ingress,
            egress=egress,
            opts=opts,
            tags={"Name": sg_name},
        )
        pulumi.export(name, {"security_group_id": self.id})


class CIDRSecurityGroupRule(aws.ec2.SecurityGroupRule):
    """Creates a security group rule to/from a CIDR"""

    def __init__(
        self,
        name: str,
        security_group_id: str,
        from_port: int,
        to_port: int,
        protocol: str,
        type: str,
        cidr_blocks: Optional[List[str]] = None,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:
        sgr_name = f"{name}-sgr"
        super().__init__(
            resource_name=sgr_name,
            security_group_id=security_group_id,
            type=type,
            from_port=from_port,
            to_port=to_port,
            protocol=protocol,
            cidr_blocks=cidr_blocks,
        )
        pulumi.export(name, {"security_group_rule_id": self.id})


class SGSecurityGroupRule(aws.ec2.SecurityGroupRule):
    """Creates a security group rule to/from a security group"""

    def __init__(
        self,
        name: str,
        security_group_id: str,
        from_port: int,
        to_port: int,
        protocol: str,
        type: str,
        source_security_group_id: Optional[str] = None,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:
        sgr_name = f"{name}-sgr"
        super().__init__(
            resource_name=sgr_name,
            security_group_id=security_group_id,
            type=type,
            from_port=from_port,
            to_port=to_port,
            protocol=protocol,
            source_security_group_id=source_security_group_id,
        )
        pulumi.export(name, {"security_group_rule_id": self.id})


class InboundSecurityGroupRules:
    """Creates security group rules for one TCP port from one or more CIDRs and/or one security group"""

    def __init__(
        self,
        parent_sg_id: str,
        port: int,
        source_cidrs: Optional[List[str]] = None,
        source_sg_id: Optional[str] = None,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:

        if source_cidrs is not None:

            if isinstance(source_cidrs, str):
                subnets = [source_cidrs]
                rule_name_suffix = source_cidrs
            else:
                subnets = [subnet.value for subnet in source_cidrs]
                rule_name_suffix = source_cidrs.__name__

            self.cidr_rule = CIDRSecurityGroupRule(
                name=f"{parent_sg_id}-{rule_name_suffix}",
                cidr_blocks=subnets,
                security_group_id=parent_sg_id,
                from_port=port,
                to_port=port,
                opts=opts,
                type="ingress",
                protocol="tcp",
            )

        if source_sg_id is not None:
            self.sg_rule = SGSecurityGroupRule(
                name=f"{parent_sg_id}-{source_sg_id}",
                source_security_group_id=source_sg_id,
                security_group_id=parent_sg_id,
                from_port=port,
                to_port=port,
                opts=opts,
                type="ingress",
                protocol="tcp",
            )

        if source_cidrs is None and source_sg_id is None:
            raise ValueError("Must provide at least one of source_cidrs or source_sg_id")


class OutboundSecurityGroupRules:
    """Creates security group rules for one TCP port to one or more CIDRs and/or one security group"""

    def __init__(
        self,
        parent_sg_id: str,
        port: int,
        destination_cidrs: Optional[List[str]] = None,
        destination_sg_id: Optional[str] = None,
        opts: Optional[pulumi.ResourceOptions] = None,
    ) -> None:

        if destination_cidrs is not None:
            if isinstance(destination_cidrs, str):
                subnets = [destination_cidrs]
                rule_name_suffix = destination_cidrs
            else:
                subnets = [subnet.value for subnet in destination_cidrs]
                rule_name_suffix = destination_cidrs.__name__

            self.cidr_rule = CIDRSecurityGroupRule(
                name=f"{parent_sg_id}-{rule_name_suffix}",
                cidr_blocks=subnets,
                security_group_id=parent_sg_id,
                from_port=port,
                to_port=port,
                type="egress",
                protocol="tcp",
            )

        if destination_sg_id is not None:
            self.sg_rule = SGSecurityGroupRule(
                name=f"{parent_sg_id}-{destination_sg_id}",
                source_security_group_id=destination_sg_id,
                security_group_id=parent_sg_id,
                from_port=port,
                to_port=port,
                type="egress",
                protocol="tcp",
            )

        if destination_cidrs is None and destination_sg_id is None:
            raise ValueError("Must provide at least one of destination_cidrs or destination_sg_id")
Yeah, that makes sense now...i think. Now I'm just trying to figure out how to create unique security group rule names without using the id of the security group they're attached to.
I'm trying to use the string all over the place in the last snippet, so that clearly won't work.
Still very new to pulumi 😅
m
I see, yeah — and because you’re using a component resource, you can’t hard code the string as you would with regular resource names, because instantiating the component more than once would fail.
b
Yes, exactly
m
I honestly haven’t hit this myself, but I’m sure there’s a pattern for it. 🤔
b
I've scoured the internet, it seems like this would be a fairly common thing to want to do (e.g. create a group, then create and attach rules)
m
well, the component resource itself needs a unique name —
i wonder if you could just use that?
in other words, the caller needs to provide a name
b
each instance of the component resource?
m
right, yes. because component resources are themselves resources, they need names of their own
python isn’t my first language, but i’ll see if i can get you an example of what i mean
here are the docs on writing component resources — this is likely what you want: https://www.pulumi.com/docs/intro/concepts/resources/components/
👀 1
this is slightly different from what you have here i think, which if i’m understanding correctly is inheriting directly from AWS resources. instead, what you want is to subclass
pulumi.ComponentResource
it’s a Python class just like what you have already, just a special class that gets tracked as a first-class resource (with its own name) and can parent other resources, so would probably be a good fit for what you need here.
when callers instantiate your component resource class, they’ll have to pass it a string name — you can then use this unique (to the stack) string name to name all of the child resources belonging to your class.
which i think is what you’re trying do — does that sound right?
b
Ah ok, I think that makes sense! I'll give it a shot. Thanks so much for your help @miniature-musician-31262
m
great! you bet. yeah, this stuff does take a little getting used to. 😅
b
In some places I instantiate
InboundSecurityGroupRule
(for example) multiple times to handle multiple sets of CIDRs etc from various sources for one security group, so I may also need to refactor the class to handle that, or maybe that means I'll need to rethink the approach. In any case I think I understand some of this a bit better!
👍 1
🙌 1