Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NTLM authentication support #56

Merged
merged 10 commits into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion ad/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type ProviderConfig struct {
KrbRealm string
KrbConfig string
KrbSpn string
WinRMUseNTLM bool
}

// NewConfig returns a new Config struct populated with Resource Data.
Expand All @@ -45,6 +46,7 @@ func NewConfig(d *schema.ResourceData) ProviderConfig {
krbRealm := d.Get("krb_realm").(string)
krbConfig := d.Get("krb_conf").(string)
krbSpn := d.Get("krb_spn").(string)
winRMUseNTLM := d.Get("winrm_use_ntlm").(bool)

cfg := ProviderConfig{
WinRMHost: winRMHost,
Expand All @@ -56,6 +58,7 @@ func NewConfig(d *schema.ResourceData) ProviderConfig {
KrbRealm: krbRealm,
KrbConfig: krbConfig,
KrbSpn: krbSpn,
WinRMUseNTLM: winRMUseNTLM,
}

return cfg
Expand All @@ -78,8 +81,13 @@ func GetWinRMConnection(config ProviderConfig) (*winrm.Client, error) {
params.TransportDecorator = NewKerberosTransporter(config)
winrmClient, err = winrm.NewClientWithParameters(endpoint, "", "", params)
} else {
winrmClient, err = winrm.NewClient(endpoint, config.WinRMUsername, config.WinRMPassword)
params := winrm.DefaultParameters
if config.WinRMUseNTLM {
params.TransportDecorator = func() winrm.Transporter { return &winrm.ClientNTLM{} }
}
winrmClient, err = winrm.NewClientWithParameters(endpoint, config.WinRMUsername, config.WinRMPassword, params)
}

if err != nil {
return nil, err
}
Expand Down
7 changes: 6 additions & 1 deletion ad/data_source_ad_computer.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ func dataSourceADComputer() *schema.Resource {
}

func dataSourceADComputerRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

dn := winrmhelper.SanitiseTFInput(d, "dn")
guid := winrmhelper.SanitiseTFInput(d, "guid")

Expand Down
6 changes: 5 additions & 1 deletion ad/data_source_ad_gpo.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ func dataSourceADGPORead(d *schema.ResourceData, meta interface{}) error {
name := winrmhelper.SanitiseTFInput(d, "name")
guid := winrmhelper.SanitiseTFInput(d, "guid")

client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

gpo, err := winrmhelper.GetGPOFromHost(client, name, guid)
if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion ad/data_source_ad_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ func dataSourceADGroup() *schema.Resource {
}

func dataSourceADGroupRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

dn := d.Get("guid").(string)

g, err := winrmhelper.GetGroupFromHost(client, dn)
Expand Down
6 changes: 5 additions & 1 deletion ad/data_source_ad_ou.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ func dataSourceADOU() *schema.Resource {
}

func dataSourceADOURead(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

name := winrmhelper.SanitiseTFInput(d, "name")
path := winrmhelper.SanitiseTFInput(d, "path")
Expand Down
7 changes: 6 additions & 1 deletion ad/data_source_ad_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ func dataSourceADUser() *schema.Resource {

func dataSourceADUserRead(d *schema.ResourceData, meta interface{}) error {
dn := d.Get("guid").(string)
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

u, err := winrmhelper.GetUserFromHost(client, dn)
if err != nil {
return err
Expand Down
75 changes: 60 additions & 15 deletions ad/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ad

import (
"strings"
"sync"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/masterzen/winrm"
Expand Down Expand Up @@ -66,6 +67,12 @@ func Provider() *schema.Provider {
DefaultFunc: schema.EnvDefaultFunc("AD_KRB_SPN", ""),
Description: "Alternative Service Principal Name. (default: none, environment variable: AD_KRB_SPN)",
},
"winrm_use_ntlm": {
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("AD_WINRM_USE_NTLM", false),
Description: "Use NTLM authentication. (default: false, environment variable: AD_WINRM_USE_NTLM)",
},
},
DataSourcesMap: map[string]*schema.Resource{
"ad_user": dataSourceADUser(),
Expand All @@ -90,29 +97,67 @@ func Provider() *schema.Provider {

// ProviderConf holds structures that are useful to the provider at runtime.
type ProviderConf struct {
Configuration *ProviderConfig
WinRMClient *winrm.Client
WinRMCPClient *winrmcp.Winrmcp
Configuration *ProviderConfig
winRMClients []*winrm.Client
winRMCPClients []*winrmcp.Winrmcp
mx *sync.Mutex
}

func initProviderConfig(d *schema.ResourceData) (interface{}, error) {
// AcquireWinRMClient get a thread safe WinRM client from the pool. Create a new one if the pool is empty
func (pcfg ProviderConf) AcquireWinRMClient() (winRMClient *winrm.Client, err error) {
pcfg.mx.Lock()
defer pcfg.mx.Unlock()
if len(pcfg.winRMClients) == 0 {
winRMClient, err = GetWinRMConnection(*pcfg.Configuration)
if err != nil {
return nil, err
}
} else {
winRMClient = pcfg.winRMClients[0]
pcfg.winRMClients = pcfg.winRMClients[1:]
}
return winRMClient, nil
}

cfg := NewConfig(d)
// ReleaseWinRMClient returns a thread safe WinRM client after usage to the pool.
func (pcfg ProviderConf) ReleaseWinRMClient(winRMClient *winrm.Client) {
pcfg.mx.Lock()
defer pcfg.mx.Unlock()
pcfg.winRMClients = append(pcfg.winRMClients, winRMClient)
}

winRMClient, err := GetWinRMConnection(cfg)
if err != nil {
return nil, err
// AcquireWinRMCPClient get a thread safe WinRM client from the pool. Create a new one if the pool is empty
func (pcfg ProviderConf) AcquireWinRMCPClient() (winRMCPClient *winrmcp.Winrmcp, err error) {
pcfg.mx.Lock()
defer pcfg.mx.Unlock()
if len(pcfg.winRMClients) == 0 {
winRMCPClient, err = GetWinRMCPConnection(*pcfg.Configuration)
if err != nil {
return nil, err
}
} else {
winRMCPClient = pcfg.winRMCPClients[0]
pcfg.winRMCPClients = pcfg.winRMCPClients[1:]
}
return winRMCPClient, nil
}

winRMCPClient, err := GetWinRMCPConnection(cfg)
if err != nil {
return nil, err
}
// ReleaseWinRMCPClient returns a thread safe WinRM client after usage to the pool.
func (pcfg ProviderConf) ReleaseWinRMCPClient(winRMCPClient *winrmcp.Winrmcp) {
pcfg.mx.Lock()
defer pcfg.mx.Unlock()
pcfg.winRMCPClients = append(pcfg.winRMCPClients, winRMCPClient)
}

func initProviderConfig(d *schema.ResourceData) (interface{}, error) {

cfg := NewConfig(d)

pcfg := ProviderConf{
Configuration: &cfg,
WinRMClient: winRMClient,
WinRMCPClient: winRMCPClient,
Configuration: &cfg,
winRMClients: make([]*winrm.Client, 0),
winRMCPClients: make([]*winrmcp.Winrmcp, 0),
mx: &sync.Mutex{},
}

return pcfg, nil
Expand Down
31 changes: 25 additions & 6 deletions ad/resource_ad_computer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ func resourceADComputerRead(d *schema.ResourceData, meta interface{}) error {
return nil
}

client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

computer, err := winrmhelper.NewComputerFromHost(client, d.Id())
if err != nil {
Expand All @@ -77,7 +81,12 @@ func resourceADComputerRead(d *schema.ResourceData, meta interface{}) error {
}

func resourceADComputerCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

computer := winrmhelper.NewComputerFromResource(d)

guid, err := computer.Create(client)
Expand All @@ -89,7 +98,12 @@ func resourceADComputerCreate(d *schema.ResourceData, meta interface{}) error {
}

func resourceADComputerUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

computer := winrmhelper.NewComputerFromResource(d)
keys := []string{"container"}
changes := make(map[string]interface{})
Expand All @@ -99,7 +113,7 @@ func resourceADComputerUpdate(d *schema.ResourceData, meta interface{}) error {
}
}

err := computer.Update(client, changes)
err = computer.Update(client, changes)
if err != nil {
return fmt.Errorf("error while updating computer with id %q: %s", d.Id(), err)
}
Expand All @@ -110,9 +124,14 @@ func resourceADComputerDelete(d *schema.ResourceData, meta interface{}) error {
if d.Id() == "" {
return nil
}
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

computer := winrmhelper.NewComputerFromResource(d)
err := computer.Delete(client)
err = computer.Delete(client)
if err != nil {
return fmt.Errorf("error while deleting a computer object with id %q: %s", d.Id(), err)
}
Expand Down
7 changes: 6 additions & 1 deletion ad/resource_ad_computer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ func testAccResourceADComputerExists(resource, name string, expected bool) resou
return fmt.Errorf("%s key not found in state", resource)
}

client := testAccProvider.Meta().(ProviderConf).WinRMClient
client, err := testAccProvider.Meta().(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer testAccProvider.Meta().(ProviderConf).ReleaseWinRMClient(client)

guid := rs.Primary.ID
computer, err := winrmhelper.NewComputerFromHost(client, guid)
if err != nil {
Expand Down
32 changes: 26 additions & 6 deletions ad/resource_ad_gplink.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ func resourceADGPLink() *schema.Resource {
}

func resourceADGPLinkRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

idParts := strings.SplitN(d.Id(), "_", 2)
if len(idParts) != 2 {
return fmt.Errorf("malformed ID for GPLink resource with ID %q", d.Id())
Expand All @@ -87,7 +92,12 @@ func resourceADGPLinkRead(d *schema.ResourceData, meta interface{}) error {
}

func resourceADGPLinkCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

gplink := winrmhelper.GetGPLinkFromResource(d)
gpLinkID, err := gplink.NewGPLink(client)
if err != nil {
Expand All @@ -99,7 +109,12 @@ func resourceADGPLinkCreate(d *schema.ResourceData, meta interface{}) error {
}

func resourceADGPLinkUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

keys := []string{"enforced", "enabled", "order"}
changes := make(map[string]interface{})
for _, key := range keys {
Expand All @@ -108,7 +123,7 @@ func resourceADGPLinkUpdate(d *schema.ResourceData, meta interface{}) error {
}
}
gplink := winrmhelper.GetGPLinkFromResource(d)
err := gplink.ModifyGPLink(client, changes)
err = gplink.ModifyGPLink(client, changes)
if err != nil {
return fmt.Errorf("while modifying GPLink with id %q: %s", d.Id(), err)
}
Expand All @@ -117,9 +132,14 @@ func resourceADGPLinkUpdate(d *schema.ResourceData, meta interface{}) error {
}

func resourceADGPLinkDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(ProviderConf).WinRMClient
client, err := meta.(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer meta.(ProviderConf).ReleaseWinRMClient(client)

gplink := winrmhelper.GetGPLinkFromResource(d)
err := gplink.RemoveGPLink(client)
err = gplink.RemoveGPLink(client)
if err != nil {
return fmt.Errorf("while deleting resource with ID %q: %s", d.Id(), err)
}
Expand Down
6 changes: 5 additions & 1 deletion ad/resource_ad_gplink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ func testAccResourceADGPLinkExists(resourceName string, order int, enforced, ena
return fmt.Errorf("%s key not found in state", resourceName)
}
id := rs.Primary.ID
client := testAccProvider.Meta().(ProviderConf).WinRMClient
client, err := testAccProvider.Meta().(ProviderConf).AcquireWinRMClient()
if err != nil {
return err
}
defer testAccProvider.Meta().(ProviderConf).ReleaseWinRMClient(client)
idParts := strings.SplitN(id, "_", 2)
if len(idParts) != 2 {
return fmt.Errorf("malformed ID for GPLink resource with ID %q", id)
Expand Down
Loading