Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .github/workflows/pulumi_staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ env:
#GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DEPLOY_ENVIRONMENT: staging
AWS_ROLE_TO_ASSUME: ${{ secrets.STAGING_GITHUB_ACTIONS_AWS_ROLE_ARN }}
PULUMI_CLOUD_URL_STAGING: ${{ vars.PULUMI_CLOUD_URL_STAGING }} # the s3 backend url: s3://xxx
jobs:
## ============================================================================
## PR Comment Trigger Handler
Expand Down Expand Up @@ -286,7 +287,7 @@ jobs:
run: |
cd ${{ env.tf_working_dir }}
export PULUMI_CONFIG_PASSPHRASE=""
pulumi login ${{ vars.PULUMI_CLOUD_URL_STAGING }}
pulumi login ${{ env.PULUMI_CLOUD_URL_STAGING }}

# Check if stack exists, create it if it doesn't
STACK_NAME="organization/${{ steps.getStackName.outputs.result }}/${{ env.DEPLOY_ENVIRONMENT }}"
Expand Down Expand Up @@ -366,7 +367,7 @@ jobs:
uses: pulumi/actions@v6
with:
always-include-summary: true
cloud-url: ${{ vars.PULUMI_CLOUD_URL_STAGING }}
cloud-url: ${{ env.PULUMI_CLOUD_URL_STAGING }}
command: preview
comment-on-pr: true
comment-on-summary: true
Expand All @@ -392,7 +393,7 @@ jobs:
run: |
cd ${{ env.tf_working_dir }}
export PULUMI_CONFIG_PASSPHRASE=""
pulumi login ${{ vars.PULUMI_CLOUD_URL_STAGING }}
pulumi login ${{ env.PULUMI_CLOUD_URL_STAGING }}

# Ensure stack exists before running up
STACK_NAME="organization/${{ steps.getStackName.outputs.result }}/${{ env.DEPLOY_ENVIRONMENT }}"
Expand All @@ -414,7 +415,7 @@ jobs:
)
with:
always-include-summary: true
cloud-url: ${{ vars.PULUMI_CLOUD_URL_STAGING }}
cloud-url: ${{ env.PULUMI_CLOUD_URL_STAGING }}
command: up
comment-on-pr: true
comment-on-summary: true
Expand Down
1 change: 1 addition & 0 deletions pulumi/components/aws/vpc/PulumiPlugin.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
runtime: python
221 changes: 221 additions & 0 deletions pulumi/components/aws/vpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# VPC Component

A reusable Pulumi component for creating AWS VPC infrastructure with public and private subnets.

## Features

- **VPC**: Creates a VPC with configurable CIDR block and DNS support
- **Public Subnets**: Configurable number of public subnets (2-3 typical) across availability zones with auto-assign public IP
- **Private Subnets**: Configurable number of private subnets (2-3 typical) across availability zones
- **Internet Gateway**: For public subnet internet connectivity
- **Route Tables**: Properly configured route tables for public and private subnets
- **Default Security Group**: Allows all inbound and outbound traffic (configurable)
- **Optional NAT Gateways**: One NAT Gateway per availability zone for private subnet internet access
- **Flexible Subnet Count**: Supports 2 or 3 subnets (or more) - simply provide the desired number of CIDR blocks

## Usage

### In Pulumi YAML

```yaml
name: my-vpc
runtime: yaml
packages:
vpc: https://github.com/ManagedKube/devops-with-ai.git/pulumi/components/aws/vpc@0.0.1

resources:
my-vpc:
type: vpc:index:Vpc
properties:
vpcCidr: "10.0.0.0/16"
publicSubnetCidrs:
- "10.0.1.0/24"
- "10.0.2.0/24"
- "10.0.3.0/24"
privateSubnetCidrs:
- "10.0.11.0/24"
- "10.0.12.0/24"
- "10.0.13.0/24"
availabilityZones:
- "us-west-2a"
- "us-west-2b"
- "us-west-2c"
enableNatGateway: true
vpcName: "my-application-vpc"
tagsAdditional:
Environment: "production"
ManagedBy: "pulumi"
```

### In Python

```python
from vpc import Vpc

vpc = Vpc(
"my-vpc",
{
"vpcCidr": "10.0.0.0/16",
"publicSubnetCidrs": [
"10.0.1.0/24",
"10.0.2.0/24",
"10.0.3.0/24",
],
"privateSubnetCidrs": [
"10.0.11.0/24",
"10.0.12.0/24",
"10.0.13.0/24",
],
"availabilityZones": [
"us-west-2a",
"us-west-2b",
"us-west-2c",
],
"enableNatGateway": True,
"vpcName": "my-application-vpc",
"tagsAdditional": {
"Environment": "production",
"ManagedBy": "pulumi",
},
},
)

# Access outputs
vpc_id = vpc.vpc_id
public_subnet_ids = vpc.public_subnet_ids
private_subnet_ids = vpc.private_subnet_ids
```

### Example with 2 Subnets

You can create a VPC with just 2 subnets by providing 2 CIDR blocks:

