Skip to content

Commit df7190c

Browse files
authored
azurerm_mssql_job_credential - add password_wo, password_wo_version and split create/update (#28808)
* add write-only password attr and split create/update * add note to docs
1 parent 34ba02d commit df7190c

File tree

3 files changed

+204
-66
lines changed

3 files changed

+204
-66
lines changed

internal/services/mssql/mssql_job_credential_resource.go

Lines changed: 104 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,25 @@ import (
88
"log"
99
"time"
1010

11+
"github.com/hashicorp/go-azure-helpers/lang/pointer"
1112
"github.com/hashicorp/go-azure-helpers/lang/response"
1213
"github.com/hashicorp/go-azure-sdk/resource-manager/sql/2023-08-01-preview/jobcredentials"
14+
"github.com/hashicorp/go-cty/cty"
15+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
16+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1317
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
1418
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
1519
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/parse"
1620
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/validate"
1721
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
1822
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
19-
"github.com/hashicorp/terraform-provider-azurerm/utils"
2023
)
2124

2225
func resourceMsSqlJobCredential() *pluginsdk.Resource {
2326
return &pluginsdk.Resource{
24-
Create: resourceMsSqlJobCredentialCreateUpdate,
27+
Create: resourceMsSqlJobCredentialCreate,
2528
Read: resourceMsSqlJobCredentialRead,
26-
Update: resourceMsSqlJobCredentialCreateUpdate,
29+
Update: resourceMsSqlJobCredentialUpdate,
2730
Delete: resourceMsSqlJobCredentialDelete,
2831

2932
Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error {
@@ -38,6 +41,10 @@ func resourceMsSqlJobCredential() *pluginsdk.Resource {
3841
Delete: pluginsdk.DefaultTimeout(60 * time.Minute),
3942
},
4043

44+
ValidateRawResourceConfigFuncs: []schema.ValidateRawResourceConfigFunc{
45+
validation.PreferWriteOnlyAttribute(cty.GetAttrPath("password"), cty.GetAttrPath("password_wo")),
46+
},
47+
4148
Schema: map[string]*pluginsdk.Schema{
4249
"name": {
4350
Type: pluginsdk.TypeString,
@@ -58,17 +65,32 @@ func resourceMsSqlJobCredential() *pluginsdk.Resource {
5865
},
5966

6067
"password": {
61-
Type: pluginsdk.TypeString,
62-
Required: true,
63-
Sensitive: true,
68+
Type: pluginsdk.TypeString,
69+
Optional: true,
70+
Sensitive: true,
71+
ConflictsWith: []string{"password_wo"},
72+
ExactlyOneOf: []string{"password", "password_wo"},
73+
},
74+
"password_wo": {
75+
Type: pluginsdk.TypeString,
76+
Optional: true,
77+
WriteOnly: true,
78+
RequiredWith: []string{"password_wo_version"},
79+
ConflictsWith: []string{"password"},
80+
ExactlyOneOf: []string{"password_wo", "password"},
81+
},
82+
"password_wo_version": {
83+
Type: pluginsdk.TypeInt,
84+
Optional: true,
85+
RequiredWith: []string{"password_wo"},
6486
},
6587
},
6688
}
6789
}
6890

69-
func resourceMsSqlJobCredentialCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
91+
func resourceMsSqlJobCredentialCreate(d *pluginsdk.ResourceData, meta interface{}) error {
7092
client := meta.(*clients.Client).MSSQL.JobCredentialsClient
71-
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
93+
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
7294
defer cancel()
7395

7496
log.Printf("[INFO] preparing arguments for Job Credential creation.")
@@ -79,39 +101,95 @@ func resourceMsSqlJobCredentialCreateUpdate(d *pluginsdk.ResourceData, meta inte
79101
}
80102
jobCredentialId := jobcredentials.NewCredentialID(jaId.SubscriptionId, jaId.ResourceGroupName, jaId.ServerName, jaId.JobAgentName, d.Get("name").(string))
81103

82-
username := d.Get("username").(string)
83-
password := d.Get("password").(string)
104+
existing, err := client.Get(ctx, jobCredentialId)
105+
if err != nil && !response.WasNotFound(existing.HttpResponse) {
106+
return fmt.Errorf("checking for presence of existing %s: %+v", jobCredentialId, err)
107+
}
84108

85-
if d.IsNewResource() {
86-
existing, err := client.Get(ctx, jobCredentialId)
87-
if err != nil {
88-
if !response.WasNotFound(existing.HttpResponse) {
89-
return fmt.Errorf("checking for presence of existing MsSql %s: %+v", jobCredentialId, err)
90-
}
91-
}
109+
if !response.WasNotFound(existing.HttpResponse) {
110+
return tf.ImportAsExistsError("azurerm_mssql_job_credential", jobCredentialId.ID())
111+
}
92112

93-
if !response.WasNotFound(existing.HttpResponse) {
94-
return tf.ImportAsExistsError("azurerm_mssql_job_credential", jobCredentialId.ID())
95-
}
113+
woPassword, err := pluginsdk.GetWriteOnly(d, "password_wo", cty.String)
114+
if err != nil {
115+
return err
116+
}
117+
118+
password := d.Get("password").(string)
119+
if !woPassword.IsNull() {
120+
password = woPassword.AsString()
96121
}
97122

98123
jobCredential := jobcredentials.JobCredential{
99-
Name: utils.String(jobCredentialId.CredentialName),
124+
Name: pointer.To(jobCredentialId.CredentialName),
100125
Properties: &jobcredentials.JobCredentialProperties{
101-
Username: username,
126+
Username: d.Get("username").(string),
102127
Password: password,
103128
},
104129
}
105130

106131
if _, err := client.CreateOrUpdate(ctx, jobCredentialId, jobCredential); err != nil {
107-
return fmt.Errorf("creating MsSql %s: %+v", jobCredentialId, err)
132+
return fmt.Errorf("creating %s: %+v", jobCredentialId, err)
108133
}
109134

110135
d.SetId(jobCredentialId.ID())
111136

112137
return resourceMsSqlJobCredentialRead(d, meta)
113138
}
114139

140+
func resourceMsSqlJobCredentialUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
141+
client := meta.(*clients.Client).MSSQL.JobCredentialsClient
142+
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d)
143+
defer cancel()
144+
145+
log.Printf("[INFO] preparing arguments for Job Credential update.")
146+
147+
jaId, err := jobcredentials.ParseJobAgentID(d.Get("job_agent_id").(string))
148+
if err != nil {
149+
return err
150+
}
151+
jobCredentialId := jobcredentials.NewCredentialID(jaId.SubscriptionId, jaId.ResourceGroupName, jaId.ServerName, jaId.JobAgentName, d.Get("name").(string))
152+
153+
existing, err := client.Get(ctx, jobCredentialId)
154+
if err != nil {
155+
return fmt.Errorf("retrieving %s: %+v", jobCredentialId, err)
156+
}
157+
158+
if existing.Model == nil {
159+
return fmt.Errorf("retrieving %s: `model` was nil", jobCredentialId)
160+
}
161+
162+
if existing.Model.Properties == nil {
163+
return fmt.Errorf("retrieving %s: `model.Properties` was nil", jobCredentialId)
164+
}
165+
payload := existing.Model
166+
167+
if d.HasChange("username") {
168+
payload.Properties.Username = d.Get("username").(string)
169+
}
170+
171+
if d.HasChange("password") {
172+
payload.Properties.Password = d.Get("password").(string)
173+
}
174+
175+
if d.HasChange("password_wo_version") {
176+
woPassword, err := pluginsdk.GetWriteOnly(d, "password_wo", cty.String)
177+
if err != nil {
178+
return err
179+
}
180+
181+
if !woPassword.IsNull() {
182+
payload.Properties.Password = woPassword.AsString()
183+
}
184+
}
185+
186+
if _, err := client.CreateOrUpdate(ctx, jobCredentialId, *payload); err != nil {
187+
return fmt.Errorf("updating %s: %+v", jobCredentialId, err)
188+
}
189+
190+
return resourceMsSqlJobCredentialRead(d, meta)
191+
}
192+
115193
func resourceMsSqlJobCredentialRead(d *pluginsdk.ResourceData, meta interface{}) error {
116194
client := meta.(*clients.Client).MSSQL.JobCredentialsClient
117195
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
@@ -140,6 +218,9 @@ func resourceMsSqlJobCredentialRead(d *pluginsdk.ResourceData, meta interface{})
140218
d.Set("username", props.Username)
141219
}
142220
}
221+
222+
d.Set("password_wo_version", d.Get("password_wo_version").(int))
223+
143224
return nil
144225
}
145226

