Add the winRM communicator to Oracle Classic builder.
update oracle classic docs with a minimal working windows example
This commit is contained in:
parent
36179c5a1d
commit
df45e0916d
|
@ -99,10 +99,6 @@ func NewConfig(raws ...interface{}) (*Config, error) {
|
|||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||
errs = packer.MultiErrorAppend(errs, es...)
|
||||
}
|
||||
if c.Comm.Type == "winrm" {
|
||||
err = fmt.Errorf("winRM is not supported with the oracle-classic builder yet.")
|
||||
errs = packer.MultiErrorAppend(errs, err)
|
||||
}
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, errs
|
||||
|
|
|
@ -19,6 +19,11 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult
|
|||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
|
||||
if config.Comm.Type != "ssh" {
|
||||
ui.Say("Not using SSH communicator; skip generating SSH keys...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// grab packer-generated key from statebag context.
|
||||
sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string))
|
||||
|
||||
|
@ -49,10 +54,14 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult
|
|||
|
||||
func (s *stepAddKeysToAPI) Cleanup(state multistep.StateBag) {
|
||||
// Delete the keys we created during this run
|
||||
keyName := state.Get("key_name").(string)
|
||||
keyName, ok := state.GetOk("key_name")
|
||||
if !ok {
|
||||
// No keys were generated; none need to be cleaned up.
|
||||
return
|
||||
}
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Deleting SSH keys...")
|
||||
deleteInput := compute.DeleteSSHKeyInput{Name: keyName}
|
||||
deleteInput := compute.DeleteSSHKeyInput{Name: keyName.(string)}
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
deleteClient := client.SSHKeys()
|
||||
err := deleteClient.DeleteSSHKey(&deleteInput)
|
||||
|
|
|
@ -18,7 +18,6 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
|
|||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
keyName := state.Get("key_name").(string)
|
||||
ipAddName := state.Get("ipres_name").(string)
|
||||
secListName := state.Get("security_list").(string)
|
||||
|
||||
|
@ -35,10 +34,13 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
|
|||
Name: config.ImageName,
|
||||
Shape: config.Shape,
|
||||
ImageList: config.SourceImageList,
|
||||
SSHKeys: []string{keyName},
|
||||
Networking: map[string]compute.NetworkingInfo{"eth0": netInfo},
|
||||
Attributes: config.attribs,
|
||||
}
|
||||
if config.Comm.Type == "ssh" {
|
||||
keyName := state.Get("key_name").(string)
|
||||
input.SSHKeys = []string{keyName}
|
||||
}
|
||||
|
||||
instanceInfo, err := instanceClient.CreateInstance(input)
|
||||
if err != nil {
|
||||
|
@ -48,6 +50,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu
|
|||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
state.Put("instance_info", instanceInfo)
|
||||
state.Put("instance_id", instanceInfo.ID)
|
||||
ui.Message(fmt.Sprintf("Created instance: %s.", instanceInfo.ID))
|
||||
return multistep.ActionContinue
|
||||
|
|
|
@ -3,10 +3,10 @@ package classic
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer/common/uuid"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
@ -15,53 +15,101 @@ type stepSecurity struct{}
|
|||
|
||||
func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Say("Configuring security lists and rules to enable SSH access...")
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
|
||||
secListName := fmt.Sprintf("/Compute-%s/%s/Packer_SSH_Allow_%s",
|
||||
config.IdentityDomain, config.Username, config.ImageName)
|
||||
commType := ""
|
||||
if config.Comm.Type == "ssh" {
|
||||
commType = "SSH"
|
||||
} else if config.Comm.Type == "winrm" {
|
||||
commType = "WINRM"
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Configuring security lists and rules to enable %s access...", commType))
|
||||
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
runUUID := uuid.TimeOrderedUUID()
|
||||
|
||||
namePrefix := fmt.Sprintf("/Compute-%s/%s/", config.IdentityDomain, config.Username)
|
||||
secListName := fmt.Sprintf("Packer_%s_Allow_%s_%s", commType, config.ImageName, runUUID)
|
||||
secListClient := client.SecurityLists()
|
||||
secListInput := compute.CreateSecurityListInput{
|
||||
Description: "Packer-generated security list to give packer ssh access",
|
||||
Name: secListName,
|
||||
Description: fmt.Sprintf("Packer-generated security list to give packer %s access", commType),
|
||||
Name: namePrefix + secListName,
|
||||
}
|
||||
_, err := secListClient.CreateSecurityList(&secListInput)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "already exists") {
|
||||
err = fmt.Errorf("Error creating security List to"+
|
||||
" allow Packer to connect to Oracle instance via SSH: %s", err)
|
||||
" allow Packer to connect to Oracle instance via %s: %s", commType, err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
// DOCS NOTE: user must have Compute_Operations role
|
||||
// Create security rule that allows Packer to connect via SSH
|
||||
secRulesClient := client.SecRules()
|
||||
secRulesInput := compute.CreateSecRuleInput{
|
||||
Action: "PERMIT",
|
||||
Application: "/oracle/public/ssh",
|
||||
Description: "Packer-generated security rule to allow ssh",
|
||||
DestinationList: fmt.Sprintf("seclist:%s", secListName),
|
||||
Name: fmt.Sprintf("Packer-allow-SSH-Rule_%s", config.ImageName),
|
||||
SourceList: config.SSHSourceList,
|
||||
// Create security rule that allows Packer to connect via SSH or winRM
|
||||
var application string
|
||||
if commType == "SSH" {
|
||||
application = "/oracle/public/ssh"
|
||||
} else if commType == "WINRM" {
|
||||
// Check to see whether a winRM security protocol is already defined;
|
||||
// don't need to do this for SSH becasue it is built into the Oracle API.
|
||||
protocolClient := client.SecurityProtocols()
|
||||
winrmProtocol := fmt.Sprintf("WINRM_%s", runUUID)
|
||||
input := compute.CreateSecurityProtocolInput{
|
||||
Name: winrmProtocol,
|
||||
Description: "packer-generated protocol to allow winRM communicator",
|
||||
DstPortSet: []string{"5985", "5986", "443"}, // TODO make configurable
|
||||
IPProtocol: "tcp",
|
||||
}
|
||||
|
||||
secRuleName := fmt.Sprintf("/Compute-%s/%s/Packer-allow-SSH-Rule_%s",
|
||||
config.IdentityDomain, config.Username, config.ImageName)
|
||||
_, err = secRulesClient.CreateSecRule(&secRulesInput)
|
||||
_, err = protocolClient.CreateSecurityProtocol(&input)
|
||||
if err != nil {
|
||||
log.Printf("Error creating security rule to allow SSH: %s", err.Error())
|
||||
if !strings.Contains(err.Error(), "already exists") {
|
||||
err = fmt.Errorf("Error creating security rule to"+
|
||||
" allow Packer to connect to Oracle instance via SSH: %s", err)
|
||||
err = fmt.Errorf("Error creating security protocol to"+
|
||||
" allow Packer to connect to Oracle instance via %s: %s", commType, err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("winrm_protocol", winrmProtocol)
|
||||
|
||||
// Check to see whether a winRM security application is already defined
|
||||
applicationClient := client.SecurityApplications()
|
||||
application = fmt.Sprintf("packer_winRM_%s", runUUID)
|
||||
applicationInput := compute.CreateSecurityApplicationInput{
|
||||
Description: "Allows Packer to connect to instance via winRM",
|
||||
DPort: "5985-5986",
|
||||
Name: application,
|
||||
Protocol: "TCP",
|
||||
}
|
||||
_, err = applicationClient.CreateSecurityApplication(&applicationInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating security application to"+
|
||||
" allow Packer to connect to Oracle instance via %s: %s", commType, err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("winrm_application", application)
|
||||
}
|
||||
secRulesClient := client.SecRules()
|
||||
secRuleName := fmt.Sprintf("Packer-allow-%s-Rule_%s_%s", commType,
|
||||
config.ImageName, runUUID)
|
||||
secRulesInput := compute.CreateSecRuleInput{
|
||||
Action: "PERMIT",
|
||||
Application: application,
|
||||
Description: "Packer-generated security rule to allow ssh/winrm",
|
||||
DestinationList: "seclist:" + namePrefix + secListName,
|
||||
Name: namePrefix + secRuleName,
|
||||
SourceList: config.SSHSourceList,
|
||||
}
|
||||
|
||||
_, err = secRulesClient.CreateSecRule(&secRulesInput)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error creating security rule to"+
|
||||
" allow Packer to connect to Oracle instance: %s", err)
|
||||
ui.Error(err.Error())
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
state.Put("security_rule_name", secRuleName)
|
||||
state.Put("security_list", secListName)
|
||||
|
@ -71,12 +119,15 @@ func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
func (s *stepSecurity) Cleanup(state multistep.StateBag) {
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
ui.Say("Deleting temporary rules and lists...")
|
||||
|
||||
namePrefix := fmt.Sprintf("/Compute-%s/%s/", config.IdentityDomain, config.Username)
|
||||
// delete security rules that Packer generated
|
||||
secRuleName := state.Get("security_rule_name").(string)
|
||||
secRulesClient := client.SecRules()
|
||||
ruleInput := compute.DeleteSecRuleInput{Name: secRuleName}
|
||||
ruleInput := compute.DeleteSecRuleInput{Name: namePrefix + secRuleName}
|
||||
err := secRulesClient.DeleteSecRule(&ruleInput)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated security rule %s; "+
|
||||
|
@ -86,10 +137,38 @@ func (s *stepSecurity) Cleanup(state multistep.StateBag) {
|
|||
// delete security list that Packer generated
|
||||
secListName := state.Get("security_list").(string)
|
||||
secListClient := client.SecurityLists()
|
||||
input := compute.DeleteSecurityListInput{Name: secListName}
|
||||
input := compute.DeleteSecurityListInput{Name: namePrefix + secListName}
|
||||
err = secListClient.DeleteSecurityList(&input)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+
|
||||
"please delete manually. (error : %s)", secListName, err.Error()))
|
||||
}
|
||||
|
||||
// Some extra cleanup if we used the winRM communicator
|
||||
if config.Comm.Type == "winrm" {
|
||||
// Delete the packer-generated protocol
|
||||
protocol := state.Get("winrm_protocol").(string)
|
||||
protocolClient := client.SecurityProtocols()
|
||||
deleteProtocolInput := compute.DeleteSecurityProtocolInput{
|
||||
Name: namePrefix + protocol,
|
||||
}
|
||||
err = protocolClient.DeleteSecurityProtocol(&deleteProtocolInput)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated winrm security protocol %s; "+
|
||||
"please delete manually. (error : %s)", protocol, err.Error()))
|
||||
}
|
||||
|
||||
// Delete the packer-generated application
|
||||
application := state.Get("winrm_application").(string)
|
||||
applicationClient := client.SecurityApplications()
|
||||
deleteApplicationInput := compute.DeleteSecurityApplicationInput{
|
||||
Name: namePrefix + application,
|
||||
}
|
||||
err = applicationClient.DeleteSecurityApplication(&deleteApplicationInput)
|
||||
if err != nil {
|
||||
ui.Say(fmt.Sprintf("Error deleting the packer-generated winrm security application %s; "+
|
||||
"please delete manually. (error : %s)", application, err.Error()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,16 +3,20 @@ package classic
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-oracle-terraform/compute"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
type stepSnapshot struct{}
|
||||
type stepSnapshot struct {
|
||||
cleanupSnap bool
|
||||
}
|
||||
|
||||
func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// get variables from state
|
||||
s.cleanupSnap = false
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
ui.Say("Creating Snapshot...")
|
||||
config := state.Get("config").(*Config)
|
||||
|
@ -26,6 +30,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
snapshotInput := &compute.CreateSnapshotInput{
|
||||
Instance: fmt.Sprintf("%s/%s", config.ImageName, instanceID),
|
||||
MachineImage: config.ImageName,
|
||||
Timeout: time.Minute * 20,
|
||||
}
|
||||
|
||||
snap, err := snapshotClient.CreateSnapshot(snapshotInput)
|
||||
|
@ -35,7 +40,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.cleanupSnap = true
|
||||
state.Put("snapshot", snap)
|
||||
ui.Message(fmt.Sprintf("Created snapshot: %s.", snap.Name))
|
||||
return multistep.ActionContinue
|
||||
|
@ -44,6 +49,9 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste
|
|||
func (s *stepSnapshot) Cleanup(state multistep.StateBag) {
|
||||
// Delete the snapshot
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
if !s.cleanupSnap {
|
||||
return
|
||||
}
|
||||
ui.Say("Deleting Snapshot...")
|
||||
client := state.Get("client").(*compute.ComputeClient)
|
||||
snap := state.Get("snapshot").(*compute.Snapshot)
|
||||
|
|
|
@ -114,3 +114,62 @@ obfuscated; you will need to add a working `username`, `password`,
|
|||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Basic Example -- Windows
|
||||
|
||||
Attributes file is optional for connecting via ssh, but required for winrm.
|
||||
|
||||
The following file contains the bare minimum necessary to get winRM working;
|
||||
you have to give it the password to give to the "Administrator" user, which
|
||||
will be the one winrm connects to. You must also whitelist your computer
|
||||
to connect via winRM -- the empty braces below whitelist any computer to access
|
||||
winRM but you can make it more secure by only allowing your build machine
|
||||
access. See the [docs](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/automating-instance-initialization-using-opc-init.html#GUID-A0A107D6-3B38-47F4-8FC8-96D50D99379B)
|
||||
for more details on how to define a trusted host.
|
||||
|
||||
Save this file as `windows_attributes.json`:
|
||||
|
||||
```{.json}
|
||||
{
|
||||
"userdata": {
|
||||
"administrator_password": "password",
|
||||
"winrm": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Following is a minimal but working Packer config that references this attributes
|
||||
file:
|
||||
```{.json}
|
||||
{
|
||||
"variables": {
|
||||
"opc_username": "{{ env `OPC_USERNAME`}}",
|
||||
"opc_password": "{{ env `OPC_PASSWORD`}}",
|
||||
"opc_identity_domain": "{{env `OPC_IDENTITY_DOMAIN`}}",
|
||||
"opc_api_endpoint": "{{ env `OPC_ENDPOINT`}}"
|
||||
},
|
||||
"builders": [
|
||||
{
|
||||
"type": "oracle-classic",
|
||||
"username": "{{ user `opc_username`}}",
|
||||
"password": "{{ user `opc_password`}}",
|
||||
"identity_domain": "{{ user `opc_identity_domain`}}",
|
||||
"api_endpoint": "{{ user `opc_api_endpoint`}}",
|
||||
"source_image_list": "/Compute-{{ user `opc_identity_domain` }}/{{ user opc_username`}}/Microsoft_Windows_Server_2012_R2-17.3.6-20170930-124649",
|
||||
"attributes_file": "./windows_attributes.json",
|
||||
"shape": "oc3",
|
||||
"image_name": "Packer_Windows_Demo_{{timestamp}}",
|
||||
"dest_image_list": "Packer_Windows_Demo",
|
||||
"communicator": "winrm",
|
||||
"winrm_username": "Administrator",
|
||||
"winrm_password": "password"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"type": "powershell",
|
||||
"inline": "Write-Output(\"HELLO WORLD\")"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
Loading…
Reference in New Issue