Skip to content

Commit

Permalink
Added account specific codeartifact cdk and workflow (#695)
Browse files Browse the repository at this point in the history
  • Loading branch information
kyhau authored Oct 5, 2024
1 parent a3efd8e commit 70d0236
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 83 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/codeartifact-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,29 @@ jobs:
ENV: ${{ needs.common.outputs.environment }}
run: |
make test-with-cdk-validator-cfnguard
account-stack:
name: Test account-specific resources IaC
runs-on: ubuntu-latest
defaults:
run:
working-directory: CodeArtifact/cdk/account_resources
env:
ENV_STAGE: dev
steps:
- uses: actions/checkout@v4
- run: make lint-python
- uses: actions/setup-node@v4
with:
node-version: 22
- name: Set up aws-cdk
run: make install-cdk
- name: Print deployment environment
run: |
echo "INFO: cdk version: $(cdk --version)"
echo "INFO: node version: $(node --version)"
echo "INFO: npm version: $(npm --version)"
echo "INFO: python3 version: $(python3 --version)"
- name: Run cdk synth
run: make synth
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.

### Added
* Added [CodeArtifact/cdk/central_resources/](CodeArtifact/cdk/central_resources/) cdk and workflow for deploying a central_resources for hosting CodeArtifact external connections and a shared CodeArtifact repository.
* Added [CodeArtifact/cdk/account_resources](CodeArtifact/cdk/account_resources/) cdk and workflow for deploying a default repository that has linked with upstreams internal-shared and external connections with the same CodeArtifact domain.
* Added `black` configurations in [pyproject.toml](./pyproject.toml).
* Added `flake8` configurations in [setup.cfg](./setup.cfg).

Expand Down
38 changes: 38 additions & 0 deletions CodeArtifact/cdk/account_resources/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export AWS_DEFAULT_REGION ?= ap-southeast-2
export CDK_DEFAULT_REGION ?= ap-southeast-2
export ENV_STAGE ?= dev

APP_NAME=$(shell grep -m 1 AppName environment/$(ENV_STAGE).yml | cut -c 10-)

install-cdk:
npm install -g aws-cdk
python3 -m pip install -U pip
pip3 install -r requirements-dev.txt

synth:
cdk synth -c env=${ENV_STAGE} --all

deploy:
pip3 install -r requirements.txt
cdk deploy $(APP_NAME) -c env=${ENV_STAGE} --require-approval never

destroy:
cdk destroy $(APP_NAME) -f -c env=${ENV_STAGE}

test-cdk:
python3 -m pytest tests/

pre-commit: format-python lint-python lint-yaml test

format-python:
black **.py

lint-python:
pip3 install flake8
flake8 **.py

lint-yaml:
yamllint -c .github/linters/.yaml-lint.yml -f parsable .

clean:
rm -rf cdk.out **/__pycache__
135 changes: 135 additions & 0 deletions CodeArtifact/cdk/account_resources/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
from os.path import dirname, join, realpath

import yaml
from aws_cdk import App, CfnOutput, CliCredentialsStackSynthesizer, Environment, Stack, Tags
from aws_cdk import aws_iam as iam
from aws_cdk.aws_codeartifact import CfnRepository
from constructs import Construct

ENV_DIR = join(dirname(realpath(__file__)), "environment")


class CodeArtifactRepoStack(Stack):
def __init__(self, scope: Construct, id: str, config: dict, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

# Create CodeArtifact repository
repo = CfnRepository(
self,
"CodeArtifactRepo",
description="Default repository for sharing within the AWS Organization with upstreams internal-shared and external connections.",
domain_name=config["DOMAIN_NAME"],
domain_owner=config["DOMAIN_OWNER"],
repository_name=config["REPO_NAME"],
upstreams=config["UPSTREAMS"],
permissions_policy_document=iam.PolicyDocument(
statements=[
iam.PolicyStatement(
sid="ReadOnlyRepositoryAccess",
effect=iam.Effect.ALLOW,
actions=[
"codeartifact:DescribePackageVersion",
"codeartifact:DescribeRepository",
"codeartifact:GetPackageVersionReadme",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ListPackages",
"codeartifact:ListPackageVersions",
"codeartifact:ListPackageVersionAssets",
"codeartifact:ListPackageVersionDependencies",
"codeartifact:ListTagsForResource",
"codeartifact:ReadFromRepository",
],
resources=["*"],
principals=[iam.AnyPrincipal()],
conditions={"StringEquals": {"aws:PrincipalOrgId": [config["AWS_ORG_ID"]]}},
),
iam.PolicyStatement(
sid="WriteRepositoryAccess",
effect=iam.Effect.ALLOW,
actions=[
"codeartifact:CopyPackageVersions",
"codeartifact:DeletePackage",
"codeartifact:DeletePackageVersions",
"codeartifact:PublishPackageVersion",
"codeartifact:PutPackageMetadata",
],
resources=["*"],
principals=[iam.AnyPrincipal()],
conditions={
"ArnLike": {"aws:PrincipalArn": config["REPO_WRITE_ACCESS_ARNS_LIKE"]}
},
),
iam.PolicyStatement(
sid="FullRepositoryAccess",
effect=iam.Effect.ALLOW,
actions=[
"codeartifact:AssociateExternalConnection",
"codeartifact:CopyPackageVersions",
"codeartifact:DeletePackage",
"codeartifact:DeletePackageVersions",
"codeartifact:DeleteRepository",
"codeartifact:DeleteRepositoryPermissionsPolicy",
"codeartifact:DescribePackageVersion",
"codeartifact:DescribeRepository",
"codeartifact:DisassociateExternalConnection",
"codeartifact:DisposePackageVersions",
"codeartifact:GetPackageVersionReadme",
"codeartifact:GetRepositoryEndpoint",
"codeartifact:ListPackageVersionAssets",
"codeartifact:ListPackageVersionDependencies",
"codeartifact:ListPackageVersions",
"codeartifact:ListPackages",
"codeartifact:PublishPackageVersion",
"codeartifact:PutPackageMetadata",
"codeartifact:PutRepositoryPermissionsPolicy",
"codeartifact:ReadFromRepository",
"codeartifact:TagResource",
"codeartifact:UpdatePackageVersionsStatus",
"codeartifact:UpdateRepository",
"codeartifact:UntagResource",
],
resources=["*"],
principals=[iam.AnyPrincipal()],
conditions={
"ArnLike": {"aws:PrincipalArn": config["REPO_FULL_ACCESS_ARNS_LIKE"]}
},
),
]
),
)
CfnOutput(self, "CodeArtifactRepoArn", value=repo.attr_arn)
CfnOutput(self, "CodeArtifactRepoDomainName", value=repo.attr_domain_name)
CfnOutput(self, "CodeArtifactRepoDomainOwner", value=repo.attr_domain_owner)
CfnOutput(self, "CodeArtifactRepoName", value=repo.attr_name)


def main():
app = App()

ENV_NAME = app.node.try_get_context("env") or "dev"

with open(join(ENV_DIR, f"{ENV_NAME}.yml"), "r") as stream:
yaml_data = yaml.safe_load(stream)
config = yaml_data if yaml_data is not None else {}

stack = CodeArtifactRepoStack(
scope=app,
id="CodeArtifactRepoStack",
config=config,
env=Environment(
account=config["AWS_ACCOUNT"],
region=config["AWS_REGION"],
),
synthesizer=CliCredentialsStackSynthesizer(),
termination_protection=(ENV_NAME == "prd"),
)

for key, value in config["TAGS"].items():
Tags.of(stack).add(key, value)

app.synth()


if __name__ == "__main__":
main()
5 changes: 5 additions & 0 deletions CodeArtifact/cdk/account_resources/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"app": "python3 app.py",
"context": {
}
}
15 changes: 15 additions & 0 deletions CodeArtifact/cdk/account_resources/environment/dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
AWS_ACCOUNT: "333333333333"
AWS_REGION: ap-southeast-2
AWS_ORG_ID: o-12324567890
DOMAIN_NAME: todo-dev
DOMAIN_OWNER: "111111111111"
REPO_NAME: todo-default
UPSTREAMS:
- internal-shared
REPO_FULL_ACCESS_ARNS_LIKE:
- arn:aws:iam::333333333333:role/deploy-role
REPO_WRITE_ACCESS_ARNS_LIKE:
- arn:aws:iam::333333333333:role/codeartifact-repo-write
TAGS:
CostCentre: TODO
Project: TODO
6 changes: 6 additions & 0 deletions CodeArtifact/cdk/account_resources/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-r requirements.txt
black~=24.8
boto3~=1.28
cdk-nag~=2.27
flake8~=6.0
pytest~=7.4
4 changes: 4 additions & 0 deletions CodeArtifact/cdk/account_resources/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
aws-cdk-lib==2.158.0
cdklabs.cdk-validator-cfnguard==0.0.58
constructs==10.3.0
pyyaml==6.0.1
49 changes: 9 additions & 40 deletions CodeArtifact/cdk/central_resources/app.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,12 @@
#!/usr/bin/env python3
from os.path import (
dirname,
join,
realpath,
)
from os.path import dirname, join, realpath

import yaml
from aws_cdk import (
App,
CliCredentialsStackSynthesizer,
Environment,
Tags,
)
from cdklabs.cdk_validator_cfnguard import (
CfnGuardValidator,
)
from lib.central_resources import (
CodeArtifactCentralResources,
)
from aws_cdk import App, CliCredentialsStackSynthesizer, Environment, Tags
from cdklabs.cdk_validator_cfnguard import CfnGuardValidator
from lib.central_resources import CodeArtifactCentralResources

ENV_DIR = join(
dirname(realpath(__file__)),
"environment",
)
ENV_DIR = join(dirname(realpath(__file__)), "environment")


def main():
Expand All @@ -44,13 +28,7 @@ def main():

ENV_NAME = app.node.try_get_context("env") or "dev"

with open(
join(
ENV_DIR,
f"{ENV_NAME}.yaml",
),
"r",
) as stream:
with open(join(ENV_DIR, f"{ENV_NAME}.yml"), "r") as stream:
yaml_data = yaml.safe_load(stream)
config = yaml_data if yaml_data is not None else {}

Expand All @@ -70,18 +48,9 @@ def main():
for key, value in config["TAGS"].items():
Tags.of(stack).add(key, value)

Tags.of(stack).add(
"Description",
"CodeArtifact central resources",
)
Tags.of(stack).add(
"Environment",
ENV_NAME,
)
Tags.of(stack).add(
"Name",
stack.stack_name,
)
Tags.of(stack).add("Description", "CodeArtifact central resources")
Tags.of(stack).add("Environment", ENV_NAME)
Tags.of(stack).add("Name", stack.stack_name)

app.synth()

Expand Down
52 changes: 9 additions & 43 deletions CodeArtifact/cdk/central_resources/lib/central_resources.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
from aws_cdk import (
CfnOutput,
Stack,
aws_iam,
aws_kms,
)
from aws_cdk.aws_codeartifact import (
CfnDomain,
CfnRepository,
)
from constructs import (
Construct,
)
from lib.kms import (
create_kms_key_and_alias,
)
from aws_cdk import CfnOutput, Stack, aws_iam, aws_kms
from aws_cdk.aws_codeartifact import CfnDomain, CfnRepository
from constructs import Construct
from lib.kms import create_kms_key_and_alias


class CodeArtifactCentralResources(Stack):
def __init__(
self,
scope: Construct,
id: str,
config: dict,
**kwargs,
) -> None:
super().__init__(
scope,
id,
**kwargs,
)
def __init__(self, scope: Construct, id: str, config: dict, **kwargs) -> None:
super().__init__(scope, id, **kwargs)

key_admin_arns = [
aws_iam.Role.from_role_arn(
Expand Down Expand Up @@ -77,21 +55,9 @@ def __init__(
write_statement,
)

CfnOutput(
self,
"CodeArtifactDomainName",
value=domain.domain_name,
)
CfnOutput(
self,
"CodeArtifactDomainOwner",
value=self.account,
)
CfnOutput(
self,
"InternalSharedRepoName",
value=internal_shared_repo.repository_name,
)
CfnOutput(self, "CodeArtifactDomainName", value=domain.domain_name)
CfnOutput(self, "CodeArtifactDomainOwner", value=self.account)
CfnOutput(self, "InternalSharedRepoName", value=internal_shared_repo.repository_name)

def create_domain_with_domain_policy(
self,
Expand Down

0 comments on commit 70d0236

Please sign in to comment.