forked from taiidani/terraform-provider-jenkins
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
jenkins_credential_vault_approle (taiidani#25)
* Add jenkins_credential_vault_approle * Update docs/resources/credential_vault_approle.md Co-authored-by: Ryan Nixon <r.taiidani@gmail.com>
- Loading branch information
Showing
5 changed files
with
426 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# jenkins_credential_vault_approle Resource | ||
|
||
Manages a Vault AppRole credential within Jenkins. This credential may then be referenced within jobs that are created. | ||
|
||
~> The "secret_id" property may leave plain-text secret id in your state file. If using the property to manage the secret id in Terraform, ensure that your state file is properly secured and encrypted at rest. | ||
|
||
~> When using this resource within a folder context it can conflict with the [folder resource](folder) template. When using these in combination you may need to add a lifecycle `ignore_changes` rule to the folder's `template` property. | ||
~> The Jenkins installation that uses this resource is expected to have the [Hashicorp Vault Plugin](https://plugins.jenkins.io/hashicorp-vault-plugin/) installed in their system. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource jenkins_credential_vault_approle example { | ||
name = "example-approle" | ||
role_id = "example" | ||
secret_id = "super-secret" | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `name` - (Required) The name of the credentials being created. This maps to the ID property within Jenkins, and cannot be changed once set. | ||
* `domain` - (Optional) The domain store to place the credentials into. If not set will default to the global credentials store. | ||
* `folder` - (Optional) The folder namespace to store the credentials in. If not set will default to global Jenkins credentials. | ||
* `scope` - (Optional) The visibility of the credentials to Jenkins agents. This must be set to either "GLOBAL" or "SYSTEM". If not set will default to "GLOBAL". | ||
* `description` - (Optional) A human readable description of the credentials being stored. | ||
* `path` - (Optional) The unique name of the approle auth backend. Defaults to `approle`. | ||
* `role_id` - (Required) The role_id to be associated with the credentials. | ||
* `secret_id` - (Optional) The secret_id to be associated with the credentials. If empty then the secret_id property will become unmanaged and expected to be set manually within Jenkins. If set then the secret_id will be updated only upon changes -- if the secret_id is set manually within Jenkins then it will not reconcile this drift until the next time the secret_id property is changed. | ||
|
||
## Attribute Reference | ||
|
||
All arguments above are exported. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
FROM jenkins/jenkins:lts | ||
|
||
RUN /usr/local/bin/install-plugins.sh cloudbees-folder pipeline-model-definition git | ||
RUN /usr/local/bin/install-plugins.sh hashicorp-vault-plugin cloudbees-folder pipeline-model-definition git | ||
|
||
HEALTHCHECK --interval=4s --start-period=5s --retries=30 CMD [ "curl", "-f", "http://localhost:8080" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
package jenkins | ||
|
||
import ( | ||
"context" | ||
"encoding/xml" | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
) | ||
|
||
// VaultAppRoleCredentials struct representing credential for storing Vault AppRole role id and secret id | ||
type VaultAppRoleCredentials struct { | ||
XMLName xml.Name `xml:"com.datapipe.jenkins.vault.credentials.VaultAppRoleCredential"` | ||
ID string `xml:"id"` | ||
Scope string `xml:"scope"` | ||
Description string `xml:"description"` | ||
Path string `xml:"path"` | ||
RoleID string `xml:"roleId"` | ||
SecretID string `xml:"secretId"` | ||
} | ||
|
||
func resourceJenkinsCredentialVaultAppRole() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateContext: resourceJenkinsCredentialVaultAppRoleCreate, | ||
ReadContext: resourceJenkinsCredentialVaultAppRoleRead, | ||
UpdateContext: resourceJenkinsCredentialVaultAppRoleUpdate, | ||
DeleteContext: resourceJenkinsCredentialVaultAppRoleDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: resourceJenkinsCredentialVaultAppRoleImport, | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Description: "The identifier assigned to the credentials.", | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"domain": { | ||
Type: schema.TypeString, | ||
Description: "The domain namespace that the credentials will be added to.", | ||
Optional: true, | ||
Default: "_", | ||
// In-place updates should be possible, but gojenkins does not support move operations | ||
ForceNew: true, | ||
}, | ||
"folder": { | ||
Type: schema.TypeString, | ||
Description: "The folder namespace that the credentials will be added to.", | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"scope": { | ||
Type: schema.TypeString, | ||
Description: "The Jenkins scope assigned to the credentials.", | ||
Optional: true, | ||
Default: "GLOBAL", | ||
ValidateDiagFunc: validateCredentialScope, | ||
}, | ||
"description": { | ||
Type: schema.TypeString, | ||
Description: "The credentials descriptive text.", | ||
Optional: true, | ||
Default: "Managed by Terraform", | ||
}, | ||
"path": { | ||
Type: schema.TypeString, | ||
Description: "Path of the roles approle backend.", | ||
Optional: true, | ||
Default: "approle", | ||
}, | ||
"role_id": { | ||
Type: schema.TypeString, | ||
Description: "The roles role_id.", | ||
Required: true, | ||
}, | ||
"secret_id": { | ||
Type: schema.TypeString, | ||
Description: "The roles secret_id. If left empty will be unmanaged.", | ||
Optional: true, | ||
Sensitive: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceJenkinsCredentialVaultAppRoleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(jenkinsClient) | ||
cm := client.Credentials() | ||
cm.Folder = formatFolderName(d.Get("folder").(string)) | ||
// return diag.FromErr(fmt.Errorf("invalid folder name '%s', '%s'", cm.Folder, d.Get("folder").(string))) | ||
// Validate that the folder exists | ||
if err := folderExists(client, cm.Folder); err != nil { | ||
return diag.FromErr(fmt.Errorf("invalid folder name '%s' specified: %w", cm.Folder, err)) | ||
} | ||
|
||
cred := VaultAppRoleCredentials{ | ||
ID: d.Get("name").(string), | ||
Scope: d.Get("scope").(string), | ||
Description: d.Get("description").(string), | ||
Path: d.Get("path").(string), | ||
RoleID: d.Get("role_id").(string), | ||
SecretID: d.Get("secret_id").(string), | ||
} | ||
|
||
domain := d.Get("domain").(string) | ||
err := cm.Add(domain, cred) | ||
if err != nil { | ||
return diag.Errorf("Could not create vault approle credentials: %s", err) | ||
} | ||
|
||
d.SetId(generateCredentialID(d.Get("folder").(string), cred.ID)) | ||
return resourceJenkinsCredentialVaultAppRoleRead(ctx, d, meta) | ||
} | ||
|
||
func resourceJenkinsCredentialVaultAppRoleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
cm := meta.(jenkinsClient).Credentials() | ||
cm.Folder = formatFolderName(d.Get("folder").(string)) | ||
|
||
cred := VaultAppRoleCredentials{} | ||
err := cm.GetSingle( | ||
d.Get("domain").(string), | ||
d.Get("name").(string), | ||
&cred, | ||
) | ||
|
||
if err != nil { | ||
if strings.HasSuffix(err.Error(), "404") { | ||
// Job does not exist | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return diag.Errorf("Could not read vault approle credentials: %s", err) | ||
} | ||
|
||
d.SetId(generateCredentialID(d.Get("folder").(string), cred.ID)) | ||
d.Set("scope", cred.Scope) | ||
d.Set("description", cred.Description) | ||
d.Set("path", cred.Path) | ||
d.Set("role_id", cred.RoleID) | ||
// NOTE: We are NOT setting the password here, as the password returned by GetSingle is garbage | ||
// Password only applies to Create/Update operations if the "password" property is non-empty | ||
|
||
return nil | ||
} | ||
|
||
func resourceJenkinsCredentialVaultAppRoleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
cm := meta.(jenkinsClient).Credentials() | ||
cm.Folder = formatFolderName(d.Get("folder").(string)) | ||
|
||
domain := d.Get("domain").(string) | ||
cred := VaultAppRoleCredentials{ | ||
ID: d.Get("name").(string), | ||
Scope: d.Get("scope").(string), | ||
Description: d.Get("description").(string), | ||
Path: d.Get("path").(string), | ||
RoleID: d.Get("role_id").(string), | ||
} | ||
|
||
// Only enforce the password if it is non-empty | ||
if d.Get("secret_id").(string) != "" { | ||
cred.SecretID = d.Get("secret_id").(string) | ||
} | ||
|
||
err := cm.Update(domain, d.Get("name").(string), &cred) | ||
if err != nil { | ||
return diag.Errorf("Could not update vault approle credentials: %s", err) | ||
} | ||
|
||
d.SetId(generateCredentialID(d.Get("folder").(string), cred.ID)) | ||
return resourceJenkinsCredentialVaultAppRoleRead(ctx, d, meta) | ||
} | ||
|
||
func resourceJenkinsCredentialVaultAppRoleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
cm := meta.(jenkinsClient).Credentials() | ||
cm.Folder = formatFolderName(d.Get("folder").(string)) | ||
|
||
err := cm.Delete( | ||
d.Get("domain").(string), | ||
d.Get("name").(string), | ||
) | ||
if err != nil { | ||
return diag.FromErr(err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceJenkinsCredentialVaultAppRoleImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) { | ||
ret := []*schema.ResourceData{d} | ||
|
||
splitID := strings.Split(d.Id(), "/") | ||
if len(splitID) < 2 { | ||
return ret, fmt.Errorf("import ID was improperly formatted. Imports need to be in the format \"[<folder>/]<domain>/<name>\"") | ||
} | ||
|
||
name := splitID[len(splitID)-1] | ||
d.Set("name", name) | ||
|
||
domain := splitID[len(splitID)-2] | ||
d.Set("domain", domain) | ||
|
||
folder := strings.Trim(strings.Join(splitID[0:len(splitID)-2], "/"), "/") | ||
d.Set("folder", folder) | ||
|
||
d.SetId(generateCredentialID(folder, name)) | ||
return ret, nil | ||
} |
Oops, something went wrong.