Skip to content

Commit

Permalink
[Initial-Commit] Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
hs-amartin committed May 26, 2021
1 parent 2076c3e commit 1226e31
Show file tree
Hide file tree
Showing 29 changed files with 1,414 additions and 1 deletion.
173 changes: 172 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,172 @@
# terraform-aws-jenkins
# terraform-aws-jenkins
To deploy this module you need to have:
1. An existing VPC with at least two subnets
2. A DockerHub hosted Jenkins image, and credentials with access to the image (and/or an override image)
3. The url and ssl cert for the url you will redirect to the LB once it's created (module does not create route53 entry)
4. A github application, and a github user account with credentials to fill in below (password is the PAT for the account, secret is the secret from the app)
5. Once run, create a DNS entry pointing from the url provided to the dns record outputed by this module

Example minimal inputs are below (you need to fill in the blanks)
```hcl
inputs = {
dockerhub_credentials_arn = ___
github_client_id = ___
jenkins_master_image = ___
jenkins_url = ___
kms_keys = []
private_subnet_ids = dependency.vpc.outputs.private_subnets
public_subnet_ids = dependency.vpc.outputs.public_subnets
secrets = [
{
Name = "JENKINS_CRED_GITHUB_SECRET"
ValueFrom = ___
},
{
Name = "JENKINS_CRED_GITHUB_PASSWORD"
ValueFrom = ___
}
]
tls_certificate_arn = ___
vpc_id = dependency.vpc.outputs.vpc_id
}
```

## Jenkins Master Image
Example Dockerfile. Note this allows for setup without user interference
```dockerfile
FROM jenkins/jenkins:lts-alpine

ENV JAVA_OPTS -Djenkins.install.runSetupWizard=false
ENV JENKINS_OPTS --httpKeepAliveTimeout=30000

COPY plugins.txt /usr/share/jenkins/ref/plugins.txt
RUN jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt

ENV CASC_JENKINS_CONFIG $REF/casc.yaml
COPY casc.yml $REF/casc.yaml
```

Example casc.yml. This uses the `github-oauth` for user management, `job-dsl` to seed jobs, and `amazon-ecs` to spin up agents on demand. All variables must be injected in terraform/ECS task.
```yaml
---
credentials:
system:
domainCredentials:
- credentials:
- usernamePassword:
id: JENKINS_CRED_GITHUB
password: ${JENKINS_CRED_GITHUB_PASSWORD}
scope: GLOBAL
username: ${JENKINS_GITHUB_USERNAME}
- string:
description: JENKINS_CRED_SLACK_INTEGRATION_TOKEN
id: JENKINS_CRED_SLACK_INTEGRATION_TOKEN
scope: GLOBAL
secret: ${JENKINS_CRED_SLACK_INTEGRATION_TOKEN}
jenkins:
authorizationStrategy:
globalMatrix:
permissions:
- Agent/Build:myorg
- Job/Build:myorg
- Job/Read:myorg
- Lockable Resources/View:myorg
- Overall/Administer:myorg*jenkins-admin
- Overall/Read:myorg
- Run/Update:myorg
- SCM/Tag:myorg
- View/Configure:myorg
- View/Create:myorg
- View/Delete:myorg
- View/Read:myorg
clouds:
- ecs:
name: example-agent
allowedOverrides: inheritFrom,label,memory,cpu
cluster: ${JENKINS_EXAMPLE_AGENT_ARN}
credentialsId: ''
jenkinsUrl: ${JENKINS_URL}
regionName: us-east-1
retentionTimeout: 5
slaveTimeoutInSeconds: 300
templates:
- assignPublicIp: false
cpu: 0
executionRole: ${JENKINS_EXAMPLE_AGENT_TASK_EXECUTION_ROLE_ARN}
label: agent-int
launchType: FARGATE
memory: 0
memoryReservation: 0
networkMode: awsvpc
platformVersion: 1.4.0
privileged: false
remoteFSRoot: /home/jenkins
securityGroups: ${JENKINS_EXAMPLE_AGENT_SECURITY_GROUP}
sharedMemorySize: 0
subnets: ${JENKINS_EXAMPLE_AGENT_SUBNETS}
taskDefinitionOverride: ${JENKINS_EXAMPLE_AGENT_TASK_DEFINITION_ARN}
uniqueRemoteFSRoot: false
crumbIssuer: standard
mode: NORMAL
numExecutors: 5
remotingSecurity:
enabled: true
scmCheckoutRetryCount: 2
securityRealm:
github:
clientID: ${JENKINS_GITHUB_CLIENTID}
clientSecret: ${JENKINS_CRED_GITHUB_SECRET}
githubApiUri: https://api.github.com
githubWebUri: https://github.com
oauthScopes: read:org,user:email,repo
slaveAgentPort: 50000
systemMessage: |
Jenkins configured automatically by Jenkins Configuration as Code plugin
jobs:
- script: |
job('Job_DSL_Seed') {
scm {
git {
remote {
url('https://github.com/myorg/myseedrepo.git')
credentials('JENKINS_CRED_GITHUB')
}
branch('main')
}
}
steps {
jobDsl {
targets('jobs/**/*.groovy')
removedJobAction('DELETE')
removedViewAction('DELETE')
removedConfigFilesAction('DELETE')
}
}
triggers {
githubPush()
hudsonStartupTrigger {
quietPeriod("90")
runOnChoice("ON_CONNECT")
label("")
nodeParameterName("")
}
}
}
security:
globalJobDslSecurityConfiguration:
useScriptSecurity: false
queueItemAuthenticator:
authenticators:
- global:
strategy: triggeringUsersAuthorizationStrategy
unclassified:
location:
adminAddress: [email protected]
url: ${JENKINS_URL}
slackNotifier:
botUser: false
room: "jenkins-deploys"
sendAsText: false
teamDomain: "mydomain"
tokenCredentialId: "JENKINS_CRED_SLACK_INTEGRATION_TOKEN"
```
74 changes: 74 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
data "github_ip_ranges" "gh_ranges" {}

