Create the Template

The next step is to create a template yaml file with the definitions to deploy the Apache Web Server.

Refer to the Template Syntax section in this document for reference.

Create a new template file

Using your preferred text editor create a new template yaml file. Add the following statements, feel free to substitute values as appropriate.

template_name: "Apache Web Server on Windows"
template_author: Gary Forghetti
template_version: "1.00"
description: "Deploys a Windows Server 2012 and installs the Apache Web Server"
long_description: "This blueprint deploys a stack and installs the Apache Web Server"

imports:
  - base_types.yaml                                                   # Import the DCM base type definitions that are referenced in this template

Define topology_template and inputs

topology_template:

  inputs:

Define AccountRegionSelector

The next step is to define a AccountRegionSelector to allow the Dell Cloud Manager console end user the ability to select the cloud, region and data center for launching the server in the stack.

  ################################################################################################################################################
  # This defines the AccountRegionSelector which allows the user to select the Cloud, Region and Datacenter
  ################################################################################################################################################
  account_region_zone_selector:                                     # Define the section for the Cloud, Region and Datacenter selection boxes
    type: dcm.inputs.AccountRegionSelector                          # Input type is dcm.inputs.accountRegionSelector 
    properties:
      regions:                                                      # Define the Cloud and Regions
        "Amazon":                                                   # Amazon
          "us-east-1": [ ]                                          # All data centers for the us-east-1 region
          "us-west-1": ["us-west-1a", "us-west-1c"]
          "us-west-2": ["us-west-2a", "us-west-2b", "us-west-2c"]
          "eu-west-1": ["eu-west-1a", "eu-west-1c"]

Define Product Selector

The next step is to define a Product selector to allow the Dell Cloud Manager console end user the ability to select the server product for launching the server in the stack. This example will use the Amazon cloud and a subset of the regions and server products. An Windows 2012 R2 machine image will be defined in the template to be used for creating the server.

Note

If you do not have access to the Amazon cloud or wish to use a different cloud provider’s cloud that Dell Cloud Manager supports, you will need to make the necessary changes below for the cloud, region(s), image(s) and products.

Add the statements to the yaml file to define the inputs product_selector. The inputs: statement must be indented inside the topology_template: statement. Refer to the Product Selector section in this document for reference.

    ################################################################################################################################################
    # This defines the Product selector which allows the user to select the server product size
    ################################################################################################################################################
    product_selector:                                             # Define the product selector so the user can select the cloud and region
      type: dcm.inputs.Product
      properties:
        accountRegionSelector: account_region_zone_selector       # This connects the AccountRegionSelector to the Product selector
        platform: WINDOWS                                         # Virtual machine images are Windows
        architecture: I64                                         # 64 bit images
        productMappings:
          "Amazon":                                               # Amazon cloud
            "us-east-1":                                          # us-east-1 Region
              image: "ami-3f0c4628"                               # The AWS machine image identitier for the Windows 2012 R2 image in this region
              products: ['t1.micro', 'm1.small', 'm1.medium']     # The virtual machine product sizes for this region
            "us-west-1":                                          # us-west-1 Region
              image: "ami-123c7472"                               # The AWS machine image identitier for the Windows 2012 R2 image in this region
              products: ['t1.micro', 'm1.small', 'm1.medium']     # The virtual machine product sizes for this region
            "us-west-2":                                          # us-west-2 Region
              image: "ami-b871aad8"                               # The AWS machine image identitier for the Windows 2012 R2 image in this region
              products: ['t1.micro', 'm1.small', 'm1.medium']     # The virtual machine product sizes for this region
            "eu-west-1":                                          # eu-west-1 Region
              image: "ami-55084526"                               # The AWS machine image identitier for the Windows 2012 R2 image in this region
              products: ['t1.micro', 'm1.small', 'm1.medium']     # The virtual machine product sizes for this region

Define outputs

In this tutorial 2 outputs will be defined and displayed on the Stack Overview page.

  • The IP address of the Apache Web Server
  • The Web URL of the Apache Web Server

Add the statements to the yaml file to define the outputs. The outputs: statement must be indented inside the topology_template: statement and aligned with the inputs: statement. Refer to the Template Outputs section in this document for reference.

##################################################################################################################################################
# This defines the outputs which appear on the DCM console Stack Overview page
##################################################################################################################################################
outputs:                                                        # Define outputs

  application_group:                                            # Create a Group
    type: dcm.outputs.DisplayGroup                              # It's a displayGroup
    properties:
      displayName: "Web Server"                                 # Set the display name for the group containing the outputs which appears on the Stack Overview page

  vm_ip:                                                        # Define an output
    type: string                                                # It's a string output
    description: IP of the server                               # Set the description for the string output
    value: {get_attribute: [windows_wm, publicIpAddress]}       # Set the value to the public IP address of the Web server running in the stack
    properties:
      displayName: Windows Web Server                           # Set the display name (label) for the string output
      displayGroup: application_group                           # Place this output in the displayGroup named application_group

  link:                                                         # Define another output for the URL of the Web server
    type: dcm.outputs.Uri                                       # It's a URI output
    value: http://xxxxxxxxxxxxx                                 # Set the initial value
    properties:
      host: {get_attribute: [windows_wm, publicIpAddress]}      # Set the hostname/ipaddress of the URI to the public IP address of the server running in the stack
      displayName: Windows Web Server URL                       # Set the display name (label) for the URI output
      displayGroup: application_group                           # Place this output in the displayGroup named application_group

Define the server

Add the statements to define the server. In this example the server name will be hardcoded to be Apache-Server. The remaining server properties will be retrieved from the corresponding properties derived from the product_selector and the Dell Cloud Manager console end user’s Launch Blueprint selections. Refer to the Server node section in this document for reference.

node_templates:

  ##################################################################################################################################################
  # This node_template defines a virtual machine named "vm" which will host the Web Server
  ##################################################################################################################################################
  windows_wm:
    type: dcm.nodes.Server
    properties:
      name: "web-server"
      cloud: { get_input: [account_region_zone_selector, cloud] }              # Retrieve cloud from the AccountRegionSelector
      cloudAccountId: { get_input: [account_region_zone_selector, accountId] } # Retrieve the cloud account ID from the AccountRegionSelector
      region: { get_input: [account_region_zone_selector, region] }            # Retrieve region from the AccountRegionSelector
      zone: { get_input: [account_region_zone_selector, zone] }                # Retrieve zone from the AccountRegionSelector
      platform: { get_input: [product_selector, platform] }                    # Retrieve platform from the Product selector
      architecture: { get_input: [product_selector, architecture] }            # Retrieve architecture from the Product selector
      product: { get_input: [product_selector, product] }                      # Retrieve product from the Product selector
      image: { get_input: [product_selector, image] }                          # Retrieve machine from the Product selector
      serverProductId: { get_input: [product_selector, serverProductId] }      # Retrieve server product id from the Product selector

Add the startupScript property statements to install, configure and start the Dell Cloud Manager agent on the launched server.

      startupScript:  |
            <powershell>
            ###########################################################################################################################################
            # Download Apache
            ###########################################################################################################################################  
            Invoke-WebRequest "http://archive.apache.org/dist/httpd/binaries/win32/httpd-2.2.25-win32-x86-openssl-0.9.8y.msi" `
            -outfile "c:\windows\temp\httpd-2.2.25-win32-x86-openssl-0.9.8y.msi"

            ###########################################################################################################################################
            # Install Apache
            ###########################################################################################################################################  
            $computername = $env:COMPUTERNAME
            msiexec /i "c:\windows\temp\httpd-2.2.25-win32-x86-openssl-0.9.8y.msi" `
            /quiet ALLUSERS="1" INSTALLDIR="c:\Apache2.2" SERVICENAME="Apache" `
            SERVERADMIN="admin@localhost.com" SERVERDOMAIN=${computername} `
            SERVERNAME=${computername} SERVERPORT="80" | Out-File c:\windows\temp\apache_install.log

            ###########################################################################################################################################
            # Build Home page index.html
            ########################################################################################################################################### 
            $html = '<!DOCTYPE html>'
            $html += '<html>'
            $html += '<head><title>ACME</title></head>'
            $html += '<body style="background-color:gainsboro;">'
            $html += '<h1><div style="text-align:center;">Welcome to ACME Anvil Corporation!</div></h1>'
            $html += '<div style="text-align:center;">'
            $html += '<img style="box-shadow: 8px 8px 5px #888888;" src="http://blueprint-designer-guide.enstratius.com/_static/acme_logo.jpg">'
            $html += '</div>'
            $html += '<p style="text-align:center;">Thank you for visiting our website. We look forward to having your business!</p>'
            $html += '</body>'
            $html += '</html>'
            $html | Out-File C:\Apache2.2\htdocs\index.html

            ###########################################################################################################################################
            # Add startup job to add a Firewall rule to open HTTP port 80 and run at system startup
            ########################################################################################################################################### 
            $trigger = New-JobTrigger -AtStartup -RandomDelay 00:01:00

            $netsh_cmd = 'netsh advfirewall firewall add rule name="OpenPort80" description="Firewall rule" `
            localport=80 dir=in protocol=TCP action=allow profile=any interfacetype=any' 
            $netsh_cmd | Out-File C:\Apache2.2\netsh_cmd.ps1

            Register-ScheduledJob -Trigger $trigger -FilePath C:\Apache2.2\netsh_cmd.ps1 -Name OpenPort80

            ###########################################################################################################################################
            # Add the Firewall rule to open HTTP port 80 now.
            ########################################################################################################################################### 
            C:\Apache2.2\netsh_cmd.ps1

            ###########################################################################################################################################
            # Extract the hostname/ip address and port from the DCM variable ${dcm.callback.url}.
            ###########################################################################################################################################
            $callback_url = "${dcm.callback.url}"
            $host_port = ${callback_url}.TrimStart("wss://").TrimEnd("/agentManager").Split(":", 2)
            $host_name = ${host_port}[0]
            $port = ${host_port}[1]
            if (${port} -eq ${null}) {
              $port = 443
            }

            ###########################################################################################################################################
            # Download the DCM Windows Agent
            ###########################################################################################################################################
            Invoke-WebRequest "http://windows.stable.agent.enstratius.com/DCM.Agent.Setup-latest.msi" -OutFile "c:\windows\temp\dcm_agent.msi"

            ###########################################################################################################################################
            # Install the Windows DCM Agent.  Note: the backtick character (`) can be used to split a command in powershell into multiple lines
            ###########################################################################################################################################
            msiexec /i "c:\windows\temp\dcm_agent.msi" /log "c:\windows\temp\dcm_agent_install.log" /q INSTALLFOLDER="c:\Enstratius\" `
            INSTALL_ENVIRONMENT="Staging" SERVER_ADDRESS=${host_name} SERVER_PORT=${port} CLOUD_PROVIDER="Amazon" | Out-Null

            ###########################################################################################################################################
            # Start the DCM Agent
            ###########################################################################################################################################            
            start-service DCMAgent
            </powershell>

Add a requirement to the launched server for a firewall which will defer creating the server until after the firewall is created. In this example the firewall section label name is vm_firewall_rules and will be defined later in the template yaml file.

requirements:                                                   # This virtual machine node has a requirement on a firewall
  - firewall: vm_firewall_rules                                 # The firewall statement is named "vm_firewall_rules"
    relationship_type: tosca.relationships.DependsOn

Define the Firewall

Add the statements to define the firewall vm_firewall_rules. Define a rule to open HTTP port 80.

Refer to the FirewallGroup node section in this document for reference.

###################################################################################################################################################
# This node_template defines a firewall which opens the HTTP port 80 and HTTPS port 443
###################################################################################################################################################
vm_firewall_rules:
  type: dcm.nodes.FirewallGroup                                              # This is a firewall
  properties:                                                                # Retrieve the cloud properties from the "inputs"
    name: "web-server-firewall"                                              # Set the firewall name
    cloud: { get_input: [account_region_zone_selector, cloud] }              # Retrieve the cloud from the AccountRegionSelector
    cloudAccountId: { get_input: [account_region_zone_selector, accountId] } # Retrieve the cloud account ID from the AccountRegionSelector
    region: { get_input: [account_region_zone_selector, region] }            # Retrieve the region from the AccountRegionSelector
    zone: { get_input: [account_region_zone_selector, zone] }                # Retrieve the zone from the AccountRegionSelector
    rules:
      - remote_ip_prefix: 0.0.0.0/0
        port: 80                                                             # Allow port 80 (HTTP) from Anywhere (0.0.0.0/0)
      - remote_ip_prefix: 0.0.0.0/0
        port: 443                                                            # Allow port 443 (HTTPS) from Anywhere (0.0.0.0/0)