Jira.yaml
---

jira Application Stack CloudFormation Deployment

This CloudFormation template will deploy an application node for jira

AWSTemplateFormatVersion: '2010-09-09' Description: jira Application Stack Metadata: Description: Provides a jira Application complete with EC2, ALB, and RDS AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Infrastructure Options Parameters: - VPC - InstanceSubnet - Label: default: EC2 Instance Configuration Parameters: - InstanceType - KeyName - Label: default: Operational Configuration Parameters: - CIDR Parameters: InstanceType: Type: String Description: The Instance type to use Default: 't2.large' VPC: Type: 'AWS::EC2::VPC::Id' Description: The VPC to support ASG instance capacity and ALB KeyName: Type: 'AWS::EC2::KeyPair::KeyName' Description: The SSH Keypair for the jira Instances Default: ToolsTeamKey InstanceSubnet: Type: 'AWS::EC2::Subnet::Id' Description: The private subnet for the application LBSubnetA: Type: 'AWS::EC2::Subnet::Id' Description: private subnet A for load balancer LBSubnetB: Type: 'AWS::EC2::Subnet::Id' Description: private subnet B for load balancer SSLCertificatARN: Type: String Description: Full ARN of the SSL Certificate to use on the load balancer Default: "copy this from certificate manager" CIDR: Type: String Description: The Cidr allowed to access jira publicly. Default: '0.0.0.0/0' InstallerFile: Type: String Description: Filename of jira archive to install. Default: UA_Jira8132.tar.gz jiraFoundationStack: Type: String Description: Name of the jira foundation stack. Default: JiraFoundation jiraRDSStack: Type: String Description: Name of the jira RDS stack. Default: JiraRDS jiraEnvironment: Type: String Description: prd, tst, or dev environments. Default: tst HostedZoneName: Type: String Description: route53 hosted zone name Default: uits-nonprod-aws.arizona.edu ServiceURL: Type: String Description: Public service URL (I.E jira.arizona.edu) Default: 'jira.arizona.edu'

Tags

The following tags are applied to all resources created by this template.

