Skip to content

Commit

Permalink
- Added VpcConfig to codebuild configuration
Browse files Browse the repository at this point in the history
- Making VPC peering network_environment configuration work.
  • Loading branch information
gitwater committed Jan 22, 2022
1 parent d3e4286 commit 931c406
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 11 deletions.
1 change: 1 addition & 0 deletions src/paco/application/reseng_deploymentpipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,7 @@ def init_stage_action_codebuild_build(self, action_config):
stack_tags=self.stack_tags,
stack_hooks=stack_hooks,
extra_context={
'env_ctx': self.env_ctx,
'base_aws_name': self.base_aws_name,
'app_name': self.app.name,
'action_config': action_config,
Expand Down
52 changes: 50 additions & 2 deletions src/paco/cftemplates/codebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@


class CodeBuild(StackTemplate):
def __init__(self, stack, paco_ctx, base_aws_name, app_name, action_config, artifacts_bucket_name):
def __init__(self, stack, paco_ctx, env_ctx, base_aws_name, app_name, action_config, artifacts_bucket_name):
pipeline_config = stack.resource
config_ref = action_config.paco_ref_parts
super().__init__(stack, paco_ctx, iam_capabilities=["CAPABILITY_NAMED_IAM"])
self.set_aws_name('CodeBuild', self.resource_group_name, self.resource.name)

self.env_ctx = env_ctx
# Troposphere Template Initialization
self.init_template('Deployment: CodeBuild')
template = self.template
Expand Down Expand Up @@ -483,6 +483,54 @@ def create_codebuild_cfn(
if action_config.concurrent_build_limit > 0:
project_dict['ConcurrentBuildLimit'] = action_config.concurrent_build_limit

if action_config.vpc_config != None:
vpc_config = action_config.vpc_config
vpc_id_param = self.create_cfn_parameter(
name='VPC',
param_type='AWS::EC2::VPC::Id',
description='The VPC Id',
value='paco.ref netenv.{}.<environment>.<region>.network.vpc.id'.format(self.env_ctx.netenv.name),
)

security_group_list = []
for sg_ref in vpc_config.security_groups:
ref = Reference(sg_ref)
sg_param_name = self.gen_cf_logical_name('SecurityGroupId'+ref.parts[-2]+ref.parts[-1])
sg_param = self.create_cfn_parameter(
name=sg_param_name,
param_type='String',
description='Security Group Id',
value=sg_ref + '.id',
)
security_group_list.append(troposphere.Ref(sg_param))

# security_group_list_param = self.create_cfn_ref_list_param(
# param_type='List<AWS::EC2::SecurityGroup::Id>',
# name='SecurityGroupList',
# description='List of security group ids to attach to CodeBuild.',
# value=vpc_config.security_groups,
# ref_attribute='id',
# )
subnet_list = []
for segment_ref in vpc_config.segments:
segment_name = segment_ref.split('.')[-1]
segment_param = self.create_cfn_parameter(
name=segment_name,
param_type='List<AWS::EC2::Subnet::Id>',
description=f'VPC Subnet Id List for CodeBuild VPC Config',
value=segment_ref + '.subnet_id_list'
)
subnet_list.append(troposphere.Ref(segment_param))

if len(subnet_list) == 0:
raise PacoException("CodeBuild VPC Config must have at least one segment defined.")

project_dict['VpcConfig'] = {
'VpcId': troposphere.Ref(vpc_id_param),
'SecurityGroupIds': security_group_list,
'Subnets': subnet_list
}

project_res = troposphere.codebuild.Project.from_dict(
'CodeBuildProject',
project_dict
Expand Down
30 changes: 25 additions & 5 deletions src/paco/cftemplates/vpc_peering.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from paco.cftemplates.cftemplates import StackTemplate
from paco.models.locations import get_parent_by_interface
from paco.models import schemas
from paco.models import schemas, references
import troposphere
import troposphere.ec2
import troposphere.route53
Expand Down Expand Up @@ -35,18 +35,20 @@ def __init__(
any_peering_enabled = False
for peer in vpc_config.peering.keys():
peer_config = vpc_config.peering[peer]
if peer_config.is_enabled():
if peer_config.is_enabled() and peer_config.peer_type == 'requester':
any_peering_enabled = True
else:
continue

if peer_config.network_environment != None:
peer_config = self.get_peer_config(peer_config)

vpc_peering_connection_res = troposphere.ec2.VPCPeeringConnection(
'VPCPeeringConnection' + peer.title(),
PeerOwnerId = peer_config.peer_account_id,
PeerRegion = peer_config.peer_region,
PeerVpcId = peer_config.peer_vpcid,
PeerRoleArn = 'arn:aws:iam::{}:role/{}'.format(
peer_config.peer_account_id, peer_config.peer_role_name
),
PeerRoleArn = f'arn:aws:iam::{peer_config.peer_account_id}:role/{peer_config.peer_role_name}',
VpcId = troposphere.Ref(vpc_id_param)
)

Expand All @@ -72,3 +74,21 @@ def __init__(

self.set_enabled(any_peering_enabled)

def get_peer_config(self, peer_config):
# Get Config
netenv_ref = references.Reference(peer_config.network_environment + '.network')
netenv_config = netenv_ref.resolve(self.paco_ctx.project)

# Peer Account ID
peer_config.peer_account_id = self.paco_ctx.get_ref(netenv_config.aws_account + '.id')

# Peer Region
peer_config.peer_region = netenv_ref.region

# Peer VPC Id
peer_config.peer_vpcid = self.paco_ctx.get_ref(netenv_config.vpc.paco_ref + '.id')

# Peer Role name is not yet automated and needs manual configuration

return peer_config

2 changes: 1 addition & 1 deletion src/paco/controllers/ctl_ssm.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def wait_for_command(self, ssm_client, account_ctx, region, resource, command_id
print(f"Command stderr: {instance_id}: {command_response['StandardErrorContent']}")
break

# Install the Agent
# Send SSM Command
def send_command(self, account_ctx, region, resource, parameters, targets, document_name):
ssm_client = account_ctx.get_aws_client('ssm', aws_region=region)
ssm_log_group_name = prefixed_name(resource, 'paco_ssm', self.paco_ctx.legacy_flag)
Expand Down
87 changes: 84 additions & 3 deletions src/paco/stack_grps/grp_network.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
from paco import utils
from paco.core.yaml import YAML
from paco.models import schemas, iam
from paco.models.locations import get_parent_by_interface
from paco.models.references import Reference
from paco.stack import StackOrder, Stack, StackGroup, StackTags
from paco.models import schemas
from pprint import pprint
import paco.cftemplates
import paco.models.networks
import paco.models.loader
from paco.models.locations import get_parent_by_interface
from paco import utils
import zope

yaml=YAML()
yaml.default_flow_sytle = False

class NetworkStackGroup(StackGroup):
"""StackGroup to manage all the stacks for a Network: VPC, NAT Gateway, VPC Peering, Security Groups, Segments"""
Expand Down Expand Up @@ -131,6 +137,15 @@ def init(self):
for peer_id in peering_config.keys():
peer_config = vpc_config.peering[peer_id]
peer_config.resolve_ref_obj = self
# Add role to the target network account
if peer_config.network_environment != None and peer_config.peer_type == 'accepter':
netenv_ref = Reference(peer_config.network_environment + '.network')
requester_netenv_config = netenv_ref.resolve(self.paco_ctx.project)
requester_account_id = self.paco_ctx.get_ref(requester_netenv_config.aws_account + '.id')
accepter_vpc_id = self.paco_ctx.get_ref(vpc_config.paco_ref+'.id')
# Only create the role if we are cross account
if self.account_ctx.id != requester_account_id:
self.gen_vpc_peering_accepter_role(peer_config, vpc_config, accepter_vpc_id, requester_account_id)
self.peering_stack = self.add_new_stack(
self.region,
vpc_config,
Expand Down Expand Up @@ -179,6 +194,72 @@ def get_security_group_stack(self, sg_id):
def get_segment_stack(self, segment_id):
return self.segment_dict[segment_id]

def gen_vpc_peering_accepter_role(self, peer_config, vpc_config, accepter_vpc_id, requester_account_id):
iam_ctl = self.paco_ctx.get_controller('IAM')
accepter_region = self.region
accepter_account_id = self.account_ctx.id

role_yaml = f"""
assume_role_policy:
effect: Allow
aws:
- '{requester_account_id}'
instance_profile: false
path: /
policies:
- name: root
statement:
- effect: Allow
action:
- ec2:AcceptVpcPeeringConnection
- effect: Allow
action:
- ec2:AcceptVpcPeeringConnection
condition:
StringEquals:
ec2:AccepterVpc: placeholder
resource:
- 'arn:aws:ec2:{accepter_region}:{accepter_account_id}:vpc-peering-connection/*'
"""

# condition:
# StringEquals:
# 'ec2:AccepterVpc': 'arn:aws:ec2:{accepter_region}:{accepter_account_id}:vpc/{accepter_vpc_id}'


role_config_dict = yaml.load(role_yaml)
role_config_dict['policies'][0]['statement'][1]['condition'] = {
'StringEquals': {
'ec2:AccepterVpc': { "Fn::Sub" : [ f'arn:aws:ec2:{accepter_region}:{accepter_account_id}:vpc/${{VpcId}}', { "VpcId": {"Ref" : "VpcId"} } ] }
}
}
role_config = iam.Role('accepter_role', peer_config)
role_config.apply_config(role_config_dict)
role_config.enabled = True
role_config.role_name = 'Peer-Accepter'

role_config.policies[0].statement[0].resource = [f"!Sub 'arn:aws:ec2:{accepter_region}:{accepter_account_id}:vpc/${{VpcId}}'"]

# IAM Roles Parameters
iam_role_params = [{
'key': 'VpcId',
'value': vpc_config.paco_ref+'.id',
'type': 'String',
'description': 'Acceptor VPC ID'
}]

iam_ctl.add_role(
account_ctx=self.account_ctx,
region=self.region,
resource=peer_config,
role=role_config,
iam_role_id=f'VPC-Peer-{peer_config.name}-Accepter',
stack_group=self,
stack_tags=self.stack_tags,
template_params=iam_role_params
)
peer_config.peer_role_name = iam_ctl.role_name(role_config.paco_ref_parts)

def resolve_ref(self, ref):
if ref.raw.endswith('network.vpc.id'):
return self.vpc_stack
Expand Down

0 comments on commit 931c406

Please sign in to comment.