```yaml
resources:
my-vpc-two-subnets:
type: vpc:index:Vpc
properties:
vpcCidr: "10.0.0.0/16"
publicSubnetCidrs:
- "10.0.1.0/24"
- "10.0.2.0/24"
privateSubnetCidrs:
- "10.0.11.0/24"
- "10.0.12.0/24"
availabilityZones:
- "us-east-1a"
- "us-east-1b"
enableNatGateway: false
vpcName: "my-two-subnet-vpc"
tagsAdditional:
Environment: "development"
```

## Parameters

### Required Parameters

- **vpcCidr** (string): CIDR block for the VPC (e.g., "10.0.0.0/16")
- **publicSubnetCidrs** (list[string]): List of CIDR blocks for public subnets (e.g., 2-3 subnets)
- **privateSubnetCidrs** (list[string]): List of CIDR blocks for private subnets (e.g., 2-3 subnets)
- **availabilityZones** (list[string]): List of availability zones to use (must match the number of subnets)
- **tagsAdditional** (dict): Additional tags to apply to all resources

**Note**: The number of subnets is determined by the length of the `publicSubnetCidrs` list. Provide 2 CIDR blocks for 2 subnets, 3 for 3 subnets, etc.

### Optional Parameters

- **enableNatGateway** (boolean): Whether to create NAT Gateways in each private subnet. Default: `false`
- **vpcName** (string): Name for the VPC. If not provided, uses the resource name

## Outputs

- **vpc_id**: The ID of the VPC
- **vpc_arn**: The ARN of the VPC
- **public_subnet_ids**: List of public subnet IDs
- **private_subnet_ids**: List of private subnet IDs
- **internet_gateway_id**: The ID of the Internet Gateway
- **nat_gateway_ids**: List of NAT Gateway IDs (empty if NAT Gateways are disabled)
- **default_security_group_id**: The ID of the default security group

## Architecture

### Without NAT Gateways (enableNatGateway: false)

```
┌─────────────────────────────────────────────────────────────┐
│ VPC │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Public │ │ Public │ │ Public │ │
│ │ Subnet AZ-1 │ │ Subnet AZ-2 │ │ Subnet AZ-3 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Internet │ │
│ │ Gateway │ │
│ └────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Private │ │ Private │ │ Private │ │
│ │ Subnet AZ-1 │ │ Subnet AZ-2 │ │ Subnet AZ-3 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```

### With NAT Gateways (enableNatGateway: true)

```
┌─────────────────────────────────────────────────────────────┐
│ VPC │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Public │ │ Public │ │ Public │ │
│ │ Subnet AZ-1 │ │ Subnet AZ-2 │ │ Subnet AZ-3 │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │ NAT GW │ │ │ │ NAT GW │ │ │ │ NAT GW │ │ │
│ │ └────┬─────┘ │ │ └────┬─────┘ │ │ └────┬─────┘ │ │
│ └──────┼───────┘ └──────┼───────┘ └──────┼───────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ Internet │ │
│ │ Gateway │ │
│ └────────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Private │ │ Private │ │ Private │ │
│ │ Subnet AZ-1 │ │ Subnet AZ-2 │ │ Subnet AZ-3 │ │
│ │ ▲ │ │ ▲ │ │ ▲ │ │
│ │ │ │ │ │ │ │ │ │ │
│ └──────┼───────┘ └──────┼───────┘ └──────┼───────┘ │
│ │ │ │ │
│ └──────────────────┴──────────────────┘ │
│ Routes to respective NAT Gateway │
└─────────────────────────────────────────────────────────────┘
```

## Testing

To run the unit tests:

```bash
cd pulumi/components/aws/vpc
python3 -m venv venv
source venv/bin/activate
pip install -r requirements-test.txt
python -m pytest tests/ -v
```

See [tests/README.md](tests/README.md) for more information on testing.

## Notes

- The default security group allows all inbound and outbound traffic. This is suitable for development but should be restricted for production use.
Copy link

Copilot AI Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The component’s "default security group" is documented as allowing all inbound and outbound traffic, which in the implementation corresponds to an ec2.SecurityGroup with all protocols/ports open to 0.0.0.0/0. Any resource attached to this group will be fully exposed to the public internet, so an attacker can scan and reach any listening service. Consider making this security group least-privilege by default (no or minimal inbound rules) and/or exposing configuration to define restrictive ingress/egress rules, with an open SG only as an explicit opt-in for development.

Copilot uses AI. Check for mistakes.
- NAT Gateways incur additional costs. Only enable them if private subnets need internet access.
- The component creates one NAT Gateway per availability zone when enabled, providing high availability but at higher cost.
- All resources are tagged with the provided `tagsAdditional` for easy identification and cost tracking.
5 changes: 5 additions & 0 deletions pulumi/components/aws/vpc/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from pulumi.provider.experimental import component_provider_host
from vpc import Vpc

if __name__ == "__main__":
component_provider_host(name="vpc", components=[Vpc])
4 changes: 4 additions & 0 deletions pulumi/components/aws/vpc/requirements-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pulumi>=3.130.0
pulumi-aws>=6.50.1
pytest>=7.4.0
pytest-asyncio>=0.21.0
2 changes: 2 additions & 0 deletions pulumi/components/aws/vpc/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pulumi>=3.130.0
pulumi-aws>=6.50.1
Loading
Loading