ServiceTag: Type: String Description: Exact name of the Service as defined in the service catalog. EnvironmentTag: Type: String Description: Used to distinguish between development, test, production,etc. environment types. AllowedValues: [dev, tst, prd, trn, stg, cfg, sup, rpt] Default: tst ContactNetidTag: Type: String Description: Used to identify the netid of the person most familiar with the usage of the resource. Default: szgilbert AccountNumberTag: Type: String Description: Identifies the financial system account number. SubAccountNumberTag: Type: String Description: Identifies the Sub account, which is NOT a number. TicketNumberTag: Type: String Description: Used to identify the jira, Cherwell, or other ticketing system ticket number to link to more information about the need for the resource. Mappings: RegionMap: us-east-1: "AMIID": "ami-c481fad3" us-west-1: "AMIID": "ami-de347abe" us-west-2: "AMIID": "ami-f2d3638a" eu-west-1: "AMIID": "ami-d41d58a7" Resources: InstanceSG: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: 'Allow traffic to jira' VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: "tcp" FromPort: "80" ToPort: "80" SourceSecurityGroupId: !Ref LBSecurityGroup Description: "HTTP Redirection traffic from Load Balancer" - IpProtocol: "tcp" FromPort: "8088" ToPort: "8088" SourceSecurityGroupId: !Ref LBSecurityGroup Description: "HTTP Application Traffic from Load Balancer" - IpProtocol: "tcp" FromPort: "22" ToPort: "22" CidrIp: "150.135.112.0/22" Description: "Infradev VPN" - IpProtocol: "tcp" FromPort: "22" ToPort: "22" CidrIp: "10.138.0.0/17" Description: "Mosaic VPN" - IpProtocol: "tcp" FromPort: "22" ToPort: "22" CidrIp: "128.196.130.211/32" Description: "ben.uits Bastian Host" EFSIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${jiraFoundationStack}-efs-sg" IpProtocol: tcp FromPort: '2049' ToPort: '2049' SourceSecurityGroupId: !Ref InstanceSG DBIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${jiraRDSStack}-dbsecuritygroup" IpProtocol: tcp FromPort: '3306' ToPort: '3306' SourceSecurityGroupId: !Ref InstanceSG EC2Role: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / Policies: - PolicyName: s3-access PolicyDocument: Statement: - Effect: Allow Action: - 's3:*' Resource: !Sub - "arn:aws:s3:::${S3Bucket}*" - S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" - PolicyName: logs-access PolicyDocument: Statement: - Effect: Allow Action: - 'logs:CreateLogStream' - 'logs:PutLogEvents' Resource: '*' EC2InstanceProfile: Type: 'AWS::IAM::InstanceProfile' Properties: Path: / Roles: - !Ref EC2Role jiraInstance: Type: "AWS::EC2::Instance" Metadata: AWS::CloudFormation::Authentication: rolebased: type: "S3" buckets: - Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" roleName: !Ref EC2Role AWS::CloudFormation::Init: configSets: bootStrap: - "configureUser" - "downloadMedia" - "installMedia" - "configureServer" - "configureAccess" - "uninstallJava" - "configureService" - "installMysql" configureUser: groups: jiraadm: {} users: jira: groups: - jiraadm homeDir: /opt/jira downloadMedia: files: /tmp/jira.tgz: mode: 644 owner: jira group: jiraadm source: !Sub - "https://s3-${AWS::Region}.amazonaws.com/${S3Bucket}/installers/${InstallerFile}" - S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" installMedia: packages: yum: nginx: [] commands: 01ensureDirectory: command: "mkdir -p /opt/jira" 02extractjira: command: "cd /tmp; tar -zxf /tmp/jira.tgz" 03movejira: command: "mv /tmp/opt/jira/jira-current /opt/jira/" configureServer: files: /etc/nginx/nginx.conf: mode: 775 owner: root content: | user nginx; worker_processes auto; error_log /var/log/nginx/error.log; include /usr/share/nginx/modules/*.conf; pid /var/run/nginx.pid; events { worker_connections 1024; } http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; client_max_body_size 30M; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/conf.d/*.conf; } /etc/nginx/conf.d/jira.conf: mode: 775 owner: root content: | server { listen *:80 default_server; listen [::]:80 default_server; return 301 https://$host$request_uri; } server { listen 8088; location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; send_timeout 600; } } /etc/cron.d/jira.backup: mode: 644 owner: root group: root content: !Sub - | 12 23 * * * root aws s3 sync --sse AES256 /efs/jira/${jiraEnvironment} s3://${S3Bucket}/backups/${jiraEnvironment} >/dev/null 2>&1 - S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" /etc/init.d/jira: mode: 775 owner: root group: jiraadm content: | #!/bin/bash

jira This shell script takes care of starting and stopping jira

chkconfig: - 58 74 description: jira.

### BEGIN INIT INFO

Provides: jira Required-Start: $network $local_fs $remote_fs Required-Stop: $network $local_fs $remote_fs Short-Description: start and stop jira Description: jira