internal/services/mssql/mssql_job_credential_resource_test.go

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@ import (
1111
"github.com/hashicorp/go-azure-helpers/lang/pointer"
1212
"github.com/hashicorp/go-azure-helpers/lang/response"
1313
"github.com/hashicorp/go-azure-sdk/resource-manager/sql/2023-08-01-preview/jobcredentials"
14+
"github.com/hashicorp/go-version"
15+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
16+
"github.com/hashicorp/terraform-plugin-testing/tfversion"
1417
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
1518
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
1619
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
20+
"github.com/hashicorp/terraform-provider-azurerm/internal/provider/framework"
1721
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
1822
)
1923

@@ -71,6 +75,59 @@ func TestAccMsSqlJobCredential_update(t *testing.T) {
7175
})
7276
}
7377

78+
func TestAccMsSqlJobCredential_writeOnlyPassword(t *testing.T) {
79+
data := acceptance.BuildTestData(t, "azurerm_mssql_job_credential", "test")
80+
r := MsSqlJobCredentialResource{}
81+
82+
resource.ParallelTest(t, resource.TestCase{
83+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
84+
tfversion.SkipBelow(version.Must(version.NewVersion("1.11.0"))),
85+
},
86+
ProtoV5ProviderFactories: framework.ProtoV5ProviderFactoriesInit(context.Background(), "azurerm"),
87+
Steps: []resource.TestStep{
88+
{
89+
Config: r.writeOnlyPassword(data, "secret", 1),
90+
Check: check.That(data.ResourceName).ExistsInAzure(r),
91+
},
92+
data.ImportStep("password_wo_version"),
93+
{
94+
Config: r.writeOnlyPassword(data, "secretUpdate", 2),
95+
Check: check.That(data.ResourceName).ExistsInAzure(r),
96+
},
97+
data.ImportStep("password_wo_version"),
98+
},
99+
})
100+
}
101+
102+
func TestAccMsSqlJobCredential_updateToWriteOnlyPassword(t *testing.T) {
103+
data := acceptance.BuildTestData(t, "azurerm_mssql_job_credential", "test")
104+
r := MsSqlJobCredentialResource{}
105+
106+
resource.ParallelTest(t, resource.TestCase{
107+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
108+
tfversion.SkipBelow(version.Must(version.NewVersion("1.11.0"))),
109+
},
110+
ProtoV5ProviderFactories: framework.ProtoV5ProviderFactoriesInit(context.Background(), "azurerm"),
111+
Steps: []resource.TestStep{
112+
{
113+
Config: r.basic(data),
114+
Check: check.That(data.ResourceName).ExistsInAzure(r),
115+
},
116+
data.ImportStep("password"),
117+
{
118+
Config: r.writeOnlyPassword(data, "secret", 1),
119+
Check: check.That(data.ResourceName).ExistsInAzure(r),
120+
},
121+
data.ImportStep("password", "password_wo_version"),
122+
{
123+
Config: r.basic(data),
124+
Check: check.That(data.ResourceName).ExistsInAzure(r),
125+
},
126+
data.ImportStep("password"),
127+
},
128+
})
129+
}
130+
74131
func (MsSqlJobCredentialResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
75132
id, err := jobcredentials.ParseCredentialID(state.ID)
76133
if err != nil {
@@ -88,46 +145,17 @@ func (MsSqlJobCredentialResource) Exists(ctx context.Context, client *clients.Cl
88145
return pointer.To(resp.Model != nil), nil
89146
}
90147

91-
func (MsSqlJobCredentialResource) basic(data acceptance.TestData) string {
148+
func (r MsSqlJobCredentialResource) basic(data acceptance.TestData) string {
92149
return fmt.Sprintf(`
93-
provider "azurerm" {
94-
features {}
95-
}
96-
97-
resource "azurerm_resource_group" "test" {
98-
name = "acctestRG-jobcredential-%[1]d"
99-
location = "%[2]s"
100-
}
101-
102-
resource "azurerm_mssql_server" "test" {
103-
name = "acctestmssqlserver%[1]d"
104-
resource_group_name = azurerm_resource_group.test.name
105-
location = azurerm_resource_group.test.location
106-
version = "12.0"
107-
administrator_login = "4dministr4t0r"
108-
administrator_login_password = "superSecur3!!!"
109-
}
110-
111-
resource "azurerm_mssql_database" "test" {
112-
name = "acctestmssqldb%[1]d"
113-
server_id = azurerm_mssql_server.test.id
114-
collation = "SQL_Latin1_General_CP1_CI_AS"
115-
sku_name = "S1"
116-
}
117-
118-
resource "azurerm_mssql_job_agent" "test" {
119-
name = "acctestmssqljobagent%[1]d"
120-
location = azurerm_resource_group.test.location
121-
database_id = azurerm_mssql_database.test.id
122-
}
150+
%s
123151
124152
resource "azurerm_mssql_job_credential" "test" {
125-
name = "acctestmssqljobcredential%[1]d"
153+
name = "acctestmssqljobcredential%[2]d"
126154
job_agent_id = azurerm_mssql_job_agent.test.id
127155
username = "test"
128156
password = "test"
129157
}
130-
`, data.RandomInteger, data.Locations.Primary)
158+
`, r.template(data), data.RandomInteger)
131159
}
132160

133161
func (r MsSqlJobCredentialResource) requiresImport(data acceptance.TestData) string {
@@ -143,7 +171,36 @@ resource "azurerm_mssql_job_credential" "import" {
143171
`, r.basic(data))
144172
}
145173

146-
func (MsSqlJobCredentialResource) update(data acceptance.TestData) string {
174+
func (r MsSqlJobCredentialResource) update(data acceptance.TestData) string {
175+
return fmt.Sprintf(`
176+
%s
177+
178+
resource "azurerm_mssql_job_credential" "test" {
179+
name = "acctestmssqljobcredential%[2]d"
180+
job_agent_id = azurerm_mssql_job_agent.test.id
181+
username = "test1"
182+
password = "test1"
183+
}
184+
`, r.template(data), data.RandomInteger)
185+
}
186+
187+
func (r MsSqlJobCredentialResource) writeOnlyPassword(data acceptance.TestData, secret string, version int) string {
188+
return fmt.Sprintf(`
189+
%[1]s
190+
191+
%[2]s
192+
193+
resource "azurerm_mssql_job_credential" "test" {
194+
name = "acctestmssqljobcredential%[3]d"
195+
job_agent_id = azurerm_mssql_job_agent.test.id
196+
username = "test"
197+
password_wo = ephemeral.azurerm_key_vault_secret.test.value
198+
password_wo_version = %[4]d
199+
}
200+
`, r.template(data), acceptance.WriteOnlyKeyVaultSecretTemplate(data, secret), data.RandomInteger, version)
201+
}
202+
203+
func (MsSqlJobCredentialResource) template(data acceptance.TestData) string {
147204
return fmt.Sprintf(`
148205
provider "azurerm" {
149206
features {}
@@ -175,12 +232,5 @@ resource "azurerm_mssql_job_agent" "test" {
175232
location = azurerm_resource_group.test.location
176233
database_id = azurerm_mssql_database.test.id
177234
}
178-
179-
resource "azurerm_mssql_job_credential" "test" {
180-
name = "acctestmssqljobcredential%[1]d"
181-
job_agent_id = azurerm_mssql_job_agent.test.id
182-
username = "test1"
183-
password = "test1"
184-
}
185235
`, data.RandomInteger, data.Locations.Primary)
186236
}

website/docs/r/mssql_job_credential.html.markdown

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,16 @@ The following arguments are supported:
5656

5757
* `job_agent_id` - (Required) The ID of the Elastic Job Agent. Changing this forces a new Elastic Job Credential to be created.
5858

59-
* `username` - (Required) The username part of the credential.
59+
* `username` - (Required) The username to use for this Elastic Job credential.
60+
61+
* `password` - (Optional) The password to use for this Elastic Job credential.
62+
63+
* `password_wo` - (Optional, Write-Only) The password to use for this Elastic Job credential.
64+
65+
~> **Note:** One of `password` or `password_wo` must be specified.
66+
67+
* `password_wo_version` - (Optional) An integer value used to trigger an update for `password_wo`. This property should be incremented when updating `password_wo`.
6068

61-
* `password` - (Required) The password part of the credential.
6269

6370
## Attributes Reference
6471

0 commit comments

Comments
 (0)