-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2076c3e
commit 1226e31
Showing
29 changed files
with
1,414 additions
and
1 deletion.
There are no files selected for viewing
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
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" | ||
``` |
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
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.
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
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 | ||
} |
Oops, something went wrong.