### END INIT INFO case "$1" in start) su - jira -c '/opt/jira/jira-current/bin/start-jira.sh' ;; stop) su - jira -c '/opt/jira/jira-current/bin/stop-jira.sh' ;; status) ps aux | grep jira ;; restart|force-reload) su - jira -c '/opt/jira/jira-current/bin/stop-jira.sh' su - jira -c '/opt/jira/jira-current/bin/start-jira.sh' ;; try-restart|condrestart) exit 3 ;; reload) exit 3 ;; *) echo $"Usage: $0 {start|stop|status|restart|try-restart|force-reload}" exit 2 esac /opt/jira/jira-current/atlassian-jira/WEB-INF/classes/jira-application.properties: mode: 644 owner: jira group: jiraadm content: !Sub | jira.home=/efs/jira/${jiraEnvironment} /opt/jira/jira-current/conf/server.xml: mode: 644 owner: jira group: jiraadm content: !Sub | <Server port="8005" shutdown="SHUTDOWN"> <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> <Service name="Catalina"> <Connector port="8080" relaxedPathChars="[]|" relaxedQueryChars="[]|{}^&#x5c;&#x60;&quot;&lt;&gt;" maxThreads="150" minSpareThreads="25" connectionTimeout="20000" enableLookups="false" maxHttpHeaderSize="8192" protocol="HTTP/1.1" useBodyEncodingForURI="true" redirectPort="8443" acceptCount="100" disableUploadTimeout="true" scheme="https" proxyName="${ServiceURL}" proxyPort="443" secure="false"/> <Engine name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Context path="" docBase="${!catalina.home}/atlassian-jira" reloadable="false" useHttpOnly="true"> <Resource name="UserTransaction" auth="Container" type="javax.transaction.UserTransaction" factory="org.objectweb.jotm.UserTransactionFactory" jotm.timeout="60"/> <Manager pathname=""/> </Context> </Host> <Valve className="org.apache.catalina.valves.AccessLogValve" pattern="%a %{jira.request.id}r %{jira.request.username}r %t &quot;%m %U%q %H&quot; %s %b %D &quot;%{Referer}i&quot; &quot;%{User-Agent}i&quot; &quot;%{jira.request.assession.id}r&quot;"/> </Engine> </Service> </Server> /opt/jira/jira-current/atlassian-jira/WEB-INF/lib/cas-client-integration-atlassian-3.3.jar: mode: 644 owner: jira group: jiraadm source: !Sub - "https://s3-${AWS::Region}.amazonaws.com/${S3Bucket}/cas/cas-client-integration-atlassian-3.3.jar" - S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" /opt/jira/jira-current/atlassian-jira/WEB-INF/lib/cas-client-core-3.3.jar: mode: 644 owner: jira group: jiraadm source: !Sub - "https://s3-${AWS::Region}.amazonaws.com/${S3Bucket}/cas/cas-client-core-3.3.jar" - S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" /opt/jira/jira-current/lib/mysql-connector-java-5.1.35-bin.jar: mode: 644 owner: jira group: jiraadm source: !Sub - "https://s3-${AWS::Region}.amazonaws.com/${S3Bucket}/mysql/mysql-connector-java-5.1.35-bin.jar" - S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" configureAccess: commands: 01setPerms: command: "chown -R jira /opt/jira && chmod -R u=rwx,go-rwx /opt/jira" 02setupHome: command: "mkdir -p /var/jira && chown -R jira /var/jira && chmod -R u=rwx,go-rwx /var/jira" 03setupShell: command: "/usr/bin/chsh -s /bin/bash jira" uninstallJava: commands: 01remove17: command: "yum remove -y java-1.7.0-openjdk" configureService: commands: completeInstallation: command: "/opt/jira/jira-current/bin/version.sh > /var/log/jiraInstallation.log" installMysql: commands: completeInstallation: command: "yum -y install mysql" services: sysvinit: nginx: enabled: "true" ensureRunning: "true" commands: - completeInstallation jira: enabled: "true" ensureRunning: "true" commands: - completeInstallation CreationPolicy: ResourceSignal: Timeout: PT20M Properties: Tags: - Key: Name Value: !Ref AWS::StackName - Key: service Value: !Ref ServiceTag - Key: environment Value: !Ref EnvironmentTag - Key: contactnetid Value: !Ref ContactNetidTag - Key: accountnumber Value: !Ref AccountNumberTag - Key: subaccountnumber Value: !Ref SubAccountNumberTag - Key: ticketnumber Value: !Ref TicketNumberTag BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: 150 VolumeType: gp2 InstanceType: !Ref InstanceType IamInstanceProfile: !Ref EC2InstanceProfile KeyName : !Ref KeyName ImageId : !FindInMap [ RegionMap, !Ref "AWS::Region" , AMIID ] UserData : Fn::Base64: !Sub - | #!/bin/bash -ex mkdir -p /efs/jira echo "${efsid}.efs.${AWS::Region}.amazonaws.com:/ /efs/jira nfs4 nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 0 0" >> /etc/fstab mount -a -t nfs4

Set the Time Zone Need to escape the / in America/Phoenix

sed -i 's/ZONE="UTC"/ZONE="America\/Phoenix"/' /etc/sysconfig/clock ln -sf /usr/share/zoneinfo/America/Phoenix /etc/localtime #Ask CFN-INIT to install the configs listed in the bootStrap configset /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --configsets bootStrap --resource jiraInstance --region ${AWS::Region} CFNEXIT=$? #Make sure that our home directory exists if [[ ! -d "/efs/jira/${jiraEnvironment}" ]]; then /opt/aws/bin/cfn-signal -s FALSE -r "/efs/jira/${jiraEnvironment} does not exist" --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region} fi

Remove anything that might be at this path, we'll re-create it below

rm -rf /efs/jira/${jiraEnvironment}/caches/indexes

