Skip to content

Commit 6449a95

Browse files
Export-DbaCredential: Open DAC unless ExcludePassword used (#10112)
1 parent a182c6e commit 6449a95

3 files changed

Lines changed: 63 additions & 131 deletions

File tree

public/Export-DbaCredential.ps1

Lines changed: 60 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ function Export-DbaCredential {
2020
2121
For MFA support, please use Connect-DbaInstance.
2222
23-
.PARAMETER Credential
24-
Login to the target OS using alternative credentials. Accepts credential objects (Get-Credential)
25-
2623
.PARAMETER Path
2724
Specifies the directory where the exported T-SQL script file will be saved. Defaults to the configured DbatoolsExport path.
2825
Use this when you want to control where credential scripts are stored for organization or compliance requirements.
@@ -35,10 +32,6 @@ function Export-DbaCredential {
3532
Specifies which credential names to export by filtering on the Identity property. Accepts an array of credential names.
3633
Use this to export specific credentials instead of all credentials, particularly useful when migrating only certain application or service accounts.
3734
38-
.PARAMETER InputObject
39-
Accepts credential objects piped from Get-DbaCredential, allowing for advanced filtering and processing scenarios.
40-
Use this in pipeline operations when you need to filter or process credentials before exporting them.
41-
4235
.PARAMETER ExcludePassword
4336
Exports credential definitions without the actual password values, replacing them with placeholder text.
4437
Use this for documentation purposes or when you need credential structure without sensitive data for security reviews.
@@ -47,6 +40,10 @@ function Export-DbaCredential {
4740
Adds the exported credential scripts to an existing file instead of overwriting it.
4841
Use this when consolidating credentials from multiple instances into a single deployment script.
4942
43+
.PARAMETER Passthru
44+
Returns the generated T-SQL script to the PowerShell pipeline instead of saving to file.
45+
Use this to capture the script in a variable, pipe to other commands, or display directly in the console.
46+
5047
.PARAMETER EnableException
5148
By default, when something goes wrong we try to catch it, interpret it and give you a friendly warning message.
5249
This avoids overwhelming you with "sea of red" exceptions, but is inconvenient because it basically disables advanced scripting.
@@ -84,23 +81,18 @@ function Export-DbaCredential {
8481
[CmdletBinding()]
8582
param (
8683
[DbaInstanceParameter[]]$SqlInstance,
87-
[string[]]$Identity,
8884
[PSCredential]$SqlCredential,
89-
[PSCredential]$Credential,
9085
[string]$Path = (Get-DbatoolsConfigValue -FullName 'Path.DbatoolsExport'),
9186
[Alias("OutFile", "FileName")]
9287
[string]$FilePath,
88+
[string[]]$Identity,
9389
[switch]$ExcludePassword,
9490
[switch]$Append,
95-
[Parameter(ValueFromPipeline)]
96-
[Microsoft.SqlServer.Management.Smo.Credential[]]$InputObject,
91+
[switch]$Passthru,
9792
[switch]$EnableException
9893
)
9994
begin {
10095
$null = Test-ExportDirectory -Path $Path
101-
$serverArray = @()
102-
$credentialArray = @{ }
103-
$credentialCollection = New-Object System.Collections.ArrayList
10496
}
10597
process {
10698
if (Test-FunctionInterrupt) { return }
@@ -110,137 +102,78 @@ function Export-DbaCredential {
110102
return
111103
}
112104

113-
if (-not $InputObject -and -not $SqlInstance) {
114-
Stop-Function -Message "You must pipe in a Credential or specify a SqlInstance"
115-
return
116-
}
117-
118-
if (Test-Bound -ParameterName SqlInstance) {
119-
foreach ($instance in $SqlInstance) {
120-
try {
121-
try {
122-
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
123-
} catch {
124-
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
125-
}
126-
127-
$serverCreds = $server.Credentials
128-
if (Test-Bound -ParameterName Identity) {
129-
$serverCreds = $serverCreds | Where-Object Identity -In $Identity
130-
}
131-
132-
$InputObject += $serverCreds
133-
} catch {
134-
Stop-Function -Message "Error occurred while establishing connection to $instance" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
105+
foreach ($instance in $SqlInstance) {
106+
try {
107+
if ($ExcludePassword) {
108+
Write-Message -Level Verbose -Message "Opening normal connection because we don't need the passwords."
109+
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9
110+
} else {
111+
Write-Message -Level Verbose -Message "Opening dedicated admin connection for password retrieval."
112+
$server = Connect-DbaInstance -SqlInstance $instance -SqlCredential $SqlCredential -MinimumVersion 9 -DedicatedAdminConnection -WarningAction SilentlyContinue
135113
}
114+
} catch {
115+
Stop-Function -Message "Failure" -Category ConnectionError -ErrorRecord $_ -Target $instance -Continue
136116
}
137-
}
138-
139-
foreach ($input in $InputObject) {
140-
$server = $input.Parent
141-
$instance = $server.DomainInstanceName
142117

143-
if ($serverArray -notcontains $instance) {
144-
try {
145-
if ($ExcludePassword) {
146-
$serverCreds = $server.Credentials
147-
$creds = New-Object System.Collections.ArrayList
148-
149-
foreach ($cred in $server.Credentials) {
150-
$credObject = [PSCustomObject]@{
151-
Name = $cred.Name
152-
Quotename = $server.Query("SELECT QUOTENAME('$($cred.Name.Replace("'", "''"))') AS Quotename").Quotename
153-
Identity = $cred.Identity.ToString()
154-
Password = ''
155-
MappedClassType = $cred.MappedClassType
156-
ProviderName = $cred.ProviderName
157-
}
158-
$creds.Add($credObject) | Out-Null
159-
}
160-
$creds | Add-Member -MemberType NoteProperty -Name 'SqlInstance' -Value $instance
161-
$creds | Add-Member -MemberType NoteProperty -Name 'ExcludePassword' -Value $ExcludePassword
162-
$credentialCollection.Add($credObject) | Out-Null
163-
} else {
164-
if (-not (Test-SqlSa -SqlInstance $server)) {
165-
Stop-Function -Message "Not a sysadmin on $instance. Quitting." -Target $instance -Continue
166-
}
167-
168-
Write-Message -Level Verbose -Message "Getting FullComputerName name for $instance."
169-
$fullComputerName = Resolve-DbaComputerName -ComputerName $server -Credential $Credential
170-
171-
Write-Message -Level Verbose -Message "Checking if Remote Registry is enabled on $instance."
172-
try {
173-
Invoke-Command2 -Raw -Credential $Credential -ComputerName $fullComputerName -ScriptBlock { Get-ItemProperty -Path "HKLM:\SOFTWARE\" } -ErrorAction Stop
174-
} catch {
175-
Stop-Function -Message "Can't connect to registry on $instance." -Target $fullComputerName -ErrorRecord $_
176-
return
177-
}
178-
179-
$creds = Get-DecryptedObject -SqlInstance $server -Type Credential
180-
Write-Message -Level Verbose -Message "Adding Members"
181-
$creds | Add-Member -MemberType NoteProperty -Name 'SqlInstance' -Value $instance
182-
$creds | Add-Member -MemberType NoteProperty -Name 'ExcludePassword' -Value $ExcludePassword
183-
$credentialCollection.Add($creds) | Out-Null
118+
if ($ExcludePassword) {
119+
$credentials = foreach ($cred in $server.Credentials) {
120+
[PSCustomObject]@{
121+
Name = $cred.Name
122+
Quotename = $server.Query("SELECT QUOTENAME('$($cred.Name.Replace("'", "''"))') AS Quotename").Quotename
123+
Identity = $cred.Identity.ToString()
124+
Password = '<EnterStrongPasswordHere>'
125+
MappedClassType = $cred.MappedClassType
126+
ProviderName = $cred.ProviderName
184127
}
185-
} catch {
186-
Stop-Function -Continue -Message "Failure" -ErrorRecord $_
187128
}
188-
189-
$serverArray += $instance
190-
191-
$key = $instance + '::' + $input.Name
192-
$credentialArray.add( $key, $true )
193129
} else {
194-
$key = $instance + '::' + $input.Name
195-
$credentialArray.add( $key, $true )
130+
$credentials = Get-DecryptedObject -SqlInstance $server -Type Credential
196131
}
197-
}
198-
}
199-
200-
end {
201-
$sql = @()
202-
foreach ($cred in $credentialCollection) {
203-
Write-Message -Level Verbose -Message "Credentials in object = $($cred.Count)"
204132

205-
foreach ($currentCred in $creds) {
206-
$FilePath = Get-ExportFilePath -Path $PSBoundParameters.Path -FilePath $PSBoundParameters.FilePath -ServerName $currentCred.SqlInstance -Type Sql
133+
if ($Identity) {
134+
$credentials = $credentials | Where-Object Identity -in $Identity
135+
}
207136

208-
$key = $currentCred.SqlInstance + '::' + $currentCred.Name
209-
if ( $credentialArray.ContainsKey($key) ) {
210-
$quotename = $currentCred.Quotename
211-
$identity = $currentCred.Identity.Replace("'", "''")
137+
if (-not $credentials) {
138+
Write-Message -Level Verbose -Message "Nothing to export"
139+
continue
140+
}
212141

213-
if ($currentCred.MappedClassType -like 'Cryptographic*') {
214-
$providerName = $currentCred.ProviderName
215-
$cryptoSql = " FOR CRYPTOGRAPHIC PROVIDER $providerName"
216-
} else {
217-
$cryptoSql = ""
218-
}
142+
$FilePath = Get-ExportFilePath -Path $PSBoundParameters.Path -FilePath $PSBoundParameters.FilePath -Type sql -ServerName $instance
219143

220-
if ($currentCred.ExcludePassword) {
221-
$sql += "CREATE CREDENTIAL $quotename WITH IDENTITY = N'$identity', SECRET = N'<EnterStrongPasswordHere>'" + $cryptoSql
222-
} else {
223-
$password = $currentCred.Password.Replace("'", "''")
224-
$sql += "CREATE CREDENTIAL $quotename WITH IDENTITY = N'$identity', SECRET = N'$password'" + $cryptoSql
144+
$sql = @()
225145

146+
foreach ($cred in $credentials) {
147+
$quotename = $cred.Quotename
148+
$identity = $cred.Identity.Replace("'", "''")
149+
$password = $cred.Password.Replace("'", "''")
150+
$cryptoSql = ""
151+
if ($cred.MappedClassType -like 'Cryptographic*') {
152+
$providerName = $cred.ProviderName
153+
$cryptoSql = " FOR CRYPTOGRAPHIC PROVIDER $providerName"
154+
}
155+
$sql += "CREATE CREDENTIAL $quotename WITH IDENTITY = N'$identity', SECRET = N'$password'" + $cryptoSql
156+
}
226157

158+
if ($Passthru) {
159+
$sql
160+
} else {
161+
try {
162+
if ($Append) {
163+
Add-Content -Path $FilePath -Value $sql
164+
} else {
165+
Set-Content -Path $FilePath -Value $sql
227166
}
228-
229-
Write-Message -Level Verbose -Message "Created Script for $quotename"
167+
Get-ChildItem -Path $FilePath
168+
} catch {
169+
Stop-Function -Message "Can't write to $FilePath" -ErrorRecord $_ -Continue
230170
}
231171
}
232172

233-
try {
234-
if ($Append) {
235-
Add-Content -Path $FilePath -Value $sql
236-
} else {
237-
Set-Content -Path $FilePath -Value $sql
238-
}
239-
} catch {
240-
Stop-Function -Message "Can't write to $FilePath" -ErrorRecord $_ -Continue
173+
# Disconnect DAC connection if it was opened
174+
if (-not $ExcludePassword) {
175+
$null = $server | Disconnect-DbaInstance -WhatIf:$false
241176
}
242-
Get-ChildItem -Path $FilePath
243-
Write-Message -Level Verbose -Message "Credentials exported to $FilePath"
244177
}
245178
}
246179
}

public/Export-DbaInstance.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ function Export-DbaInstance {
264264
if ($Exclude -notcontains 'Credentials') {
265265
Write-Message -Level Verbose -Message "Exporting SQL credentials"
266266
Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Exporting SQL credentials"
267-
$null = Export-DbaCredential -SqlInstance $server -Credential $Credential -FilePath "$exportPath\credentials.sql" -ExcludePassword:$ExcludePassword
267+
$null = Export-DbaCredential -SqlInstance $server -FilePath "$exportPath\credentials.sql" -ExcludePassword:$ExcludePassword
268268
Get-ChildItem -ErrorAction Ignore -Path "$exportPath\credentials.sql"
269269
}
270270

tests/Export-DbaCredential.Tests.ps1

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ Describe $CommandName -Tag UnitTests {
1212
$expectedParameters = $TestConfig.CommonParameters
1313
$expectedParameters += @(
1414
"SqlInstance",
15-
"Identity",
1615
"SqlCredential",
17-
"Credential",
1816
"Path",
1917
"FilePath",
18+
"Identity",
2019
"ExcludePassword",
2120
"Append",
22-
"InputObject",
21+
"Passthru",
2322
"EnableException"
2423
)
2524
Compare-Object -ReferenceObject $expectedParameters -DifferenceObject $hasParameters | Should -BeNullOrEmpty

0 commit comments

Comments
 (0)