diff --git a/website/source/intro/getting-started/build-image.html.md b/website/source/intro/getting-started/build-image.html.md index 0375b7b1b..1161e81e0 100644 --- a/website/source/intro/getting-started/build-image.html.md +++ b/website/source/intro/getting-started/build-image.html.md @@ -12,7 +12,7 @@ description: |- # Build an Image With Packer installed, let's just dive right into it and build our first image. -Our first image will be an [Amazon EC2 AMI](https://aws.amazon.com/ec2/) +Our first image will be an [Amazon EC2 AMI](https://aws.amazon.com/ec2/). This is just an example. Packer can create images for [many platforms][platforms]. If you don't have an AWS account, [create one now](https://aws.amazon.com/free/). @@ -160,7 +160,7 @@ typically represent an ID (such as in the case of an AMI) or a set of files (such as for a VMware virtual machine). In this example, we only have a single artifact: the AMI in us-east-1 that was created. -This AMI is ready to use. If you wanted you could go and launch this AMI right +This AMI is ready to use. If you wanted you could go and launch this AMI right now and it would work great. -> **Note:** Your AMI ID will surely be different than the one above. If you @@ -201,7 +201,7 @@ how to validate and build templates into machine images. ## Some more examples: -### Another Linux Example, with provisioners: +### Another GNU/Linux Example, with provisioners: Create a file named `welcome.txt` and add the following: ``` @@ -210,12 +210,13 @@ WELCOME TO PACKER! Create a file named `example.sh` and add the following: -``` + +```bash #!/bin/bash -echo "hello +echo "hello" ``` -Set your access key and id as environment variables, so we don't need to pass +Set your access key and id as environment variables, so we don't need to pass them in through the command line: ``` @@ -225,7 +226,7 @@ export AWS_SECRET_ACCESS_KEY=MYSECRETACCESSKEY Now save the following text in a file named `firstrun.json`: -``` +```json { "variables": { "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", @@ -275,10 +276,10 @@ Now save the following text in a file named `firstrun.json`: and to build, run `packer build firstrun.json` -Note that if you wanted to use a `source_ami` instead of a `source_ami_filter` +Note that if you wanted to use a `source_ami` instead of a `source_ami_filter` it might look something like this: `"source_ami": "ami-fce3c696",` -Your output will look like this: +Your output will look like this: ``` amazon-ebs output will be in this color. @@ -317,29 +318,75 @@ amazon-ebs output will be in this color. ==> amazon-ebs: Waiting for AMI to become ready... ``` -### A windows example - -Note that this uses a larger instance. You will be charged for it. Also keep -in mind that using windows AMIs incurs a fee that you don't get when you use -linux AMIs. - -You'll need to have a boostrapping file to enable ssh or winrm; here's a basic -example of that file. - -``` -# set administrator password +### A Windows Example + +As with the GNU/Linux example above, should you decide to follow along and +build an AMI from the example template, provided you qualify for free tier +usage, you should not be charged for actually building the AMI. +However, please note that you will be charged for storage of the snapshot +associated with any AMI that you create. +If you wish to avoid further charges, follow the steps in the [Managing the +Image](/intro/getting-started/build-image.html#managing-the-image) section +above to deregister the created AMI and delete the associated snapshot once +you're done. + +Again, in this example, we are making use of an existing AMI available from +the Amazon marketplace as the *source* or starting point for building our +own AMI. In brief, Packer will spin up the source AMI, connect to it and then +run whatever commands or scripts we've configured in our build template to +customize the image. Finally, when all is done, Packer will wrap the whole +customized package up into a brand new AMI that will be available from the +[AWS AMI management page]( +https://console.aws.amazon.com/ec2/home?region=us-east-1#s=Images). Any +instances we subsequently create from this AMI will have our all of our +customizations baked in. This is the core benefit we are looking to +achieve from using the [Amazon EBS builder](/docs/builders/amazon-ebs.html) +in this example. + +Now, all this sounds simple enough right? Well, actually it turns out we +need to put in just a *bit* more effort to get things working as we'd like... + +Here's the issue: Out of the box, the instance created from our source AMI +is not configured to allow Packer to connect to it. So how do we fix it so +that Packer can connect in and customize our instance? + +Well, it turns out that Amazon provides a mechanism that allows us to run a +set of *pre-supplied* commands within the instance shortly after the instance +starts. Even better, Packer is aware of this mechanism. This gives us the +ability to supply Packer with the commands required to configure the instance +for a remote connection *in advance*. Once the commands are run, Packer +will be able to connect directly in to the instance and make the +customizations we need. + +Here's a basic example of a file that will configure the instance to allow +Packer to connect in over WinRM. As you will see, we will tell Packer about +our intentions by referencing this file and the commands within it from +within the `"builders"` section of our +[build template](/docs/templates/index.html) that we will create later. + +Note the `` and `` tags at the top and bottom of +the file. These tags tell Amazon we'd like to run the enclosed code with +PowerShell. You can also use `` tags to enclose any commands +that you would normally run in a Command Prompt window. See +[Running Commands on Your Windows Instance at Launch]( +http://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-user-data.html) +for more info about what's going on behind the scenes here. + +```powershell + +# Set administrator password net user Administrator SuperS3cr3t! wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE -# First, make sure WinRM doesn't run and can't be connected to -netsh advfirewall firewall add rule name="WinRM" protocol=TCP dir=in localport=5985 action=block -net stop winrm +# First, make sure WinRM can't be connected to +netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block -# turn off PowerShell execution policy restrictions -Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope LocalMachine +# Delete any existing WinRM listeners +winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null +winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null -# configure WinRM -winrm quickconfig -q +# Create a new WinRM listener and configure +winrm create winrm/config/listener?Address=*+Transport=HTTP winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}' winrm set winrm/config '@{MaxTimeoutms="7200000"}' winrm set winrm/config/service '@{AllowUnencrypted="true"}' @@ -347,75 +394,127 @@ winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}' winrm set winrm/config/service/auth '@{Basic="true"}' winrm set winrm/config/client/auth '@{Basic="true"}' -net stop winrm -set-service winrm -startupType automatic - -# Finally, allow WinRM connections and start the service -netsh advfirewall firewall set rule name="WinRM" new action=allow -net start winrm +# Configure UAC to allow privilege elevation in remote shells +$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System' +$Setting = 'LocalAccountTokenFilterPolicy' +Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force + +# Configure and restart the WinRM Service; Enable the required firewall exception +Stop-Service -Name WinRM +Set-Service -Name WinRM -StartupType Automatic +netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any +Start-Service -Name WinRM + ``` - -Save the above code in a file named `bootstrap_win.txt`. - -The example config below shows the two different ways of using the powershell -provisioner: `inline` and `script`. -The first example, `inline`, allows you to provide short snippets of code, and -will create the script file for you. The second example allows you to run more -complex code by providing the path to a script to run on the guest vm. - -Here's an example of a `sample_script.ps1` that will work with the environment -variables we will set in our packer config; copy the contents into your own -`sample_script.ps1` and provide the path to it in your packer config: - -``` -Write-Output("PACKER_BUILD_NAME is automatically set for you,) -Write-Output("or you can set it in your builder variables; ) -Write-Output("the default for this builder is: " + $Env:PACKER_BUILD_NAME ) -Write-Output("Remember that escaping variables in powershell requires backticks: ) -Write-Output("for example, VAR1 from our config is " + $Env:VAR1 ) -Write-Output("Likewise, VAR2 is " + $Env:VAR2 ) -Write-Output("and VAR3 is " + $Env:VAR3 ) +Save the above code in a file named `bootstrap_win.txt`. + +-> **A quick aside/warning:** +Windows administrators in the know might be wondering why we haven't simply +used a `winrm quickconfig -q` command in the script above, as this would +*automatically* set up all of the required elements necessary for connecting +over WinRM. Why all the extra effort to configure things manually? +Well, long and short, use of the `winrm quickconfig -q` command can sometimes +cause the Packer build to fail shortly after the WinRM connection is +established. How? +1. Among other things, as well as setting up the listener for WinRM, the +quickconfig command also configures the firewall to allow management messages +to be sent over HTTP. +2. This undoes the previous command in the script that configured the +firewall to prevent this access. +3. The upshot is that the system is configured and ready to accept WinRM +connections earlier than intended. +4. If Packer establishes its WinRM connection immediately after execution of +the 'winrm quickconfig -q' command, the later commands within the script that +restart the WinRM service will unceremoniously pull the rug out from under +the connection. +5. While Packer does *a lot* to ensure the stability of its connection in to +your instance, this sort of abuse can prove to be too much and *may* cause +your Packer build to stall irrecoverably or fail! + +Now we've got the business of getting Packer connected to our instance +taken care of, let's get on with the *real* reason we're doing all this, +which is actually configuring and customizing the instance. Again, we do this +with [Provisioners](/docs/provisioners/index.html). + +The example config below shows the two different ways of using the [PowerShell +provisioner](/docs/provisioners/powershell.html): `inline` and `script`. +The first example, `inline`, allows you to provide short snippets of code, and +will create the script file for you. The second example allows you to run more +complex code by providing the path to a script to run on the guest VM. + +Here's an example of a `sample_script.ps1` that will work with the environment +variables we will set in our build template; copy the contents into your own +`sample_script.ps1` and provide the path to it in your build template: + +```powershell +Write-Host "PACKER_BUILD_NAME is automatically set for you, " -NoNewline +Write-Host "or you can set it in your builder variables; " -NoNewline +Write-Host "The default for this builder is:" $Env:PACKER_BUILD_NAME + +Write-Host "Use backticks as the escape character when required in powershell:" +Write-Host "For example, VAR1 from our config is:" $Env:VAR1 +Write-Host "Likewise, VAR2 is:" $Env:VAR2 +Write-Host "Finally, VAR3 is:" $Env:VAR3 ``` -Next you need to create a packer config that will use this bootstrap file. See -the example below, which contains examples of using source_ami_filter for -windows in addition to the powershell and windows-restart provisioners: +Finally, we need to create the actual [build template]( +/docs/templates/index.html). +Remember, this template is the core configuration file that Packer uses to +understand what you want to build, and how you want to build it. + +As mentioned earlier, the specific builder we are using in this example +is the [Amazon EBS builder](/docs/builders/amazon-ebs.html). +The template below demonstrates use of the [`source_ami_filter`]( +/docs/builders/amazon-ebs.html#source_ami_filter) configuration option +available within the builder for automatically selecting the *latest* +suitable source Windows AMI provided by Amazon. +We also use the `user_data_file` configuration option provided by the builder +to reference the bootstrap file we created earlier. As you will recall, our +bootstrap file contained all the commands we needed to supply in advance of +actually spinning up the instance, so that later on, our instance is +configured to allow Packer to connect in to it. + +The `"provisioners"` section of the template demonstrates use of the +[powershell](/docs/provisioners/powershell.html) and +[windows-restart](/docs/provisioners/windows-restart.html) provisioners to +customize and control the build process: -``` +```json { "variables": { - "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", - "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", - "region": "us-east-1" + "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}", + "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}", + "region": "us-east-1" }, "builders": [ - { - "type": "amazon-ebs", - "access_key": "{{ user `aws_access_key` }}", - "secret_key": "{{ user `aws_secret_key` }}", - "region": "us-east-1", - "instance_type": "m3.medium", - "source_ami_filter": { - "filters": { - "virtualization-type": "hvm", - "name": "*WindowsServer2012R2*", - "root-device-type": "ebs" + { + "type": "amazon-ebs", + "access_key": "{{ user `aws_access_key` }}", + "secret_key": "{{ user `aws_secret_key` }}", + "region": "{{ user `region` }}", + "instance_type": "t2.micro", + "source_ami_filter": { + "filters": { + "virtualization-type": "hvm", + "name": "*Windows_Server-2012-R2*English-64Bit-Base*", + "root-device-type": "ebs" + }, + "most_recent": true, + "owners": "amazon" }, - "most_recent": true, - "owners": "amazon" - }, - "ami_name": "packer-demo-{{timestamp}}", - "user_data_file": "./bootstrap_win.txt", - "communicator": "winrm", - "winrm_username": "Administrator", - "winrm_password": "SuperS3cr3t!" - }], + "ami_name": "packer-demo-{{timestamp}}", + "user_data_file": "./bootstrap_win.txt", + "communicator": "winrm", + "winrm_username": "Administrator", + "winrm_password": "SuperS3cr3t!" + } + ], "provisioners": [ { "type": "powershell", "environment_vars": ["DEVOPS_LIFE_IMPROVER=PACKER"], - "inline": "Write-Output(\"HELLO NEW USER; WELCOME TO $Env:DEVOPS_LIFE_IMPROVER\")" + "inline": "Write-Host \"HELLO NEW USER; WELCOME TO $Env:DEVOPS_LIFE_IMPROVER\"" }, { "type": "windows-restart" @@ -433,45 +532,57 @@ windows in addition to the powershell and windows-restart provisioners: } ``` -Then `packer build firstrun.json` +Save the build template as `firstrun.json`. + +Next we need to set things up so that Packer is able to access and use our +AWS account. Set your access key and id as environment variables, so we +don't need to pass them in through the command line: + +``` +export AWS_ACCESS_KEY_ID=MYACCESSKEYID +export AWS_SECRET_ACCESS_KEY=MYSECRETACCESSKEY +``` + +Finally, we can create our new AMI by running `packer build firstrun.json` You should see output like this: ``` amazon-ebs output will be in this color. -==> amazon-ebs: Prevalidating AMI Name: packer-demo-1507234504 - amazon-ebs: Found Image ID: ami-d79776ad -==> amazon-ebs: Creating temporary keypair: packer_59d692c8-81f9-6a15-2502-0ca730980bed -==> amazon-ebs: Creating temporary security group for this instance: packer_59d692f0-dd01-6879-d8f8-7765327f5365 -==> amazon-ebs: Authorizing access to port 5985 on the temporary security group... +==> amazon-ebs: Prevalidating AMI Name: packer-demo-1507933843 + amazon-ebs: Found Image ID: ami-23d93c59 +==> amazon-ebs: Creating temporary keypair: packer_59e13e94-203a-1bca-5327-bebf0d5ad15a +==> amazon-ebs: Creating temporary security group for this instance: packer_59e13ea9-3220-8dab-29c0-ed7f71e221a1 +==> amazon-ebs: Authorizing access to port 5985 from 0.0.0.0/0 in the temporary security group... ==> amazon-ebs: Launching a source AWS instance... ==> amazon-ebs: Adding tags to source instance amazon-ebs: Adding tag: "Name": "Packer Builder" - amazon-ebs: Instance ID: i-04467596029d0a2ff -==> amazon-ebs: Waiting for instance (i-04467596029d0a2ff) to become ready... + amazon-ebs: Instance ID: i-0349406ac85f02166 +==> amazon-ebs: Waiting for instance (i-0349406ac85f02166) to become ready... ==> amazon-ebs: Skipping waiting for password since WinRM password set... ==> amazon-ebs: Waiting for WinRM to become available... amazon-ebs: WinRM connected. ==> amazon-ebs: Connected to WinRM! ==> amazon-ebs: Provisioning with Powershell... -==> amazon-ebs: Provisioning with powershell script: /var/folders/8t/0yb5q0_x6mb2jldqq_vjn3lr0000gn/T/packer-powershell-provisioner079851514 +==> amazon-ebs: Provisioning with powershell script: /var/folders/15/d0f7gdg13rnd1cxp7tgmr55c0000gn/T/packer-powershell-provisioner175214995 amazon-ebs: HELLO NEW USER; WELCOME TO PACKER ==> amazon-ebs: Restarting Machine ==> amazon-ebs: Waiting for machine to restart... - amazon-ebs: WIN-164614OO21O restarted. + amazon-ebs: WIN-TEM0TDL751M restarted. ==> amazon-ebs: Machine successfully restarted, moving on ==> amazon-ebs: Provisioning with Powershell... -==> amazon-ebs: Provisioning with powershell script: ./scripts/sample_script.ps1 - amazon-ebs: PACKER_BUILD_NAME is automatically set for you, or you can set it in your builder variables; the default for this builder is: amazon-ebs - amazon-ebs: Remember that escaping variables in powershell requires backticks; for example VAR1 from our config is A$Dollar - amazon-ebs: Likewise, VAR2 is A`Backtick - amazon-ebs: and VAR3 is A'SingleQuote +==> amazon-ebs: Provisioning with powershell script: ./sample_script.ps1 + amazon-ebs: PACKER_BUILD_NAME is automatically set for you, or you can set it in your builder variables; The default for this builder is: amazon-ebs + amazon-ebs: Use backticks as the escape character when required in powershell: + amazon-ebs: For example, VAR1 from our config is: A$Dollar + amazon-ebs: Likewise, VAR2 is: A`Backtick + amazon-ebs: Finally, VAR3 is: A'SingleQuote ==> amazon-ebs: Stopping the source instance... amazon-ebs: Stopping instance, attempt 1 ==> amazon-ebs: Waiting for the instance to stop... -==> amazon-ebs: Creating the AMI: packer-demo-1507234504 - amazon-ebs: AMI: ami-2970b753 +==> amazon-ebs: Creating the AMI: packer-demo-1507933843 + amazon-ebs: AMI: ami-100fc56a ==> amazon-ebs: Waiting for AMI to become ready... ==> amazon-ebs: Terminating the source AWS instance... ==> amazon-ebs: Cleaning up any extra volumes... @@ -482,10 +593,31 @@ Build 'amazon-ebs' finished. ==> Builds finished. The artifacts of successful builds are: --> amazon-ebs: AMIs were created: -us-east-1: ami-2970b753 +us-east-1: ami-100fc56a ``` -And if you navigate to your EC2 dashboard you should see your shiny new AMI. +And if you navigate to your EC2 dashboard you should see your shiny new AMI +listed in the main window of the Images -> AMIs section. + +Why stop there though? + +As you'll see, with one simple change to the template above, it's +just as easy to create your own Windows 2008 or Windows 2016 AMIs. Just +set the value for the name field within `source_ami_filter` as required: + +For Windows 2008 SP2: + +``` + "name": "*Windows_Server-2008-SP2*English-64Bit-Base*", +``` + +For Windows 2016: + +``` + "name": "*Windows_Server-2016-English-Full-Base*", +``` +The bootstrapping and sample provisioning should work the same across all +Windows server versions. [platforms]: /docs/builders/index.html