Make sure our target index foler exists, else create it

if [[ ! -d "/var/lib/jira/caches/indexes" ]]; then mkdir -p /var/lib/jira/caches/indexes if [[ $? -gt 0 ]]; then /opt/aws/bin/cfn-signal -s FALSE -r "failed to create /var/lib/jira/caches/indexes" --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region} fi chown jira:jira -R /var/lib/jira fi

Create a sym link

ln -s /var/lib/jira/caches/indexes /efs/jira/${jiraEnvironment}/caches/indexes chown -h jira:jira /efs/jira/${jiraEnvironment}/caches/indexes

Add tools-team SSH key to JIRA User

JIRA_USER_HOME=/opt/jira mkdir /opt/jira/.ssh chown jira:jira $JIRA_USER_HOME/.ssh chmod 700 $JIRA_USER_HOME/.ssh aws s3 cp s3://${S3Bucket}/ssh/uits-tools-team-public.pem $JIRA_USER_HOME/.ssh/authorized_keys chown jira:jira $JIRA_USER_HOME/.ssh/authorized_keys chmod 600 $JIRA_USER_HOME/.ssh/authorized_keys aws s3 cp s3://${S3Bucket}/ssh/uits-tools-team-public.pem $JIRA_USER_HOME/.ssh/gaucherie.pem cat $JIRA_USER_HOME/.ssh/gaucherie.pem >> $JIRA_USER_HOME/.ssh/authorized_keys #Signal the result to the CF service /opt/aws/bin/cfn-signal -e $CFNEXIT --stack ${AWS::StackName} --resource jiraInstance --region ${AWS::Region} chown -R jira:jira /efs/jira - efsid: Fn::ImportValue: !Sub "${jiraFoundationStack}-fs-id" S3Bucket: Fn::ImportValue: !Sub "${jiraFoundationStack}-jirabucket" SecurityGroupIds: - !Ref InstanceSG SubnetId: !Ref InstanceSubnet loadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Scheme: internal Subnets: - Ref: LBSubnetA - Ref: LBSubnetB LoadBalancerAttributes: - Key: idle_timeout.timeout_seconds Value: '50' SecurityGroups: - Ref: LBSecurityGroup Tags: - Key: Name Value: !Sub "${AWS::StackName}-LoadBalancer" - Key: service Value: !Ref ServiceTag - Key: environment Value: !Ref EnvironmentTag - Key: contactnetid Value: !Ref ContactNetidTag - Key: accountnumber Value: !Ref AccountNumberTag - Key: subaccountnumber Value: !Ref SubAccountNumberTag - Key: ticketnumber Value: !Ref TicketNumberTag LBSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: 'Allow traffic to jira load balancer' VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: !Ref CIDR IpProtocol: "tcp" FromPort: "80" ToPort: "80" - CidrIp: !Ref CIDR IpProtocol: "tcp" FromPort: "443" ToPort: "443" ALBTargetGroup80: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 60 UnhealthyThresholdCount: 10 HealthCheckPath: / Name: !Sub "${AWS::StackName}-targetgroup80" Port: 80 Protocol: HTTP VpcId: Ref: VPC Targets: - Id: !Ref jiraInstance Port: 80 ALBTargetGroup443: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 60 UnhealthyThresholdCount: 10 HealthCheckPath: / Name: !Sub "${AWS::StackName}-targetgroup443" Port: 8088 Protocol: HTTP VpcId: Ref: VPC Targets: - Id: !Ref jiraInstance Port: 8088 ALBListener80: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: Ref: ALBTargetGroup80 LoadBalancerArn: Ref: loadBalancer Port: 80 Protocol: HTTP ALBListener443: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: Ref: ALBTargetGroup443 LoadBalancerArn: Ref: loadBalancer Port: 443 Protocol: HTTPS Certificates: - CertificateArn: !Ref SSLCertificatARN EnvDnsRecord: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Sub "${HostedZoneName}." Name: !Sub "jira-${jiraEnvironment}.${HostedZoneName}." Type: "CNAME" TTL: "200" ResourceRecords: - !GetAtt loadBalancer.DNSName Outputs: jiraEndpoint: Description: The endpoint where jira will become accessible on the desired service port Value: !Sub "https://jira-${jiraEnvironment}.${HostedZoneName}"