locals {
secret_arns = concat(
[for secret in var.secrets : secret.ValueFrom],
[var.dockerhub_credentials_arn],
var.kms_keys)
}

module "efs" {
source = "./modules/efs"
subnet_ids = var.private_subnet_ids
vpc_id = var.vpc_id
}

module "iam" {
source = "./modules/iam"
agent_creation_policies = var.agent_creation_policies
jenkins_jobs_iam_policy_arn = module.efs.jenkins_jobs_iam_policy_arn
secret_arns = local.secret_arns
}

module "alb" {
source = "./modules/alb"

public_cidr_blocks = concat(data.github_ip_ranges.gh_ranges.hooks, var.public_http_cidr_blocks)
private_cidr_blocks = var.private_http_cidr_blocks

public_subnet_ids = var.public_subnet_ids
private_subnet_ids = var.private_subnet_ids
vpc_id = var.vpc_id

tls_certificate_arn = var.tls_certificate_arn
}

locals {
environment_variables = concat(
var.environment_variables,
[
{
name = "JENKINS_URL"
value = "https://${var.jenkins_url}"
}
]
)
}

resource "aws_security_group_rule" "instance_outbound" {
type = "egress"
description = "jenkins outbound"
from_port = 0
to_port = 65535
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
security_group_id = module.alb.target_sg_id
}

module "ecs" {
source = "./modules/ecs"
dockerhub_credentials_arn = var.dockerhub_credentials_arn
environment_variables = local.environment_variables
file_system_id = module.efs.jenkins_jobs_id
jenkins_master_image = var.jenkins_master_image
jobs_access_point_id = module.efs.jenkins_jobs_ap_id
private_target_group_arn = module.alb.private_target_group_arn
public_target_group_arn = module.alb.public_target_group_arn
sg_ids = [module.alb.target_sg_id, module.efs.sg_id]
secrets = var.secrets
subnet_ids = var.private_subnet_ids
task_role_arn = module.iam.jenkins_master_task_role_arn
task_execution_role_arn = module.iam.jenkins_master_execution_role_arn
workspace_access_point_id = module.efs.jenkins_workspace_ap_id
vpc_id = var.vpc_id
}
Empty file added modules/agent/README.md
Empty file.
105 changes: 105 additions & 0 deletions modules/agent/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
locals {
secret_arns = concat(
[for secret in var.secrets : secret.ValueFrom],
[var.dockerhub_credentials_arn],
var.kms_keys)
}

module "iam" {
source = "./modules/iam"
secret_arns = local.secret_arns
}

module "ecs" {
source = "./modules/ecs"
dockerhub_credentials_arn = var.dockerhub_credentials_arn
environment_variables = var.environment_variables
jenkins_agent_image = var.jenkins_agent_image
secrets = var.secrets
task_role_arn = module.iam.jenkins_agent_task_role_arn
task_execution_role_arn = module.iam.jenkins_agent_execution_role_arn
vpc_id = var.vpc_id
}

resource "aws_iam_policy" "create_tasks" {
name = "jenkins_master_create_agent_tasks"
description = "Access top create jenkins agent tasks"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = [
"ecs:ListClusters",
"ecs:DescribeContainerInstances",
"ecs:ListTaskDefinitions",
"ecs:DescribeTaskDefinition"
]
Effect = "Allow"
Resource = "*"
}, {
Action = [
"iam:PassRole"
]
Effect = "Allow"
Resource = [module.iam.jenkins_agent_execution_role_arn, module.iam.jenkins_agent_task_role_arn]
},
{
Action = [
"ecs:ListContainerInstances",
"ecs:DescribeClusters"
]
Effect = "Allow"
Resource = module.ecs.jenkins_agent_cluster_arn
},
{
Action = [
"ecs:RunTask"
]
Effect = "Allow"
Resource = module.ecs.jenkins_agent_task_definition_arn
Condition = {
"ArnEquals" = {
"ecs:cluster" = [module.ecs.jenkins_agent_cluster_arn]
}
}
},
{
Action = [
"ecs:DescribeTasks",
"ecs:StopTask"
]
Effect = "Allow"
Resource = "arn:aws:ecs:*:*:task/*"
Condition = {
"ArnEquals" = {
"ecs:cluster" = [module.ecs.jenkins_agent_cluster_arn]
}
}
}
]
})
}

resource "aws_iam_role" "agent_task_creation_role" {
name = "jenkins_agent_task_creation_role"

# Terraform's "jsonencode" function converts a
# Terraform expression result to valid JSON syntax.
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = { "AWS" : "arn:aws:iam::${var.jenkins_master_account}:root" }
},
]
})
}

resource "aws_iam_role_policy_attachment" "agent_task_creation_attachment" {
role = aws_iam_role.agent_task_creation_role.name
policy_arn = aws_iam_policy.create_tasks.arn
}
Loading

0 comments on commit 1226e31

Please sign in to comment.