Skip to content

Commit 7d3e5b9

Browse files
authored
Merge pull request #337 from pgporada/allow-api-token-auth
Allow API token authentication
2 parents 73d06e3 + a736cc2 commit 7d3e5b9

File tree

6 files changed

+111
-38
lines changed

6 files changed

+111
-38
lines changed

docs/guides/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ To install this provider, copy and paste this code into your Terraform configura
66
terraform {
77
required_providers {
88
proxmox = {
9-
source = "Telmate/proxmox"
9+
source = "telmate/proxmox"
1010
version = "<version tag>"
1111
}
1212
}

docs/index.md

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,33 @@
33
A Terraform provider is responsible for understanding API interactions and exposing resources. The Proxmox provider
44
uses the Proxmox API. This provider exposes two resources: [proxmox_vm_qemu](docs/resources/vm_qemu.md) and [proxmox_lxc](docs/resources/lxc.md).
55

6-
## Creating the connection
6+
## Creating the connection via username and password
77

88
When connecting to the Proxmox API, the provider has to know at least three parameters: the URL, username and password.
9-
One can supply fields using the provider syntax in Terraform. It is recommended to pass secrets through environment
9+
One can supply fields using the provider syntax in Terraform. It is recommended to pass secrets through environment
1010
variables.
1111

1212
```bash
13-
export PM_PASS=password
13+
export PM_USER="terraform-user@pve"
14+
export PM_PASS="password"
15+
```
16+
17+
```hcl
18+
provider "proxmox" {
19+
pm_api_url = "https://proxmox-server01.example.com:8006/api2/json"
20+
}
21+
```
22+
23+
## Creating the connection via username and API token
24+
25+
```bash
26+
export PM_API_TOKEN_ID="terraform-user@pve!mytoken"
27+
export PM_API_TOKEN_SECRET="afcd8f45-acc1-4d0f-bb12-a70b0777ec11"
1428
```
1529

1630
```hcl
1731
provider "proxmox" {
1832
pm_api_url = "https://proxmox-server01.example.com:8006/api2/json"
19-
pm_user = "terraform-user@pve"
2033
}
2134
```
2235

@@ -25,10 +38,12 @@ provider "proxmox" {
2538
The following arguments are supported in the provider block:
2639

2740
* `pm_api_url` - (Required; or use environment variable `PM_API_URL`) This is the target Proxmox API endpoint.
28-
* `pm_user` - (Required; or use environment variable `PM_USER`) The user, maybe required to include @pam.
29-
* `pm_password` - (Required; sensitive; or use environment variable `PM_PASS`) The password.
30-
* `pm_otp` - (Optional; or use environment variable `PM_OTP`) The 2FA OTP code.
31-
* `pm_tls_insecure` - (Optional) Disable TLS verification while connecting.
41+
* `pm_user` - (Optional; or use environment variable `PM_USER`) The user, remember to include the authentication realm such as myuser@pam or myuser@pve.
42+
* `pm_password` - (Optional; sensitive; or use environment variable `PM_PASS`) The password.
43+
* `pm_api_token_id` - (Optional; or use environment variable `PM_API_TOKEN_ID`) This is an [API token](https://pve.proxmox.com/pve-docs/pveum-plain.html) you have previously created for a specific user.
44+
* `pm_api_token_secret` - (Optional; or use environment variable `PM_API_TOKEN_SECRET`) This is a uuid that is only available when initially creating the token.
45+
* `pm_otp` - (Optional; or use environment variable `PM_OTP`) The 2FA OTP code.
46+
* `pm_tls_insecure` - (Optional) Disable TLS verification while connecting to the proxmox server.
3247
* `pm_parallel` - (Optional; defaults to 4) Allowed simultaneous Proxmox processes (e.g. creating resources).
3348
* `pm_log_enable` - (Optional; defaults to false) Enable debug logging, see the section below for logging details.
3449
* `pm_log_levels` - (Optional) A map of log sources and levels.
@@ -42,10 +57,12 @@ Additionally, one can set the `PM_OTP_PROMPT` environment variable to prompt for
4257
The provider is able to output detailed logs upon request. Note that this feature is intended for development purposes, but could also be used to help investigate bugs. For example: the following code when placed into the provider "proxmox" block will enable loging to the file "terraform-plugin-proxmox.log". All log sources will default to the "debug" level, and any stdout/stderr from sublibraries (proxmox-api-go) will be silenced (set to non-empty string to enable).
4358

4459
```hcl
60+
provider "proxmox" {
4561
pm_log_enable = true
4662
pm_log_file = "terraform-plugin-proxmox.log"
4763
pm_log_levels = {
4864
_default = "debug"
4965
_capturelog = ""
5066
}
67+
}
5168
```

main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package main
22

33
import (
44
"github.com/Telmate/terraform-provider-proxmox/proxmox"
5-
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
65
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
77
)
88

99
func main() {

proxmox/provider.go

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77
"regexp"
88
"strconv"
99
"sync"
10+
"strings"
1011

1112
pxapi "github.com/Telmate/proxmox-api-go/proxmox"
12-
//pxapi "github.com/doransmestad/proxmox-api-go/proxmox"
1313
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1414
)
1515

@@ -46,15 +46,15 @@ func Provider() *schema.Provider {
4646
Schema: map[string]*schema.Schema{
4747
"pm_user": {
4848
Type: schema.TypeString,
49-
Required: true,
49+
Optional: true,
5050
DefaultFunc: schema.EnvDefaultFunc("PM_USER", nil),
51-
Description: "username, maywith with @pam",
51+
Description: "Username e.g. myuser or myuser@pam",
5252
},
5353
"pm_password": {
5454
Type: schema.TypeString,
55-
Required: true,
55+
Optional: true,
5656
DefaultFunc: schema.EnvDefaultFunc("PM_PASS", nil),
57-
Description: "secret",
57+
Description: "Password to authenticate into proxmox",
5858
Sensitive: true,
5959
},
6060
"pm_api_url": {
@@ -63,6 +63,19 @@ func Provider() *schema.Provider {
6363
DefaultFunc: schema.EnvDefaultFunc("PM_API_URL", nil),
6464
Description: "https://host.fqdn:8006/api2/json",
6565
},
66+
"pm_api_token_id": {
67+
Type: schema.TypeString,
68+
Optional: true,
69+
DefaultFunc: schema.EnvDefaultFunc("PM_API_TOKEN_ID", nil),
70+
Description: "API TokenID e.g. root@pam!mytesttoken",
71+
},
72+
"pm_api_token_secret": {
73+
Type: schema.TypeString,
74+
Optional: true,
75+
DefaultFunc: schema.EnvDefaultFunc("PM_API_TOKEN_SECRET", nil),
76+
Description: "The secret uuid corresponding to a TokenID",
77+
Sensitive: true,
78+
},
6679
"pm_parallel": {
6780
Type: schema.TypeInt,
6881
Optional: true,
@@ -72,20 +85,24 @@ func Provider() *schema.Provider {
7285
Type: schema.TypeBool,
7386
Optional: true,
7487
DefaultFunc: schema.EnvDefaultFunc("PM_TLS_INSECURE", false),
88+
Description: "By default, every TLS connection is verified to be secure. This option allows terraform to proceed and operate on servers considered insecure. For example if you're connecting to a remote host and you do not have the CA cert that issued the proxmox api url's certificate.",
7589
},
7690
"pm_log_enable": {
77-
Type: schema.TypeBool,
78-
Optional: true,
79-
Default: false,
91+
Type: schema.TypeBool,
92+
Optional: true,
93+
Default: false,
94+
Description: "Enable provider logging to get proxmox API logs",
8095
},
8196
"pm_log_levels": {
82-
Type: schema.TypeMap,
83-
Optional: true,
97+
Type: schema.TypeMap,
98+
Optional: true,
99+
Description: "Configure the logging level to display; trace, debug, info, warn, etc",
84100
},
85101
"pm_log_file": {
86-
Type: schema.TypeString,
87-
Optional: true,
88-
Default: "terraform-plugin-proxmox.log",
102+
Type: schema.TypeString,
103+
Optional: true,
104+
Default: "terraform-plugin-proxmox.log",
105+
Description: "Write logs to this specific file",
89106
},
90107
"pm_timeout": {
91108
Type: schema.TypeInt,
@@ -105,17 +122,26 @@ func Provider() *schema.Provider {
105122
"proxmox_vm_qemu": resourceVmQemu(),
106123
"proxmox_lxc": resourceLxc(),
107124
"proxmox_lxc_disk": resourceLxcDisk(),
108-
// TODO - storage_iso
109-
// TODO - bridge
110-
// TODO - vm_qemu_template
125+
// TODO - proxmox_storage_iso
126+
// TODO - proxmox_bridge
127+
// TODO - proxmox_vm_qemu_template
111128
},
112129

113130
ConfigureFunc: providerConfigure,
114131
}
115132
}
116133

117134
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
118-
client, err := getClient(d.Get("pm_api_url").(string), d.Get("pm_user").(string), d.Get("pm_password").(string), d.Get("pm_otp").(string), d.Get("pm_tls_insecure").(bool), d.Get("pm_timeout").(int))
135+
client, err := getClient(
136+
d.Get("pm_api_url").(string),
137+
d.Get("pm_user").(string),
138+
d.Get("pm_password").(string),
139+
d.Get("pm_api_token_id").(string),
140+
d.Get("pm_api_token_secret").(string),
141+
d.Get("pm_otp").(string),
142+
d.Get("pm_tls_insecure").(bool),
143+
d.Get("pm_timeout").(int),
144+
)
119145
if err != nil {
120146
return nil, err
121147
}
@@ -153,13 +179,43 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
153179
}, nil
154180
}
155181

156-
func getClient(pm_api_url string, pm_user string, pm_password string, pm_otp string, pm_tls_insecure bool, pm_timeout int) (*pxapi.Client, error) {
182+
func getClient(pm_api_url string, pm_user string, pm_password string, pm_api_token_id string, pm_api_token_secret string, pm_otp string, pm_tls_insecure bool, pm_timeout int) (*pxapi.Client, error) {
157183
tlsconf := &tls.Config{InsecureSkipVerify: true}
158184
if !pm_tls_insecure {
159185
tlsconf = nil
160186
}
187+
188+
var err error
189+
190+
if pm_password != "" && pm_api_token_secret != "" {
191+
err = fmt.Errorf("Password and API token secret both exist, choose one or the other.")
192+
}
193+
194+
if pm_password == "" && pm_api_token_secret == "" {
195+
err = fmt.Errorf("Password and API token do not exist, one of these must exist.")
196+
}
197+
198+
if strings.Contains(pm_user, "!") && pm_password != "" {
199+
err = fmt.Errorf("You appear to be using an API TokenID username with your password.")
200+
}
201+
202+
if !strings.Contains(pm_api_token_id, "!") {
203+
err = fmt.Errorf("Your API TokenID username should contain a !, check your API credentials.")
204+
}
205+
161206
client, _ := pxapi.NewClient(pm_api_url, nil, tlsconf, pm_timeout)
162-
err := client.Login(pm_user, pm_password, pm_otp)
207+
208+
// User+Pass authentication
209+
if pm_user != "" && pm_password != "" {
210+
err = client.Login(pm_user, pm_password, pm_otp)
211+
}
212+
213+
// API authentication
214+
if pm_api_token_id != "" && pm_api_token_secret != "" {
215+
// Unsure how to get an err for this
216+
client.SetAPIToken( pm_api_token_id, pm_api_token_secret)
217+
}
218+
163219
if err != nil {
164220
return nil, err
165221
}
@@ -195,6 +251,7 @@ func (lock *pmApiLockHolder) lock() {
195251
pconf.CurrentParallel++
196252
pconf.Mutex.Unlock()
197253
}
254+
198255
func (lock *pmApiLockHolder) unlock() {
199256
if !lock.locked {
200257
return

proxmox/resource_vm_qemu.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"time"
1212

1313
pxapi "github.com/Telmate/proxmox-api-go/proxmox"
14-
//pxapi "github.com/doransmestad/proxmox-api-go/proxmox"
1514
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1615
)
1716

proxmox/resource_vm_qemu_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,15 +300,15 @@ func TestAccProxmoxVmQemu_CreateCloneWithTwoDisks(t *testing.T) {
300300
}
301301

302302
// TestAccProxmoxVmQemu_StandardUpdateNoReboot tests a simple update of a vm_qemu resource,
303-
// and the modified parameters can be applied without reboot.
303+
// and the modified parameters can be applied without reboot.
304304
func TestAccProxmoxVmQemu_UpdateNoReboot(t *testing.T) {
305305
resourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
306306
resourcePath := fmt.Sprintf("proxmox_vm_qemu.%s", resourceName)
307307

308308
resource.Test(t, resource.TestCase{
309309
PreCheck: func() { testAccPreCheck(t) },
310310
Providers: testAccProxmoxProviderFactory(),
311-
311+
312312
Steps: []resource.TestStep{
313313
{
314314
Config: testAccExampleQemuBasic(resourceName, testAccProxmoxTargetNode),
@@ -318,26 +318,26 @@ func TestAccProxmoxVmQemu_UpdateNoReboot(t *testing.T) {
318318
},
319319
{
320320
// since we're just renaming there should be no reboot
321-
Config: strings.Replace(testAccExampleQemuBasic(resourceName, testAccProxmoxTargetNode),
322-
"name = \"" + resourceName + "\"", "name = \"" + resourceName + "-renamed\"", 1),
321+
Config: strings.Replace(testAccExampleQemuBasic(resourceName, testAccProxmoxTargetNode),
322+
"name = \""+resourceName+"\"", "name = \""+resourceName+"-renamed\"", 1),
323323
Check: resource.ComposeTestCheckFunc(
324-
resource.TestCheckResourceAttr(resourcePath, "name", resourceName + "-renamed"),
324+
resource.TestCheckResourceAttr(resourcePath, "name", resourceName+"-renamed"),
325325
),
326326
},
327327
},
328328
})
329329
}
330330

331331
// TestAccProxmoxVmQemu_UpdateRebootRequired tests a simple update of a vm_qemu resource,
332-
// and the modified parameters can be only applied with reboot.
332+
// and the modified parameters can be only applied with reboot.
333333
func TestAccProxmoxVmQemu_UpdateRebootRequired(t *testing.T) {
334334
resourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
335335
resourcePath := fmt.Sprintf("proxmox_vm_qemu.%s", resourceName)
336336

337337
resource.Test(t, resource.TestCase{
338338
PreCheck: func() { testAccPreCheck(t) },
339339
Providers: testAccProxmoxProviderFactory(),
340-
340+
341341
Steps: []resource.TestStep{
342342
{
343343
Config: testAccExampleQemuBasic(resourceName, testAccProxmoxTargetNode),
@@ -354,4 +354,4 @@ func TestAccProxmoxVmQemu_UpdateRebootRequired(t *testing.T) {
354354
},
355355
},
356356
})
357-
}
357+
}

0 commit comments

Comments
 (0)