control-m-agent-nolambda.yaml
---

Control-M Linux Agent Template

This CloudFormation template will deploy an Amazon Linux2 server with ssh permitted from limited networks, and a set of Control-M specific ports open to limited identified networks

AWSTemplateFormatVersion: '2010-09-09' Description: Control-M Agent Linux Instance

Parameters

These are the input parameters for this template. All of these parameters must be supplied for this template to be deployed.

Parameters: CMFoundationStack: Type: String Description: Name of the Control-M foundation stack. Default: Control-M-Foundation CMServerStack: Type: String Description: Name of the Control-M server stack. Default: None ServerName: Type: String Description: >- Enter the name of the host or service. Max 15 alphanumeric characters plus dash and underscore. The dash and underscore cannot be first or last in the name. AllowedPattern: '(^((?![-_])(?=.*[a-z].*)[a-z0-9-_]{1,20}(?<![-_]))$)' ConstraintDescription: Max 15 alphanumeric characters plus dash and underscore. The dash and underscore cannot be first or last in the name. VPC: Type: AWS::EC2::VPC::Id Description: The VPC in which to deploy this stack InstanceSubnets: Type: List<AWS::EC2::Subnet::Id> Description: The private subnets for the application (2 or more) PeopleSoftAgentDesired: Description: Install the PeopleSoft (PS8) agent? Type: String Default: false AllowedValues: [false, true] SysJava11: Description: Install and use Java 11 from the system for the PS8 module? Type: String Default: false AllowedValues: [false, true] OverrideCMServers: Description: >- Override the permitted CM Servers imported from the CMServerStack. Space separated list of CTM Server FQDNs. Leave blank to use the CMServerStack values. Type: String

Default Operating System for EC2 instance.

OSType: Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id> Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 Description: Amazon Linux Latest AMI ID AllowedValues: - /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

Default EC2 Instance Type for Application instances.

InstanceType: Description: >- EC2 Instance Type t3.medium (2vCPU x 4GB) t3.large (2vCPU x 8GB) t3.xlarge (4vCPU x 16GB) m5.large (2vCPU x 8GB) m5.xlarge (4vCPU x 16GB) Type: String Default: t3.medium AllowedValues: [t3.medium, t3.large, t3.xlarge, m5.large, m5.xlarge] RootVolumeSize: Description: Root Volume Size (/dev/xvda or /dev/nvme0n1) Type: Number MinValue: 20 MaxValue: 2048 Default: 40 FirstOptionalDataVolumeDesired: Description: Add Data Disk 1 (/dev/xvdb or /dev/nvme1n1)? Type: String Default: false AllowedValues: [false, true] FirstOptionalDataVolumeSize: Description: Data Disk 1 Volume Size (/dev/xvdb or /dev/nvme1n1) Type: Number MinValue: 10 MaxValue: 2048 Default: 10 SecondOptionalDataVolumeDesired: Description: Add Data Disk 2 (/dev/xvdc or /dev/nvme2n1)? Type: String Default: false AllowedValues: [false, true] SecondOptionalDataVolumeSize: Description: Data Disk 2 Volume Size (/dev/xvdc or /dev/nvme2n1) Type: Number MinValue: 10 MaxValue: 2048 Default: 10 KeyName: Description: Amazon EC2 Key Pair Type: AWS::EC2::KeyPair::KeyName AsgMaxSize: Description: >- Auto Scaling Group Max Inst Count [0-2]. Will be used for Desired Count too. Type: Number MinValue: 0 MaxValue: 2 Default: 1 HostedZoneName: Type: String Description: route53 hosted zone name (lowercase only) Default: uits-prod-aws.arizona.edu AllowedPattern: '(?=^.{4,253}$)(^((?!-)[a-z0-9-]{1,63}(?<!-)\.)+[a-z]{2,63}$)' ConstraintDescription: A dot-delimited DNS zone name, permitting numbers, lowercase letters, and dashes GroupName: Type: String Description: >- Enter the name of the LDAP Group of the owners. AllowedPattern: '(^((?![._-])[a-zA-Z0-9._-]{1,63}(?<![._-]))$)' Default: invalidplaceholdername DRS3BucketName: Type: String Description: >- Enter the name of the DR S3 Bucket. AllowedPattern: '(^((?![-])[a-z0-9-]{0,63}(?<![-]))$)'

Tags

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

TagService: Type: String Description: Exact name of the Service as defined in the service catalog. Default: Control-M Agent TagEnvironment: Type: String Description: >- Used to distinguish between development, test, production,etc. environment types. AllowedValues: [dev, tst, prd] Default: tst TagContactNetID: Type: String Description: >- Used to identify the netid of the person most familiar with the usage of the resource. TagAccountNumber: Type: String Description: Identifies the financial system account number. TagTicketNumber: 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. TagBackupSLA: Type: String Description: >- Select Backup SLA from the following UITS Standard - Backup policy includes daily backups retained for one month and monthly backups retained for one year None - No backups performed AllowedValues: ["UITS Standard", "None"] Default: "UITS Standard"

Metadata

Metadata is mostly for organizing and presenting Parameters in a better way when using CloudFormation in the AWS Web UI.

Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Environment Settings Parameters: - CMFoundationStack - CMServerStack - Label: default: Server Settings Parameters: - ServerName - VPC - InstanceSubnets - PeopleSoftAgentDesired - SysJava11 - OverrideCMServers - OSType - InstanceType - RootVolumeSize - FirstOptionalDataVolumeDesired - FirstOptionalDataVolumeSize - SecondOptionalDataVolumeDesired - SecondOptionalDataVolumeSize - KeyName - AsgMaxSize - HostedZoneName - GroupName - DRS3BucketName - Label: default: Tagging Parameters: - TagService - TagEnvironment - TagContactNetID - TagAccountNumber - TagTicketNumber - TagBackupSLA ParameterLabels: TagEnvironment: default: 'Environment Type:' TagTicketNumber: default: 'Ticket Number:' TagContactNetID: default: 'Contact NetID:'

Conditions

Conditions: CreateFirstOptionalDataVolume: !Equals [true, !Ref FirstOptionalDataVolumeDesired] CreateSecondOptionalDataVolume: !Equals [true, !Ref SecondOptionalDataVolumeDesired] LinkCMServerStack: !Not [!Equals [!Ref CMServerStack, "None"]] MasterProd: !Equals ["760232551367", !Ref AWS::AccountId] IsProd: !Equals ["prd", !Ref TagEnvironment] AddSGNonProd: !And - !Not [!Condition IsProd] - !Not [!Condition LinkCMServerStack] AddSGProd: !And - !Condition IsProd - !Not [!Condition LinkCMServerStack]

Resources

This is the EC2 instance deployed by the template.

Resources:

AWS Account Information

Lambda function to introspect VPCs, subnets, and select most available

#AccountInfo:

Type: Custom::AccountInfo Properties: ServiceToken: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:fdn-cf-account-info" VPCInfo:

  • vpcid
  • public-subnet-a
  • public-subnet-b
  • private-subnet-a
  • private-subnet-b
  • choose-private-subnet
  • choose-public-subnet

EC2 Instance Launch Config

Launch Config to deploy the EC2 instance with some tags.

Ec2InstanceLc: Type: AWS::AutoScaling::LaunchConfiguration Properties: IamInstanceProfile: !Ref EnvInstanceProfile ImageId: !Ref OSType KeyName: !Ref KeyName InstanceType: !Ref InstanceType SecurityGroups: - Fn::ImportValue: !Sub "${CMFoundationStack}-ssh-sg" - Ref: InstanceSecurityGroup - Fn::ImportValue: !Sub "${CMFoundationStack}-cmagent-sg" BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeSize: !Ref RootVolumeSize VolumeType: gp2 Encrypted: true - !If - CreateFirstOptionalDataVolume - DeviceName: /dev/xvdb Ebs: VolumeSize: !Ref FirstOptionalDataVolumeSize VolumeType: gp2 Encrypted: true - !Ref "AWS::NoValue" - !If - CreateSecondOptionalDataVolume - DeviceName: /dev/xvdc Ebs: VolumeSize: !Ref SecondOptionalDataVolumeSize VolumeType: gp2 Encrypted: true - !Ref "AWS::NoValue" UserData: Fn::Base64: !Sub - | #!/bin/bash export WORKINGPATH=/tmp/cftdeployscripts

Create temporary working folder if it doesn't already exist

if [ ! -d $WORKINGPATH ]; then mkdir -p $WORKINGPATH fi

Import CFT parameters into a local parameters file if they don't already exist

if [ ! -f $WORKINGPATH/cftdeploy-params ]; then cat <<EOFPARAMS > $WORKINGPATH/cftdeploy-params HostedZoneName=${HostedZoneName} ServerName=${ServerName} AWSRegion=${AWS::Region} GroupName=${GroupName} S3BucketName=${S3BucketName} EFSId=${EFSId} CTMServerName="${CTMServerName}" CTMDomainName="${CTMDomainName}" PeopleSoftAgentDesired=${PeopleSoftAgentDesired} SysJava11=${SysJava11} DRS3BucketName=${DRS3BucketName} WORKINGPATH=${!WORKINGPATH} SNS_NOTIFICATION_TOPIC=${SNS_NOTIFICATION_TOPIC} EOFPARAMS fi

Import the instance customization script from S3 if it doesn't already exist

