mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-08-22 19:38:35 +00:00
feat(provider): add support for private key authentication for SSH (#1076)
* feat(provider): add support for private key authentication for SSH Also fix bunch of issues with acceptance tests --------- Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
66ec9f4b9b
commit
2c6d3ad01d
27
.github/workflows/test.yml
vendored
27
.github/workflows/test.yml
vendored
@ -5,7 +5,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- "release/**"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -30,9 +29,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
tools/go.sum
|
||||
cache-dependency-path: "**/*.sum"
|
||||
|
||||
- name: Get dependencies
|
||||
if: steps.filter.outputs.go == 'true'
|
||||
@ -63,9 +60,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
tools/go.sum
|
||||
cache-dependency-path: "**/*.sum"
|
||||
|
||||
- name: Get dependencies
|
||||
if: steps.filter.outputs.go == 'true'
|
||||
@ -88,9 +83,6 @@ jobs:
|
||||
terraform: [ 1.6 ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
environment: pve-acc
|
||||
concurrency:
|
||||
group: acceptance
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@ -101,18 +93,11 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version-file: "go.mod"
|
||||
cache-dependency-path: |
|
||||
go.sum
|
||||
tools/go.sum
|
||||
cache-dependency-path: "**/*.sum"
|
||||
|
||||
- name: Get dependencies
|
||||
run: go mod download
|
||||
|
||||
- name: Setup ssh-agent
|
||||
uses: webfactory/ssh-agent@v0.9.0
|
||||
with:
|
||||
ssh-private-key: ${{ secrets.PROXMOX_VE_SSH_USER_PRIVATE_KEY }}
|
||||
|
||||
- uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v3.0.0
|
||||
with:
|
||||
terraform_version: ${{ matrix.terraform }}.*
|
||||
@ -122,9 +107,11 @@ jobs:
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
TF_ACC: 1
|
||||
PROXMOX_VE_INSECURE: false
|
||||
PROXMOX_VE_API_TOKEN: "${{ secrets.PROXMOX_VE_API_TOKEN }}"
|
||||
PROXMOX_VE_ENDPOINT: "https://${{ secrets.PROXMOX_VE_HOST }}:8006/"
|
||||
PROXMOX_VE_SSH_AGENT: false
|
||||
PROXMOX_VE_SSH_USERNAME: "terraform"
|
||||
PROXMOX_VE_SSH_AGENT: true
|
||||
PROXMOX_VE_INSECURE: false
|
||||
PROXMOX_VE_SSH_PRIVATE_KEY: "${{ secrets.PROXMOX_VE_SSH_PRIVATE_KEY }}"
|
||||
run: make testacc
|
||||
|
||||
|
@ -114,6 +114,41 @@ The provider does not use OS-specific SSH configuration files, such as `~/.ssh/c
|
||||
Instead, it uses the SSH protocol directly, and supports the `SSH_AUTH_SOCK` environment variable (or `agent_socket` argument) to connect to the `ssh-agent`.
|
||||
This allows the provider to use the SSH agent configured by the user, and to support multiple SSH agents running on the same machine.
|
||||
You can find more details on the SSH Agent [here](https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys#adding-your-ssh-keys-to-an-ssh-agent-to-avoid-typing-the-passphrase).
|
||||
The SSH agent authentication takes precedence over the `private_key` and `password` authentication.
|
||||
|
||||
### SSH Private Key
|
||||
|
||||
In some cases where SSH agent is not available, for example when running Terraform from a Windows machine, or when using a CI/CD pipeline that does not support SSH agent forwarding,
|
||||
you can use the `private_key` argument in the `ssh` block (or alternatively `PROXMOX_VE_SSH_PRIVATE_KEY` environment variable) to provide the private key for the SSH connection.
|
||||
|
||||
The private key must be in PEM format, and can be loaded from a file:
|
||||
|
||||
```terraform
|
||||
provider "proxmox" {
|
||||
...
|
||||
|
||||
ssh {
|
||||
agent = false
|
||||
private_key = file("~/.ssh/id_rsa")
|
||||
}
|
||||
}
|
||||
```
|
||||
Not recommended, but you can also use a heredoc syntax to provide the private key as a string (note that the private key content must not be indented):
|
||||
```terraform
|
||||
provider "proxmox" {
|
||||
...
|
||||
|
||||
ssh {
|
||||
agent = false
|
||||
private_key = <<EOF
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
<SKIPPED>
|
||||
DMUWUEaH7yMCKl7uCZ9xAAAAAAECAwQF
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### SSH User
|
||||
|
||||
@ -317,6 +352,7 @@ In addition to [generic provider arguments](https://www.terraform.io/docs/config
|
||||
- `password` - (Optional) The password to use for the SSH connection. Defaults to the password used for the Proxmox API connection. Can also be sourced from `PROXMOX_VE_SSH_PASSWORD`.
|
||||
- `agent` - (Optional) Whether to use the SSH agent for the SSH authentication. Defaults to `false`. Can also be sourced from `PROXMOX_VE_SSH_AGENT`.
|
||||
- `agent_socket` - (Optional) The path to the SSH agent socket. Defaults to the value of the `SSH_AUTH_SOCK` environment variable. Can also be sourced from `PROXMOX_VE_SSH_AUTH_SOCK`.
|
||||
- `private_key` - (Optional) The private key to use for the SSH connection. Can also be sourced from `PROXMOX_VE_SSH_PRIVATE_KEY`. The private key must be in PEM format.
|
||||
- `socks5_server` - (Optional) The address of the SOCKS5 proxy server to use for the SSH connection. Can also be sourced from `PROXMOX_VE_SSH_SOCKS5_SERVER`.
|
||||
- `socks5_username` - (Optional) The username to use for the SOCKS5 proxy server. Can also be sourced from `PROXMOX_VE_SSH_SOCKS5_USERNAME`.
|
||||
- `socks5_password` - (Optional) The password to use for the SOCKS5 proxy server. Can also be sourced from `PROXMOX_VE_SSH_SOCKS5_PASSWORD`.
|
||||
|
@ -63,6 +63,7 @@ type proxmoxProviderModel struct {
|
||||
SSH []struct {
|
||||
Agent types.Bool `tfsdk:"agent"`
|
||||
AgentSocket types.String `tfsdk:"agent_socket"`
|
||||
PrivateKey types.String `tfsdk:"private_key"`
|
||||
Password types.String `tfsdk:"password"`
|
||||
Username types.String `tfsdk:"username"`
|
||||
Socks5Server types.String `tfsdk:"socks5_server"`
|
||||
@ -145,8 +146,9 @@ func (p *proxmoxProvider) Schema(_ context.Context, _ provider.SchemaRequest, re
|
||||
NestedObject: schema.NestedBlockObject{
|
||||
Attributes: map[string]schema.Attribute{
|
||||
"agent": schema.BoolAttribute{
|
||||
Description: "Whether to use the SSH agent for authentication. " +
|
||||
"Defaults to `false`.",
|
||||
Description: "Whether to use the SSH agent for authentication. Takes precedence over " +
|
||||
"the `private_key` and `password` fields. Defaults to the value of the " +
|
||||
"`PROXMOX_VE_SSH_AGENT` environment variable, or `false` if not set.",
|
||||
Optional: true,
|
||||
},
|
||||
"agent_socket": schema.StringAttribute{
|
||||
@ -155,6 +157,12 @@ func (p *proxmoxProvider) Schema(_ context.Context, _ provider.SchemaRequest, re
|
||||
"environment variable.",
|
||||
Optional: true,
|
||||
},
|
||||
"private_key": schema.StringAttribute{
|
||||
Description: "The unencrypted private key (in PEM format) used for the SSH connection. " +
|
||||
"Defaults to the value of the `PROXMOX_VE_SSH_PRIVATE_KEY` environment variable.",
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
},
|
||||
"password": schema.StringAttribute{
|
||||
Description: "The password used for the SSH connection. " +
|
||||
"Defaults to the value of the `password` field of the " +
|
||||
@ -332,6 +340,7 @@ func (p *proxmoxProvider) Configure(
|
||||
sshUsername := utils.GetAnyStringEnv("PROXMOX_VE_SSH_USERNAME")
|
||||
sshPassword := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PASSWORD")
|
||||
sshAgent := utils.GetAnyBoolEnv("PROXMOX_VE_SSH_AGENT")
|
||||
sshPrivateKey := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PRIVATE_KEY")
|
||||
sshAgentSocket := utils.GetAnyStringEnv("SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK")
|
||||
sshSocks5Server := utils.GetAnyStringEnv("PROXMOX_VE_SSH_SOCKS5_SERVER")
|
||||
sshSocks5Username := utils.GetAnyStringEnv("PROXMOX_VE_SSH_SOCKS5_USERNAME")
|
||||
@ -356,6 +365,10 @@ func (p *proxmoxProvider) Configure(
|
||||
sshAgentSocket = config.SSH[0].AgentSocket.ValueString()
|
||||
}
|
||||
|
||||
if !config.SSH[0].PrivateKey.IsNull() {
|
||||
sshPrivateKey = config.SSH[0].PrivateKey.ValueString()
|
||||
}
|
||||
|
||||
if !config.SSH[0].Socks5Server.IsNull() {
|
||||
sshSocks5Server = config.SSH[0].Socks5Server.ValueString()
|
||||
}
|
||||
@ -390,7 +403,7 @@ func (p *proxmoxProvider) Configure(
|
||||
}
|
||||
|
||||
sshClient, err := ssh.NewClient(
|
||||
sshUsername, sshPassword, sshAgent, sshAgentSocket,
|
||||
sshUsername, sshPassword, sshAgent, sshAgentSocket, sshPrivateKey,
|
||||
sshSocks5Server, sshSocks5Username, sshSocks5Password,
|
||||
&apiResolverWithOverrides{
|
||||
ar: apiResolver{c: apiClient},
|
||||
|
@ -9,6 +9,7 @@ package tests
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
|
||||
@ -21,6 +22,12 @@ const (
|
||||
accTestContainerCloneName = "proxmox_virtual_environment_container.test_container_clone"
|
||||
)
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
var (
|
||||
accTestContainerID = 100000 + rand.Intn(99999) //nolint:gosec
|
||||
accCloneContainerID = 200000 + rand.Intn(99999) //nolint:gosec
|
||||
)
|
||||
|
||||
func TestAccResourceContainer(t *testing.T) {
|
||||
accProviders := testAccMuxProviders(context.Background(), t)
|
||||
|
||||
@ -50,7 +57,7 @@ resource "proxmox_virtual_environment_download_file" "ubuntu_container_template"
|
||||
}
|
||||
resource "proxmox_virtual_environment_container" "test_container" {
|
||||
node_name = "%s"
|
||||
vm_id = 1100
|
||||
vm_id = %d
|
||||
template = %t
|
||||
|
||||
disk {
|
||||
@ -83,7 +90,7 @@ resource "proxmox_virtual_environment_container" "test_container" {
|
||||
type = "ubuntu"
|
||||
}
|
||||
}
|
||||
`, accTestNodeName, isTemplate)
|
||||
`, accTestNodeName, accTestContainerID, isTemplate)
|
||||
}
|
||||
|
||||
func testAccResourceContainerCreateCheck(t *testing.T) resource.TestCheckFunc {
|
||||
@ -92,8 +99,9 @@ func testAccResourceContainerCreateCheck(t *testing.T) resource.TestCheckFunc {
|
||||
return resource.ComposeTestCheckFunc(
|
||||
resource.TestCheckResourceAttr(accTestContainerName, "description", "my\ndescription\nvalue\n"),
|
||||
func(*terraform.State) error {
|
||||
err := getNodesClient().Container(1100).WaitForContainerStatus(context.Background(), "running", 10, 1)
|
||||
err := getNodesClient().Container(accTestContainerID).WaitForContainerStatus(context.Background(), "running", 10, 1)
|
||||
require.NoError(t, err, "container did not start")
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
@ -105,17 +113,17 @@ resource "proxmox_virtual_environment_container" "test_container_clone" {
|
||||
depends_on = [proxmox_virtual_environment_container.test_container]
|
||||
|
||||
node_name = "%s"
|
||||
vm_id = 1101
|
||||
vm_id = %d
|
||||
|
||||
clone {
|
||||
vm_id = 1100
|
||||
vm_id = proxmox_virtual_environment_container.test_container.id
|
||||
}
|
||||
|
||||
initialization {
|
||||
hostname = "test-clone"
|
||||
}
|
||||
}
|
||||
`, accTestNodeName)
|
||||
`, accTestNodeName, accCloneContainerID)
|
||||
}
|
||||
|
||||
func testAccResourceContainerCreateCloneCheck(t *testing.T) resource.TestCheckFunc {
|
||||
@ -123,8 +131,9 @@ func testAccResourceContainerCreateCloneCheck(t *testing.T) resource.TestCheckFu
|
||||
|
||||
return resource.ComposeTestCheckFunc(
|
||||
func(*terraform.State) error {
|
||||
err := getNodesClient().Container(1101).WaitForContainerStatus(context.Background(), "running", 10, 1)
|
||||
err := getNodesClient().Container(accCloneContainerID).WaitForContainerStatus(context.Background(), "running", 10, 1)
|
||||
require.NoError(t, err, "container did not start")
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
@ -127,9 +127,9 @@ func uploadSnippetFile(t *testing.T, file *os.File) {
|
||||
|
||||
sshUsername := utils.GetAnyStringEnv("PROXMOX_VE_SSH_USERNAME")
|
||||
sshAgentSocket := utils.GetAnyStringEnv("SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK", "PM_VE_SSH_AUTH_SOCK")
|
||||
|
||||
sshPrivateKey := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PRIVATE_KEY")
|
||||
sshClient, err := ssh.NewClient(
|
||||
sshUsername, "", true, sshAgentSocket,
|
||||
sshUsername, "", true, sshAgentSocket, sshPrivateKey,
|
||||
"", "", "",
|
||||
&nodeResolver{
|
||||
node: ssh.ProxmoxNode{
|
||||
|
@ -58,6 +58,7 @@ type client struct {
|
||||
password string
|
||||
agent bool
|
||||
agentSocket string
|
||||
privateKey string
|
||||
socks5Server string
|
||||
socks5Username string
|
||||
socks5Password string
|
||||
@ -68,6 +69,7 @@ type client struct {
|
||||
func NewClient(
|
||||
username string, password string,
|
||||
agent bool, agentSocket string,
|
||||
privateKey string,
|
||||
socks5Server string, socks5Username string, socks5Password string,
|
||||
nodeResolver NodeResolver,
|
||||
) (Client, error) {
|
||||
@ -91,6 +93,7 @@ func NewClient(
|
||||
password: password,
|
||||
agent: agent,
|
||||
agentSocket: agentSocket,
|
||||
privateKey: privateKey,
|
||||
socks5Server: socks5Server,
|
||||
socks5Username: socks5Username,
|
||||
socks5Password: socks5Password,
|
||||
@ -309,12 +312,26 @@ func (c *client) openNodeShell(ctx context.Context, node ProxmoxNode) (*ssh.Clie
|
||||
return sshClient, nil
|
||||
}
|
||||
|
||||
tflog.Error(ctx, "Failed ssh connection through agent, falling back to password authentication",
|
||||
tflog.Error(ctx, "Failed SSH connection through agent",
|
||||
map[string]interface{}{
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
|
||||
if c.privateKey != "" {
|
||||
sshClient, err = c.createSSHClientWithPrivateKey(ctx, cb, kh, sshHost)
|
||||
if err == nil {
|
||||
return sshClient, nil
|
||||
}
|
||||
|
||||
tflog.Error(ctx, "Failed SSH connection with private key",
|
||||
map[string]interface{}{
|
||||
"error": err,
|
||||
})
|
||||
}
|
||||
|
||||
tflog.Info(ctx, "Falling back to password authentication for SSH connection")
|
||||
|
||||
sshClient, err = c.createSSHClient(ctx, cb, kh, sshHost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to authenticate user %q over SSH to %q. Please verify that ssh-agent is "+
|
||||
@ -374,6 +391,27 @@ func (c *client) createSSHClientAgent(
|
||||
return c.connect(ctx, sshHost, sshConfig)
|
||||
}
|
||||
|
||||
func (c *client) createSSHClientWithPrivateKey(
|
||||
ctx context.Context,
|
||||
cb ssh.HostKeyCallback,
|
||||
kh knownhosts.HostKeyCallback,
|
||||
sshHost string,
|
||||
) (*ssh.Client, error) {
|
||||
privateKey, err := ssh.ParsePrivateKey([]byte(c.privateKey))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: c.username,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(privateKey)},
|
||||
HostKeyCallback: cb,
|
||||
HostKeyAlgorithms: kh.HostKeyAlgorithms(sshHost),
|
||||
}
|
||||
|
||||
return c.connect(ctx, sshHost, sshConfig)
|
||||
}
|
||||
|
||||
func (c *client) connect(ctx context.Context, sshHost string, sshConfig *ssh.ClientConfig) (*ssh.Client, error) {
|
||||
if c.socks5Server != "" {
|
||||
sshClient, err := c.socks5SSHClient(sshHost, sshConfig)
|
||||
|
@ -111,6 +111,7 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
|
||||
sshPassword := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PASSWORD", "PM_VE_SSH_PASSWORD")
|
||||
sshAgent := utils.GetAnyBoolEnv("PROXMOX_VE_SSH_AGENT", "PM_VE_SSH_AGENT")
|
||||
sshAgentSocket := utils.GetAnyStringEnv("SSH_AUTH_SOCK", "PROXMOX_VE_SSH_AUTH_SOCK", "PM_VE_SSH_AUTH_SOCK")
|
||||
sshPrivateKey := utils.GetAnyStringEnv("PROXMOX_VE_SSH_PRIVATE_KEY")
|
||||
sshSocks5Server := utils.GetAnyStringEnv("PROXMOX_VE_SSH_SOCKS5_SERVER")
|
||||
sshSocks5Username := utils.GetAnyStringEnv("PROXMOX_VE_SSH_SOCKS5_USERNAME")
|
||||
sshSocks5Password := utils.GetAnyStringEnv("PROXMOX_VE_SSH_SOCKS5_PASSWORD")
|
||||
@ -135,19 +136,23 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
|
||||
sshConf[mkProviderSSHAgent] = sshAgent
|
||||
}
|
||||
|
||||
if _, ok := sshConf[mkProviderSSHAgentSocket]; !ok {
|
||||
if v, ok := sshConf[mkProviderSSHAgentSocket]; !ok || v.(string) == "" {
|
||||
sshConf[mkProviderSSHAgentSocket] = sshAgentSocket
|
||||
}
|
||||
|
||||
if _, ok := sshConf[mkProviderSSHSocks5Server]; !ok {
|
||||
if v, ok := sshConf[mkProviderSSHPrivateKey]; !ok || v.(string) == "" {
|
||||
sshConf[mkProviderSSHPrivateKey] = sshPrivateKey
|
||||
}
|
||||
|
||||
if v, ok := sshConf[mkProviderSSHSocks5Server]; !ok || v.(string) == "" {
|
||||
sshConf[mkProviderSSHSocks5Server] = sshSocks5Server
|
||||
}
|
||||
|
||||
if _, ok := sshConf[mkProviderSSHSocks5Username]; !ok {
|
||||
if v, ok := sshConf[mkProviderSSHSocks5Username]; !ok || v.(string) == "" {
|
||||
sshConf[mkProviderSSHSocks5Username] = sshSocks5Username
|
||||
}
|
||||
|
||||
if _, ok := sshConf[mkProviderSSHSocks5Password]; !ok {
|
||||
if v, ok := sshConf[mkProviderSSHSocks5Password]; !ok || v.(string) == "" {
|
||||
sshConf[mkProviderSSHSocks5Password] = sshSocks5Password
|
||||
}
|
||||
|
||||
@ -168,6 +173,7 @@ func providerConfigure(_ context.Context, d *schema.ResourceData) (interface{},
|
||||
sshConf[mkProviderSSHPassword].(string),
|
||||
sshConf[mkProviderSSHAgent].(bool),
|
||||
sshConf[mkProviderSSHAgentSocket].(string),
|
||||
sshConf[mkProviderSSHPrivateKey].(string),
|
||||
sshConf[mkProviderSSHSocks5Server].(string),
|
||||
sshConf[mkProviderSSHSocks5Username].(string),
|
||||
sshConf[mkProviderSSHSocks5Password].(string),
|
||||
|
@ -29,6 +29,7 @@ const (
|
||||
mkProviderSSHPassword = "password"
|
||||
mkProviderSSHAgent = "agent"
|
||||
mkProviderSSHAgentSocket = "agent_socket"
|
||||
mkProviderSSHPrivateKey = "private_key"
|
||||
mkProviderSSHSocks5Server = "socks5_server"
|
||||
mkProviderSSHSocks5Username = "socks5_username"
|
||||
mkProviderSSHSocks5Password = "socks5_password"
|
||||
@ -123,8 +124,9 @@ func createSchema() map[string]*schema.Schema {
|
||||
mkProviderSSHAgent: {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Description: "Whether to use the SSH agent for authentication. " +
|
||||
"Defaults to `false`.",
|
||||
Description: "Whether to use the SSH agent for authentication. Takes precedence over " +
|
||||
"the `private_key` and `password` fields. Defaults to the value of the " +
|
||||
"`PROXMOX_VE_SSH_AGENT` environment variable, or `false` if not set.",
|
||||
DefaultFunc: func() (interface{}, error) {
|
||||
for _, k := range []string{"PROXMOX_VE_SSH_AGENT", "PM_VE_SSH_AGENT"} {
|
||||
v := os.Getenv(k)
|
||||
@ -148,6 +150,13 @@ func createSchema() map[string]*schema.Schema {
|
||||
),
|
||||
ValidateFunc: validation.StringIsNotEmpty,
|
||||
},
|
||||
mkProviderSSHPrivateKey: {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Sensitive: true,
|
||||
Description: "The unencrypted private key (in PEM format) used for the SSH connection. " +
|
||||
"Defaults to the value of the `PROXMOX_VE_SSH_PRIVATE_KEY` environment variable.",
|
||||
},
|
||||
mkProviderSSHSocks5Server: {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
Loading…
Reference in New Issue
Block a user