HeatTemplateFormatVersion: '2012-12-12' Description: Simple Server Parameters: Name: Description: ServerName Type: String PrimaryNetworkId: Description: PrimaryNetworkId Type: String KeyName: Default: cloud Description: Name of an existing EC2 KeyPair to enable SSH access Type: String InstanceZone: Default: nova Description: AvailabilityZone for this instance Type: String Flavor: Default: m1.small Description: Flavor to boot Type: String Image: Default: centos-7 Description: Image of choice Type: String StartupTimeout: Default: 1700 Description: How long to wait on node coming up Type: Number PrimarySecurityGroup: Default: default Description: Security Group to use Type: String ExtraUserData: Default: '' Description: Extra UserData script Type: String RebootExtraUserData: Default: '' Description: Extra UserData script to call after the reboot. Type: String Metadata: Default: "{}" Description: Extra metadata to attach to the instance. Type: Json FloatingIpAddress: Default: '-1' Description: The floating ip address you would like to attach Type: String FloatingIpId: Default: '-1' Description: The floating ip id Type: String AssignFloatingIp: AllowedValues: ["True", "False"] Default: "False" Description: Use true to setup a floating ip Type: String FloatingIpType: AllowedValues: ["instance_ip", "port_id"] Default: "instance_ip" Description: How to configure. instance_ip or port_id. Type: String SecurityGroupSimplePorts: Default: '' Description: Ports to allow if SecurityGroupType=Simple Type: String SecurityGroupRules: Default: '' Description: Rules to use if SecurityGroupType=Raw Type: Json SecurityGroupType: AllowedValues: ["Simple", "Raw"] Default: "Simple" Description: Which type of Security Group to use Type: String CreateSecurityGroup: AllowedValues: ["True", "False"] Default: "False" Description: Use True to setup a Security Group Type: String RebootAfter: AllowedValues: ["True", "False"] Default: "False" Description: Use True to reboot after setup. Type: String AutoFinish: AllowedValues: ["True", "False"] Default: "True" Description: Successfully finish the stack after ExtraUserData is successfully executed. Type: String RebootRequiredService: Default: "sshd" Description: Which systemd service to wait for on reboot to finish. Type: String AssignVolume: AllowedValues: ["True", "False"] Default: "False" Description: Use true to setup a volume Type: String VolumeId: Description: "The Volume ID to attach" Default: "-1" Type: String VolumeName: Description: "Name of the volume used when created with Volume-Init" Default: "-1" Type: String MountPoint: Default: "/srv" Description: If a volume is specified, mount here Type: String BaseURL: Default: 'https://raw.githubusercontent.com/EMSL-MSC/heat-templates/master/cfn/lib' Description: Base URL where other templates live. Type: String Mappings: CreateSecurityGroup: "True": {URL: "/private/ThisOrThat_A.yaml"} "False": {URL: "/private/ThisOrThat_B.yaml"} Outputs: FloatingIpId: Description: Floating IP ID Value: "Fn::GetAtt": [FloatingIp, Outputs.FloatingIpId] SecurityGroupId: Description: Security Group ID Value: "Fn::GetAtt": [SecurityGroupId, Outputs.val] InstanceId: Description: Instance ID Value: Ref: Instance PortId: Description: Port ID Value: "Fn::GetAtt": [PrimaryInterface, Outputs.PortId] FixedIps: Description: Neutron Fixed IPs Value: "Fn::GetAtt": [PrimaryInterface, Outputs.FixedIps] Resources: VolumeMount: Properties: Parameters: InstanceId: {Ref: Instance} VolumeId: {Ref: VolumeId} AssignVolume: {Ref: AssignVolume} TemplateURL: Fn::Join: - '' - - {Ref: BaseURL} - "/VolumeMount.yaml" Type: AWS::CloudFormation::Stack SecurityGroupId: Properties: Parameters: a: {"Fn::GetAtt": [SecurityGroup, Outputs.SecurityGroupId]} b: {Ref: PrimarySecurityGroup} TemplateURL: Fn::Join: - '' - - {Ref: BaseURL} - Fn::FindInMap: - CreateSecurityGroup - {Ref: CreateSecurityGroup} - URL Type: AWS::CloudFormation::Stack FloatingIp: Properties: Parameters: NetworkId: {Ref: PrimaryNetworkId} InstanceId: {Ref: Instance} IpAddress: {Ref: FloatingIpAddress} FloatingIpId: {Ref: FloatingIpId} PortId: "Fn::GetAtt": [PrimaryInterface, Outputs.PortId] AssignFloatingIp: {Ref: AssignFloatingIp} Type: {Ref: FloatingIpType} BaseURL: {Ref: BaseURL} TemplateURL: Fn::Join: - '' - - {Ref: BaseURL} - "/FloatingIp.yaml" Type: AWS::CloudFormation::Stack SecurityGroup: Properties: Parameters: Name: {Ref: Name} SimplePorts: {Ref: SecurityGroupSimplePorts} Rules: {Ref: SecurityGroupRules} Type: {Ref: SecurityGroupType} CreateSecurityGroup: {Ref: CreateSecurityGroup} TemplateURL: Fn::Join: - '' - - {Ref: BaseURL} - "/SecurityGroup.yaml" Type: AWS::CloudFormation::Stack PrimaryInterface: Properties: Parameters: Name: {Ref: Name} NetworkId: {Ref: PrimaryNetworkId} DefaultSecurityGroups: "False" SecurityGroups: SecurityGroups: Fn::Join: - '' - - '["' - {"Fn::GetAtt": [SecurityGroupId, Outputs.val]} - '"]' BaseURL: {Ref: BaseURL} TemplateURL: Fn::Join: - '' - - {Ref: BaseURL} - "/NeutronPort.yaml" Type: AWS::CloudFormation::Stack Instance: Properties: name: Fn::Join: - '' - - {Ref: Name} availability_zone: {Ref: InstanceZone} image: {Ref: Image} flavor: {Ref: Flavor} key_name: {Ref: KeyName} metadata: {Ref: Metadata} networks: - {port: {"Fn::GetAtt": [PrimaryInterface, Outputs.PortId]}} user_data_format: RAW user_data: {Ref: UserData} Type: OS::Nova::Server CloudConfig: Type: OS::Heat::CloudConfig Properties: cloud_config: UserData: Type: OS::Heat::MultipartMime Properties: parts: - config: {Ref: CloudConfig} - config: Fn::Replace: - wc_url: {Ref: WaitHandleInstance} NORMAL_EXTRA_USER_DATA: {Ref: ExtraUserData} REBOOT_EXTRA_USER_DATA: {Ref: RebootExtraUserData} AUTO_FINISH: {Ref: AutoFinish} REBOOT_AFTER: {Ref: RebootAfter} REQUIRED_SERVICE: {Ref: RebootRequiredService} MOUNT_POINT: {Ref: MountPoint} VOLUME_NAME: {Ref: VolumeName} - | #!/bin/bash ## Error reporting helper function cat > /var/lib/cloud/error_hook <<"EOF" function error_exit { curl -X PUT -H 'Content-Type:' --data-binary '{"Status": "FAILURE", "Reason": "'"$1"'", "Data": "Application has completed configuration.", "UniqueId": "0000"}' "wc_url" exit 1 } function trap_error { error_exit "Line: ${BASH_LINENO[0]}, ExitCode: $1, File: $2" } function finished_ok { curl -X PUT -H 'Content-Type:' --data-binary '{"Status": "SUCCESS", "Reason": "Configuration Complete", "Data": "Application has completed configuration.", "UniqueId": "0001"}' "wc_url" } function trap_all_errors { set -eu MYSCRIPT="$1" trap 'trap_error "$?" "$MYSCRIPT"' ERR } EOF . /var/lib/cloud/error_hook trap_all_errors "$0" cat > /etc/rc.finishreboot <<"SIMPLE_SERVER_EOF" #!/bin/bash . /var/lib/cloud/error_hook trap_all_errors "$0" chmod -x /etc/rc.finishreboot ## Reboot Extra userdata REBOOT_EXTRA_USER_DATA if [ "AUTO_FINISH" == "True" ]; then finished_ok fi SIMPLE_SERVER_EOF chmod +x /etc/rc.finishreboot which systemctl >> /dev/null if [ $? -eq 0 ]; then cat > /etc/systemd/system/finishreboot.service <<"EOF" [Unit] Description=Finish up reboot After=REQUIRED_SERVICE.service Requires=REQUIRED_SERVICE.service ConditionFileIsExecutable=/etc/rc.finishreboot [Service] Type=forking RemainAfterExit=yes TimeoutStartSec=0 ExecStart=/etc/rc.finishreboot [Install] WantedBy=multi-user.target EOF systemctl enable finishreboot.service else echo '[ -x /etc/rc.finishreboot ] && /etc/rc.finishreboot' >> /etc/rc.local fi if [ "xVOLUME_NAME" != "x" -a "xVOLUME_NAME" != "x-1" ]; then while [ ! -e /dev/vdb ]; do echo waiting for /dev/vdb to attach; sleep 10; done if [ "xMOUNT_POINT" != "x" -a "xMOUNT_POINT" != "x-1" ]; then which vgchange || yum install -y lvm2 || apt-get install lvm2 || true vgchange -a y VOLUME_NAME-vg0 echo '/dev/VOLUME_NAME-vg0/VOLUME_NAME-data MOUNT_POINT auto defaults,auto,noatime 0 0' | tee -a /etc/fstab mount MOUNT_POINT fi fi ## Extra userdata NORMAL_EXTRA_USER_DATA if [ "AUTO_FINISH" == "True" -a "REBOOT_AFTER" == "False" ]; then finished_ok fi #The reboot module in cloud-init isn't always loaded. Fall back this way. if [ "REBOOT_AFTER" == "True" ]; then (while true; do ps ax | grep cloud-init | grep -v grep | grep -v tee >> /dev/null || break sleep 1 done; sleep 2; shutdown -r now)& fi - config: Fn::Replace: - VALUE: {Ref: RebootAfter} - | #cloud-config power_state: mode: reboot timeout: 30 condition: VALUE WaitConditionInstance: DependsOn: Instance Properties: Handle: {Ref: WaitHandleInstance} Timeout: {Ref: StartupTimeout} Type: AWS::CloudFormation::WaitCondition WaitHandleInstance: Properties: {} Type: AWS::CloudFormation::WaitConditionHandle