if [ ! -f $WORKINGPATH/control-m-agent.sh ]; then aws s3 sync s3://${S3BucketName}/scripts $WORKINGPATH/ chmod +x $WORKINGPATH/control-m-agent.sh fi

Run the instance customization script

$WORKINGPATH/control-m-agent.sh $WORKINGPATH/cftdeploy-params

Clean up on completion

rm -r $WORKINGPATH - S3BucketName: Fn::ImportValue: !Sub "${CMFoundationStack}-bucket" EFSId: Fn::ImportValue: !Sub "${CMFoundationStack}-fs-id" CTMServerName: Fn::If: - LinkCMServerStack - Fn::ImportValue: !Sub "${CMServerStack}-ctmserver-base" - !Ref OverrideCMServers CTMDomainName: Fn::If: - LinkCMServerStack - Fn::ImportValue: !Sub "${CMServerStack}-ctmserver-domain" - "" SNS_NOTIFICATION_TOPIC: Fn::ImportValue: fdn-logging-alarm-topic

Auto Scaling Group

Define the ASG for the Cluster Instances

Ec2InstanceAsg: Type: AWS::AutoScaling::AutoScalingGroup Properties: VPCZoneIdentifier: !Ref InstanceSubnets LaunchConfigurationName: !Ref Ec2InstanceLc MinSize: "0" MaxSize: !Ref AsgMaxSize DesiredCapacity: !Ref AsgMaxSize MetricsCollection: - Granularity: 1Minute Tags: - Key: Name Value: !Sub Control-M-${ServerName} PropagateAtLaunch: true - Key: service Value: !Ref TagService PropagateAtLaunch: true - Key: environment Value: !Ref TagEnvironment PropagateAtLaunch: true - Key: contactnetid Value: !Ref TagContactNetID PropagateAtLaunch: true - Key: accountnumber Value: !Ref TagAccountNumber PropagateAtLaunch: true - Key: ticketnumber Value: !Ref TagTicketNumber PropagateAtLaunch: true - Key: BackupSLA Value: !Ref TagBackupSLA PropagateAtLaunch: true

Instance Security Group

Security group for the EC2 instance, that allows you to ssh into the instance TODO: Needs to be updated to permit passing this as a parameter.

InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Control-M instance members VpcId: !Ref VPC Tags: - Key: Name Value: !Sub Control-M-Inst-SG-${ServerName} - Key: service Value: !Ref TagService - Key: environment Value: !Ref TagEnvironment - Key: contactnetid Value: !Ref TagContactNetID - Key: accountnumber Value: !Ref TagAccountNumber - Key: ticketnumber Value: !Ref TagTicketNumber

EFS Security Group

Permit the instance access to the EFS export

EFSIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${CMFoundationStack}-efs-sg" IpProtocol: tcp FromPort: 2049 ToPort: 2049 SourceSecurityGroupId: !Ref InstanceSecurityGroup

SSH Security Group

Permit the instance ssh access to the other instances

SSHIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: Fn::ImportValue: !Sub "${CMFoundationStack}-ssh-sg" IpProtocol: tcp FromPort: 22 ToPort: 22 SourceSecurityGroupId: !Ref InstanceSecurityGroup Description: "Control-M self"

Control-M Agent Security Group

Permit the Control-M server to connect to the Control-M agent from another account

CMAgentIngressNonProdA: Type: AWS::EC2::SecurityGroupIngress Condition: AddSGNonProd Properties: GroupId: Fn::ImportValue: !Sub "${CMFoundationStack}-cmagent-sg" IpProtocol: tcp FromPort: 7017 ToPort: 7017 CidrIp: "10.220.189.0/24" Description: "Control-M Servers in ua-uits-general-nonprod" CMAgentIngressNonProdB: Type: AWS::EC2::SecurityGroupIngress Condition: AddSGNonProd Properties: GroupId: Fn::ImportValue: !Sub "${CMFoundationStack}-cmagent-sg" IpProtocol: tcp FromPort: 7017 ToPort: 7017 CidrIp: "10.220.190.0/24" Description: "Control-M Servers in ua-uits-general-nonprod" CMAgentIngressProdA: Type: AWS::EC2::SecurityGroupIngress Condition: AddSGProd Properties: GroupId: Fn::ImportValue: !Sub "${CMFoundationStack}-cmagent-sg" IpProtocol: tcp FromPort: 7017 ToPort: 7017 CidrIp: "10.221.84.0/22" Description: "Control-M Servers in ua-erp" CMAgentIngressProdB: Type: AWS::EC2::SecurityGroupIngress Condition: AddSGProd Properties: GroupId: Fn::ImportValue: !Sub "${CMFoundationStack}-cmagent-sg" IpProtocol: tcp FromPort: 7017 ToPort: 7017 CidrIp: "10.221.88.0/22" Description: "Control-M Servers in ua-erp"

Instance Policy

This is the IAM policythat will be attached to the instance's IAM role. Any AWS specific permissions that the node might need should be defined here. TODO: Determine what policy is needed

InstancePolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: Access to SSM parameters, S3, Route53, and SNS for deployment PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - 'ssm:DescribeParameters' Resource: - '*' - Effect: Allow Action: - 'ssm:GetParameter' Resource: - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/ad_join/*" - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/agents/*" - Effect: Allow Action: - 'ec2:Describe*' Resource: - '*' - Effect: Allow Action: - s3:GetObject - s3:ListBucket Resource: !Sub - "arn:aws:s3:::${S3Bucket}*" - S3Bucket: Fn::ImportValue: !Sub "${CMFoundationStack}-bucket" - Effect: Allow Action: - route53:ListHostedZones - route53:ListHostedZonesByName - route53:ChangeResourceRecordSets Resource: - "*" - Effect: Allow Action: - sns:publish Resource: - !ImportValue fdn-logging-alarm-topic Roles: - !Ref EnvInstanceRole

S3 Replication Policy

This is the IAM policythat will be attached to the instance's IAM role. Any AWS specific permissions that the node might need should be defined here. TODO: Determine what policy is needed

S3ReplicationPolicy: Type: AWS::IAM::ManagedPolicy Condition: MasterProd Properties: ManagedPolicyName: !Sub "${ServerName}-s3" Description: Access to remote S3 buckets for sync PolicyDocument: Version: 2012-10-17 Statement:

Sync to bucket in other regions

- Effect: Allow Action: - s3:GetBucketLocation - s3:ListBucket - s3:PutObject - s3:PutObjectAcl - s3:GetObject - s3:DeleteObject Resource: - "arn:aws:s3:::edu-arizona-dr-aws-kuali-dr-control-m-tst/*" - "arn:aws:s3:::edu-arizona-dr-aws-kuali-dr-control-m-tst" - "arn:aws:s3:::edu-arizona-ua-uits-kuali-nonprod-control-m-tst/*" - "arn:aws:s3:::edu-arizona-ua-uits-kuali-nonprod-control-m-tst" - "arn:aws:s3:::edu-arizona-kuali-prod-aws-control-m-prd/*" - "arn:aws:s3:::edu-arizona-kuali-prod-aws-control-m-prd" - "arn:aws:s3:::edu-arizona-ps-nonprod-control-m-tst/*" - "arn:aws:s3:::edu-arizona-ps-nonprod-control-m-tst" - "arn:aws:s3:::edu-arizona-ps-prod-control-m-prd/*" - "arn:aws:s3:::edu-arizona-ps-prod-control-m-prd" - "arn:aws:s3:::edu-arizona-dr-aws-ps-dr-control-m-tst/*" - "arn:aws:s3:::edu-arizona-dr-aws-ps-dr-control-m-tst" - "arn:aws:s3:::edu-arizona-uits-general-nonprod-control-m-dev/*" - "arn:aws:s3:::edu-arizona-uits-general-nonprod-control-m-dev" - "arn:aws:s3:::edu-arizona-uits-general-nonprod-control-m-tst/*" - "arn:aws:s3:::edu-arizona-uits-general-nonprod-control-m-tst" Roles: - !Ref EnvInstanceRole

Instance Role

This is the IAM role that will be applied to the EC2 Instance. Any AWS specific permissions that the node might need should be defined here.

EnvInstanceRole: Type: AWS::IAM::Role Properties: RoleName: !Ref ServerName AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole Path: "/" ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore #- arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

Instance Profile

This is just a little construct to connect a set of roles together into a profile. The profile is referenced by the EC2 Instance.

EnvInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: "/" Roles: - !Ref EnvInstanceRole

Instance Route53 records

This creates placeholder Route53 records that are populated at boot time by the instance itself

InstanceRoute53RecordSetGroup: Type: AWS::Route53::RecordSetGroup Properties: Comment: Creating records Control-M Server instances HostedZoneName: !Sub "${HostedZoneName}." RecordSets: - Name: !Sub "${ServerName}-a.${HostedZoneName}." Type: A TTL: "200" ResourceRecords: - 127.0.0.0 - Name: !Sub "${ServerName}-b.${HostedZoneName}." Type: A TTL: "200" ResourceRecords: - 127.0.0.0

Outputs

TODO: Determine what other outputs back to the caller will be needed.

Outputs: SshSecurityGroup: Description: "Instance Security Group" Value: !Ref InstanceSecurityGroup Export: Name: !Sub "${AWS::StackName}-inst-sg" AsgID: Description: "AutoScalingGroup ID" Value: !Ref Ec2InstanceAsg Export: Name: !Sub "${AWS::StackName}-asg-id"