Click image to expand
-
-
Save jeshan/3b8eb9d3feb014006674147e99382c26 to your computer and use it in GitHub Desktop.
cfnbuddy requirements template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: "2010-09-09" | |
Description: resources required for clients to use cfnbuddy | |
Metadata: | |
AWS::CloudFormation::Interface: | |
ParameterGroups: | |
- Label: GitHub/GitLab | |
Parameters: | |
- GitIntegrationType | |
- Namespace | |
- GitRepositoryName | |
- PersonalAccessToken | |
- Label: GitHub | |
Parameters: | |
- GitHubDomain | |
- Label: GitLab | |
Parameters: | |
- GitLabDomain | |
- Label: CfnBuddySettings | |
Parameters: | |
- AllowViewBudgets | |
- ImageBackground | |
ParameterLabels: | |
ImageBackground: | |
default: White or Transparent background for images? | |
Namespace: | |
default: GitHub or GitLab namespace under which to create the repository | |
GitHubDomain: | |
default: GitHub domain | |
GitIntegrationType: | |
default: Your Git Provider | |
GitLabDomain: | |
default: GitLab domain | |
AllowViewBudgets: | |
default: Allow Viewing Budgets? | |
GitRepositoryName: | |
default: Git Repository Name | |
PersonalAccessToken: | |
default: Personal Access Token | |
Parameters: | |
GitIntegrationType: | |
Type: String | |
AllowedValues: [CodeCommit, GitHub, GitLab] | |
Default: CodeCommit | |
Description: Which integration to use | |
Namespace: | |
Type: String | |
Default: '' | |
Description: | | |
For GitHub: it's either your personal or organization name. | |
For GitLab: it's either your username or your group name | |
Otherwise, leave empty. | |
PersonalAccessToken: | |
Type: String | |
Default: '' | |
Description: 'GitHub or GitLab: the access token that has permissions to clone and create the repository.' | |
NoEcho: true | |
GitHubDomain: | |
Type: String | |
Description: 'The domain where your GitHub instance is hosted, format: hub.example.com. Leave default if not self-hosting' | |
Default: github.com | |
GitLabDomain: | |
Type: String | |
Description: 'The domain where your GitLab instance is hosted, format: lab.example.com. Leave default if not self-hosting' | |
Default: gitlab.com | |
GitRepositoryName: | |
Type: String | |
Default: '' | |
Description: 'Name of the repository to be created/used by cfnbuddy. Default: cfnbuddy-YOUR_ACCOUNT_ID' | |
AllowViewBudgets: | |
Type: String | |
AllowedValues: ['Yes', 'No'] | |
Default: 'No' | |
Description: Needed to create the AWS::Budgets::Budget resources (optional because the api is not free and is estimated to cost $7.20 per month) | |
ImageBackground: | |
Type: String | |
AllowedValues: [White, Transparent] | |
Default: White | |
Description: Images can be placed on either a white or a transparent background (White is just fine; Changeable at any time) | |
Conditions: | |
isGitHub: !Equals [!Ref GitIntegrationType, GitHub] | |
isGitLab: !Equals [!Ref GitIntegrationType, GitLab] | |
isNeedSync: !Or [!Condition isGitHub, !Condition isGitLab] | |
isAllowViewBudgets: !Equals [!Ref AllowViewBudgets, Yes] | |
hasGitRepositoryName: !Not [!Equals [!Ref GitRepositoryName, '']] | |
Resources: | |
Role: | |
Type: AWS::IAM::Role | |
Properties: | |
RoleName: cfnbuddy-client | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: sts:AssumeRole | |
Effect: Allow | |
Principal: | |
AWS: '880876798629' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/ReadOnlyAccess | |
- !Ref AcceptPrPolicy | |
- !Ref GitCredentialsPolicy | |
- !Ref DenyUnnecessaryPermissionsPolicy | |
- !If [isNeedSync, !Ref SyncPolicy, !Ref 'AWS::NoValue'] | |
- !If [isAllowViewBudgets, !Ref ViewBudgetPolicy, !Ref 'AWS::NoValue'] | |
AcceptPrPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- codecommit:MergePullRequestByFastForward # we need this to merge CodeCommit pull requests into master | |
Resource: !Sub '${Repo.Arn}' | |
Version: '2012-10-17' | |
GitCredentialsPolicy: # we need this to access the new CodeCommit repository | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- iam:CreateServiceSpecificCredential | |
- iam:ResetServiceSpecificCredential | |
- iam:ListServiceSpecificCredentials | |
Resource: !GetAtt CodeCommitUser.Arn | |
- Effect: Allow | |
Action: | |
- codecommit:CreatePullRequest | |
- codecommit:GitPull | |
Resource: !Sub '${Repo.Arn}' | |
- Effect: Allow | |
Action: | |
- codecommit:GitPush | |
Resource: !Sub '${Repo.Arn}' | |
Condition: | |
StringEqualsIfExists: | |
codecommit:References: [refs/heads/master] | |
"Null": | |
codecommit:References: false | |
SyncPolicy: # needed to launch the GitHub/GitLab sync job | |
Type: AWS::IAM::ManagedPolicy | |
Condition: isNeedSync | |
Properties: | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- codebuild:StartBuild | |
Resource: !If [isGitHub, !GetAtt GitHubSyncJob.Arn, !If [isGitLab, !GetAtt GitLabSyncJob.Arn, !Ref 'AWS::NoValue']] | |
DenyUnnecessaryPermissionsPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Deny | |
Action: | |
- dynamodb:GetItem | |
- dynamodb:BatchGetItem | |
- dynamodb:Query | |
- dynamodb:Scan | |
- ec2:GetConsoleOutput | |
- ec2:GetConsoleScreenshot | |
- ecr:BatchGetImage | |
- ecr:GetAuthorizationToken | |
- ecr:GetDownloadUrlForLayer | |
- kinesis:Get* | |
- lambda:GetFunction | |
- logs:GetLogEvents | |
- s3:GetObject | |
- sdb:Select* | |
- sqs:ReceiveMessage | |
Resource: '*' | |
Repo: | |
Type: AWS::CodeCommit::Repository | |
DeletionPolicy: Retain | |
UpdateReplacePolicy: Retain | |
Properties: | |
RepositoryName: !If [hasGitRepositoryName, !Ref GitRepositoryName, !Sub 'cfnbuddy-${AWS::AccountId}'] | |
GitHubSyncJob: | |
Type: AWS::CodeBuild::Project | |
Condition: isGitHub | |
Properties: | |
Artifacts: | |
Type: NO_ARTIFACTS | |
Description: Syncs cfnbuddy CodeCommit repo to your GitHub repo | |
Environment: | |
ComputeType: BUILD_GENERAL1_SMALL | |
EnvironmentVariables: | |
- Name: ACCOUNT_ID | |
Value: !Ref AWS::AccountId | |
- Name: BASE_DOMAIN | |
Value: !Ref GitHubDomain | |
- Name: REPOSITORY_NAME | |
Value: !GetAtt Repo.Name | |
- Name: NAMESPACE | |
Value: !Ref Namespace | |
- Name: PERSONAL_ACCESS_TOKEN | |
Type: SECRETS_MANAGER | |
Value: !Ref PersonalAccessTokenSecret | |
Image: aws/codebuild/standard:3.0 | |
Type: LINUX_CONTAINER | |
QueuedTimeoutInMinutes: 50 | |
ServiceRole: !GetAtt SyncJobRole.Arn | |
Source: | |
BuildSpec: | | |
version: 0.2 | |
phases: | |
install: | |
commands: | |
- pip3 install requests | |
runtime-versions: | |
python: 3.8 | |
build: | |
commands: | |
- | | |
cat <<EOF > /tmp/index.py | |
import os | |
from subprocess import call | |
from urllib.parse import quote_plus | |
import requests | |
repository_name = os.environ['REPOSITORY_NAME'] | |
namespace = os.environ['NAMESPACE'] | |
account_id = os.environ['ACCOUNT_ID'] | |
token = os.environ['PERSONAL_ACCESS_TOKEN'] | |
region = os.environ['AWS_REGION'] | |
base_domain = os.environ['BASE_DOMAIN'] | |
api_path = f'https://api.{base_domain}' if base_domain == 'github.com' else f'https://{base_domain}/api' | |
base_branch = 'master' | |
headers = {'Authorization': f'token {token}', 'Accept': 'application/vnd.github.v3+json'} | |
def main(tag_name, release_description): | |
create_repository() | |
qtoken = quote_plus(token) | |
run(f'git remote add github https://{namespace}:{qtoken}@{base_domain}/{namespace}/{repository_name}.git') | |
run('git push -q github --all') | |
run('git push -q github --tags') | |
create_release(tag_name, release_description) | |
def run(command): | |
code = call(command.split(' ')) | |
if code != 0: | |
raise Exception('failed to run command') | |
def create_repository(): | |
body = { | |
'name': repository_name, | |
'description': f'Automatically created by cfnbuddy for AWS account {account_id}', | |
'private': True, | |
} | |
is_org = requests.get(f'{api_path}/orgs/{namespace}').status_code == 200 | |
if is_org: | |
print('Creating repo in org...') | |
response = requests.post(f'{api_path}/orgs/{namespace}/repos', json=body, headers=headers) | |
else: | |
print("Creating repo in user's personal space") | |
response = requests.post(f'{api_path}/user/repos', json=body, headers=headers) | |
requests.patch(f'{api_path}/repos/{namespace}/{repository_name}', json={'default_branch': base_branch}, headers=headers) | |
def create_release(tag_name, body): | |
print(f'Releasing {tag_name}') | |
response = requests.post( | |
f'{api_path}/repos/{namespace}/{repository_name}/releases', | |
json={'name': tag_name, 'tag_name': tag_name, 'target_commitish': base_branch, 'body': body}, | |
headers=headers, | |
).json() | |
print(response) | |
if __name__ == '__main__': | |
main(os.environ['TAG_NAME'], os.environ['RELEASE_DESCRIPTION']) | |
EOF | |
- "for i in `git branch -a | grep remote | grep -v HEAD | grep -v master`; do git branch -q --track ${i#remotes/origin/} $i; done" # track all branches | |
- python /tmp/index.py | |
Location: !GetAtt Repo.CloneUrlHttp | |
Type: CODECOMMIT | |
LogsConfig: | |
CloudWatchLogs: | |
GroupName: !Ref SyncLogs | |
Status: ENABLED | |
SourceVersion: master | |
GitLabSyncJob: | |
Type: AWS::CodeBuild::Project | |
Condition: isGitLab | |
Properties: | |
Artifacts: | |
Type: NO_ARTIFACTS | |
Description: Syncs cfnbuddy CodeCommit repo to your GitLab repo | |
Environment: | |
ComputeType: BUILD_GENERAL1_SMALL | |
EnvironmentVariables: | |
- Name: ACCOUNT_ID | |
Value: !Ref AWS::AccountId | |
- Name: BASE_DOMAIN | |
Value: !Ref GitLabDomain | |
- Name: REPOSITORY_NAME | |
Value: !GetAtt Repo.Name | |
- Name: NAMESPACE | |
Value: !Ref Namespace | |
- Name: PERSONAL_ACCESS_TOKEN | |
Type: SECRETS_MANAGER | |
Value: !Ref PersonalAccessTokenSecret | |
Image: aws/codebuild/standard:3.0 | |
Type: LINUX_CONTAINER | |
QueuedTimeoutInMinutes: 50 | |
ServiceRole: !GetAtt SyncJobRole.Arn | |
Source: | |
BuildSpec: | | |
version: 0.2 | |
phases: | |
install: | |
commands: | |
- pip3 install requests | |
runtime-versions: | |
python: 3.8 | |
build: | |
commands: | |
- | | |
cat <<EOF > /tmp/index.py | |
import os | |
from subprocess import call | |
from urllib.parse import quote_plus | |
import requests | |
repository_name = os.environ['REPOSITORY_NAME'] | |
namespace = os.environ['NAMESPACE'] | |
account_id = os.environ['ACCOUNT_ID'] | |
token = os.environ['PERSONAL_ACCESS_TOKEN'] | |
region = os.environ['AWS_REGION'] | |
base_domain = os.environ['BASE_DOMAIN'] | |
api_path = f'https://{base_domain}/api' | |
base_branch = 'master' | |
headers = {'Private-Token': token} | |
def main(tag_name, release_description): | |
create_repository() | |
qtoken = quote_plus(token) | |
run(f'git remote add gitlab https://{namespace}:{qtoken}@{base_domain}/{namespace}/{repository_name}.git') | |
run('git push -q gitlab --all') | |
run('git push -q gitlab --tags') | |
create_release(tag_name, release_description) | |
def run(command): | |
code = call(command.split(' ')) | |
if code != 0: | |
raise Exception('failed to run command') | |
def create_repository(): | |
namespace_id = None | |
user = requests.get(f'{api_path}/v4/user', headers=headers).json() | |
user_id = user['id'] | |
username = user['username'] | |
print(f'Got user id: {user_id}, {username}') | |
if username != namespace: | |
namespace_response = requests.get(f'{api_path}/v4/namespaces/{quote_plus(namespace)}', headers=headers).json() | |
namespace_id = namespace_response.get('id') | |
body = { | |
'name': repository_name, | |
'description': f'Automatically created by cfnbuddy for AWS account {account_id}', | |
'visibility': 'private', | |
'namespace_id': namespace_id, | |
'tag_list': ['cfnbuddy'], | |
'request_access_enabled': True, | |
'issues_enabled': False, | |
'merge_requests_enabled': False, | |
'jobs_enabled': False, | |
'wiki_enabled': False, | |
'snippets_enabled': False, | |
'container_registry_enabled': False, | |
'shared_runners_enabled': False, | |
} | |
response = requests.post(f'{api_path}/v4/projects', headers=headers, json=body) | |
if response.status_code not in [200, 201]: | |
print(response.json()) | |
print( | |
f'Could not create repository for user {namespace} (user id: {user_id}) in namespace {namespace_id}, got status code: {response.status_code}' | |
) | |
else: | |
print(f'Created repository for user {namespace}') | |
print(f'Setting default branch to {base_branch}') | |
response = requests.put( | |
f'{api_path}/v4/projects/{namespace_path()}', headers=headers, json={'default_branch': base_branch} | |
) | |
print(response.json()) | |
def namespace_path(): | |
return quote_plus(f'{namespace}/{repository_name}') | |
def create_release(tag_name, description): | |
print(f'Releasing {tag_name} on {base_domain}') | |
release_response = requests.post( | |
f'{api_path}/v4/projects/{namespace_path()}/releases', headers=headers, | |
json={'name': tag_name, 'tag_name': tag_name, 'ref': base_branch, 'description': description}, | |
).json() | |
print(release_response) | |
if __name__ == '__main__': | |
main(os.environ['TAG_NAME'], os.environ['RELEASE_DESCRIPTION']) | |
EOF | |
- "for i in `git branch -a | grep remote | grep -v HEAD | grep -v master`; do git branch -q --track ${i#remotes/origin/} $i; done" # track all branches | |
- python /tmp/index.py | |
Location: !GetAtt Repo.CloneUrlHttp | |
Type: CODECOMMIT | |
LogsConfig: | |
CloudWatchLogs: | |
GroupName: !Ref SyncLogs | |
Status: ENABLED | |
SourceVersion: master | |
SyncJobRole: | |
Type: AWS::IAM::Role | |
Condition: isNeedSync | |
Properties: | |
AssumeRolePolicyDocument: | |
Statement: | |
- Action: sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: codebuild.amazonaws.com | |
Policies: | |
- PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- codecommit:GitPull | |
Resource: !Sub '${Repo.Arn}' | |
- Effect: Allow | |
Action: | |
- secretsmanager:GetSecretValue | |
Resource: !If [isNeedSync, !Ref PersonalAccessTokenSecret, !Ref 'AWS::NoValue'] | |
- Effect: Allow | |
Resource: | |
- !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${SyncLogs}' | |
- !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${SyncLogs}/*' | |
- !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${SyncLogs}:log-stream:*' | |
Action: | |
- logs:CreateLogGroup | |
- logs:CreateLogStream | |
- logs:PutLogEvents | |
Version: '2012-10-17' | |
PolicyName: sync-job-policy | |
PersonalAccessTokenSecret: | |
Type: AWS::SecretsManager::Secret | |
Condition: isNeedSync | |
Properties: | |
Description: Your GitHub/GitLab access token privately stored in your account. It never leaves your account | |
Name: /cfnbuddy/personal-access-token | |
SecretString: !Ref PersonalAccessToken | |
SyncLogs: | |
Type: AWS::Logs::LogGroup | |
Condition: isNeedSync | |
Properties: | |
RetentionInDays: 7 | |
CodeCommitUser: | |
Type: AWS::IAM::User | |
Properties: | |
ManagedPolicyArns: | |
- !Ref GitPullPushPolicy | |
GitPullPushPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Properties: | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Action: | |
- codecommit:GitPull | |
- codecommit:GitPush | |
Resource: !Sub 'arn:aws:codecommit:${AWS::Region}:${AWS::AccountId}:${Repo.Name}' | |
ViewBudgetPolicy: | |
Type: AWS::IAM::ManagedPolicy | |
Condition: isAllowViewBudgets | |
Properties: | |
PolicyDocument: | |
Statement: | |
- Effect: Allow | |
Action: | |
- budgets:ViewBudget | |
Resource: !Sub 'arn:aws:budgets::${AWS::AccountId}:budget/*' | |
Version: '2012-10-17' | |
GitRepositoryNameParam: | |
Type: AWS::SSM::Parameter | |
Properties: | |
Name: /cfnbuddy/git-repository-name | |
Type: String | |
Value: !GetAtt Repo.Name | |
CodeCommitParam: | |
Type: AWS::SSM::Parameter | |
Properties: | |
Name: /cfnbuddy/codecommit-username | |
Type: String | |
Value: !Ref CodeCommitUser | |
SyncJobParam: | |
Type: AWS::SSM::Parameter | |
Condition: isNeedSync | |
Properties: | |
Name: /cfnbuddy/sync-job | |
Type: String | |
Value: !If [isGitHub, !Ref GitHubSyncJob, !If [isGitLab, !Ref GitLabSyncJob, !Ref 'AWS::NoValue']] | |
ImageBackgroundParam: | |
Type: AWS::SSM::Parameter | |
Properties: | |
Name: /cfnbuddy/image-background | |
Type: String | |
Value: !Ref ImageBackground |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment