AWS Developer Tools Blog

Installing Scheduled Tasks on EC2 Windows Instances

Today’s guest post is part one of a two part series by AWS Solutions Architect Russell Day.

Windows administrators and developers often use scheduled tasks to run programs or scripts on a recurring basis. In this post, we will demonstrate how to use the Amazon EC2 User data option to install scheduled tasks on Windows EC2 instances automatically at launch.

Using the user data field to specify scripts that will automatically configure instances is commonly referred to as bootstrapping. In this post, we will specify a PowerShell script in the user data field to install scheduled tasks when EC2 instances are launched. We will demonstrate two methods for launching EC2 instances: the EC2 console and AWS Tools for PowerShell.

Before we can get started, we need to export the scheduled tasks and store them in a location accessible to our EC2 instances. We will use Task Scheduler to export the scheduled tasks to XML and store them in an Amazon S3 bucket.

Export scheduled tasks.

In Task Scheduler, right-click on the scheduled tasks you want to export and install as XML files.

Create an S3 bucket to store the XML scheduled task definitions.

Use the S3 Console, CLI, or AWS Tools for Windows PowerShell to create an S3 bucket that will store the XML task definition files created in step 1.

Create manifest file(s).

The manifest file(s) contains the scheduled tasks you want to install on the target instances. Consider using a separate manifest file for each unique set of tasks (for example, ProductionServerTasks.xml, DevelopmentServerTasks.xml).

Modify and save the following XML to create your manifest file(s).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <scheduledTasks>
    <task name="Daily Scheduled Task 1" source="ScheduledTask1.xml" />
    <task name="Daily Scheduled Task 2" source="ScheduledTask2.xml" />
    <task name="Daily Scheduled Task 3" source="ScheduledTask3.xml" />
    <task name="Daily Scheduled Task 4" source="ScheduledTask4.xml" />
  </scheduledTasks>
</configuration>

Upload the exported scheduled task definitions and manifest file(s) to S3.

Upload the scheduled tasks definitions created in step 1 and the manifest file(s) created in step 3 to the S3 bucket created in step 2.

Create a PowerShell script to download and install the scheduled tasks.

The following PowerShell script contains functions to download and install the scheduled tasks stored in our S3 bucket. Replace the $S3Bucket and $TaskManifest parameters with your S3 bucket name and manifest file name.

$VerbosePreference = "Continue";
$WorkingDirectory = "c:tasks";
$TaskManifest = "TaskManifest.xml";
$S3Bucket = "YourS3BucketName";
function Invoke-Functions
{
    Download-ScheduledTasks
    Install-ScheduledTasks
}
function Download-ScheduledTasks
{
    Read-S3Object `
        -BucketName $S3Bucket `
        -Key $TaskManifest `
        -File $WorkingDirectory$TaskManifest

    [xml]$cfg = gc $WorkingDirectory$TaskManifest;
    $cfg.configuration.scheduledtasks.task | 
        %{ 
           $task = $_;
           [string] $TaskFile = $task.source
           Read-S3Object `
                -BucketName $S3Bucket `
                -Key $task.source `
                -File "$WorkingDirectory$TaskFile" 
        }	
}

function Install-ScheduledTasks
{		
    [xml]$cfg = gc $WorkingDirectory$TaskManifest;
    $cfg.configuration.scheduledtasks.task | 
        %{
           $task = $_;
           [string] $TaskFile = $task.source
            Register-ScheduledTask `
                -Xml (get-content "$WorkingDirectory$TaskFile" | out-string) `
                -TaskName $task.name
        }
}

Invoke-Functions | Out-File "c:InstallTasksLog.txt" -Verbose;

Create an EC2 role to allow GetObject permissions to the S3 bucket.

Our PowerShell script uses the Read-S3Object PowerShell cmdlet to download the scheduled task definitions from S3. Therefore, we need to create an EC2 role that allows our EC2 instances to access our S3 bucket objects on our behalf.

Follow these steps to create the EC2 role.

  1. Open the IAM console.
  2. In the navigation pane, choose Policies.
  3. Choose Create Policy.
  4. Choose Create Your Own Policy, and use the following policy template. Replace [YourS3BucketName] with the name of your bucket.

  5. In the navigation pane, choose Roles.
  6. Choose Create Role.
  7. In the Role Name field, type a name for your role.
  8. Under AWS Service Roles, choose Amazon EC2, and then choose Select.
  9. On the Attach Policy page, choose the policy you created, and then choose Next Step.
  10. On the Review page, choose Create Role.

Use the EC2 Console to Launch EC2 Instance(s).

  1. Open the EC2 console, and choose Launch Instance.
  2. Choose your version of Microsoft Windows Server.
  3. Continue to Step: 3 Configure Instance Details.

    • For IAM Role, choose the EC2 role you just created.
    • In Advanced Details, paste the PowerShell script into the text box. Be sure to enclose it in tags as shown here.

  4. Complete the wizard steps and launch the Windows EC2 instance(s).
  5. After your instance(s) have been launched, you can verify the installation of your scheduled tasks.

Use AWS Tools for Windows PowerShell to Launch EC2 Instances.

In keeping with our theme of automation, you can use PowerShell to create the instances programmatically.

  1. If you have not already configured your PowerShell environment, follow these instructions to configure your PowerShell console to use the AWS Tools for Windows PowerShell.
  2. Save the PowerShell script that will download and install the scheduled tasks as InstallWindowsTasks.ps1.
  3. Save the following PowerShell script as a module named AWSHelper.psm1. This allows you to reuse it when you launch Windows EC2 instances in the future. Modify the following parameters with your environment resource values:

    # the key pair to associate with the instance(s)
    $KeyPairName
    # the EC2 instance(s) security group ID
    $SecurityGroupId
    # the subnet ID for the instance(s) after launch
    $SubnetId
    # the ARN of the EC2 role we created to allow access to our S3 bucket
    $InstanceProfile
    

     

    $VerbosePreference = "Continue";
    $scriptpath = $MyInvocation.MyCommand.Path;
    $moduledirectory = Split-Path $scriptpath;
    
    function ConvertTo-Base64($string) {
       $bytes = [System.Text.Encoding]: UTF8.GetBytes ($string);
       $encoded = [System.Convert]::ToBase64String($bytes); 
       return $encoded;
    }
    
    function New-WindowsEC2Instance
    {
      [CmdletBinding()]
      Param
      (                    
        [Parameter(Mandatory=$false)]
        [string] $InstanceType = "t2.micro",
        [Parameter(Mandatory=$false)]
        [string] $KeyPairName = "YourKeyPair", 
        [Parameter(Mandatory=$false)]
        [string] $SecurityGroupId = "sg-5xxxxxxx", 
        [Parameter(Mandatory=$false)]
        [string] $SubnetId = "subnet-1xxxxxxx",	
        [Parameter(Mandatory=$true)]
        [int32] $Count, 
        [Parameter(Mandatory=$false)]
        [string] $InstanceProfile ="EC2RoleARN",
        [Parameter(Mandatory=$false)]
        [string] $UserScript 
            = (Join-Path $script:moduledirectory "InstallWindowsTasks.ps1")
      )
      Process
      {
        $ami = Get-EC2ImageByName -Names 'WINDOWS_2012R2_BASE'
        $ImageID =  $ami[0].ImageId
        $UserData = "";
        if ($userScript -and (Test-Path $userScript))
        {
          $contents = "" + [System.IO.File]::ReadAllText($UserScript) + "";
    	  $filePath = gi $UserScript;
          $UserData = ConvertTo-Base64($contents);
        }
    
        $params = @{};
        $params.Add("ImageID", $ImageID);
        $params.Add("InstanceType", $InstanceType);
        $params.Add("KeyName", $KeyPairName); 
        $params.Add("MaxCount", $Count);
        $params.Add("MinCount", $Count);
        $params.Add("InstanceProfile_Arn", $InstanceProfile);
        $params.Add("SecurityGroupId", $SecurityGroupId); 
        $params.Add("SubnetId", $SubnetId);
        $params.Add("UserData", $UserData); 	
    
        $reservation = New-EC2Instance @params;
      }
    }
    
  4. To invoke the PowerShell code, import the AWSHelper.psm1 module, and then call the New-WindowsEC2Instance cmdlet as shown. Type the number of instances at the prompt.

Summary

The User data option provides a convenient way to automate the customization of your EC2 instances. For more information, see the following resources:

http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/UsingConfig_WinAMI.html#user-data-execution
http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/walkthrough-powershell.html