Microsoft DacServices Powershell error on constructor when calling new-object - powershell

I have the following Powershell script I am trying to run:
add-type -path "C:\Program Files (x86)\Microsoft SQL Server\110\DAC\bin\Microsoft.SqlServer.Dac.dll";
$d = new-object Microsoft.SqlServer.Dac.DacServices "server=localhost"
# Load dacpac from file & deploy to database named pubsnew
$dp = [microsoft.sqlserver.dac.dacpackage]::load("c:\deploy\MyDbDacPac.dacpac")
$d.deploy($dp, "MyDb", $true)
However, when it runs, I am getting the following error:
New-Object : Exception calling ".ctor" with "1" argument(s): "The type initializer for 'Microsoft.SqlServer.Dac.DacServices' threw an exception."
At C:\Scripts\DeployDacPac.ps1:3 char:16
+ $d = new-object <<<< Microsoft.SqlServer.Dac.DacServices "server=localhost"
+ CategoryInfo : InvalidOperation: (:) [New-Object], MethodInvocationException
+ FullyQualifiedErrorId : Cons tructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
I am trying to run this for an automated database deploy but cannot get past this weird error.
I have already set my execution policy to remotesigned and updated my runtime version for Powershell to .NET 4.0. Can't figure out what else could be wrong.
Any help would be greatly appreciated!

The problem here is that the default authentication method is SQL Server authentication which expects a username and password. You will need to either supply those parameters or explicitly specify that Windows authentication should be used. You can do this by replacing your connection string argument with the following.
"server=localhost;Integrated Security = True;"
Alternatively, you could use the following function to encapsulate this logic. Note that the default parameter set is 'WindowsAuthentication' which does not include the UserName or Password parameters. If you supply either of these, Powershell will use the 'SqlServerAuthentication' parameter set and the $PSCmdlet.ParameterSetName variable will be set appropriately.
function Get-DacServices()
{
[CmdletBinding(DefaultParameterSetName="WindowsAuthentication")]
Param(
[string]$ServerName = 'localhost',
[Parameter(ParameterSetName='SqlServerAuthentication')]
[string]$UserName,
[Parameter(ParameterSetName='SqlServerAuthentication')]
[string]$Password
)
$connectionString = "server=$serverName;";
if($PSCmdlet.ParameterSetName -eq 'SqlServerAuthentication')
{
$connectionString += "User ID=$databaseUsername;Password=$databasePassword;";
}
else
{
$connectionString += "Integrated Security = True;";
}
$result = new-object Microsoft.SqlServer.Dac.DacServices $connectionString;
return $result;
}

Related

PowerShell on Windows: "Windows Data Protection API (DPAPI) is not supported on this platform."

I need to use the Data Protection API on Windows, but PowerShell does not seem to be able to. When I run this script:
$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser
$ciphertext = [System.Text.Encoding]::UTF8.GetBytes("hallo welt")
$protected = [System.Security.Cryptography.ProtectedData]::Protect($ciphertext, $null, $scope)
$unprotected = [System.Security.Cryptography.ProtectedData]::Unprotect($protected, $null, $scope)
$text = [System.Text.Encoding]::UTF8.GetString($unprotected)
Write-Output $text
I get this output:
Exception calling "Protect" with "3" argument(s): "Windows Data Protection API (DPAPI) is not supported on this platform."
At line:3 char:1
+ $protected = [System.Security.Cryptography.ProtectedData]::Protect($c ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : PlatformNotSupportedException
When I create a console application and do the same thing in C#, it works perfectly. Why does it not work in PowerShell?
Edit:
This does work on PowerShell Core. Why not on the classic PowerShell?
I figured it out. The reason this didn't work in PowerShell but in PowerShell Core was that I actually loaded the wrong assembly in PowerShell.
As soon as I loaded the correct assembly for .net 4.6.1 it worked.
Add-Type -Path "D:\_packages\System.Security.Cryptography.ProtectedData.4.6.0\lib\net461\System.Security.Cryptography.ProtectedData.dll"
$scope = [System.Security.Cryptography.DataProtectionScope]::CurrentUser
$ciphertext = [System.Text.Encoding]::UTF8.GetBytes("hallo welt")
$protected = [System.Security.Cryptography.ProtectedData]::Protect($ciphertext, $null, $scope)
$unprotected = [System.Security.Cryptography.ProtectedData]::Unprotect($protected, $null, $scope)
$text = [System.Text.Encoding]::UTF8.GetString($unprotected)
Write-Output $text

Object reference not set to an instance of an object when opening WinSCP session

I have a PowerShell script that uses the WinSCP .Net library to get some files from a FTP server.
The script runs automatically over night on a server from a administrator account using Task Scheduler.
The problem is that sometimes (it does not have a pattern or at least I can't figure it out) it gives me an error : "Object reference not set to an instance of an object".
Error:
Exception calling "Open" with "1" argument(s): "Object reference not set to an
instance of an object."
At C:\user\blabla\powershellscript.ps1:47
char:5
+ $session.Open($sessionOptions)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NullReferenceException
The actual line from the code (line 47) is : $session.Open($sessionOptions).
If I run the script or the task from Task Scheduler it runs smoothly and does not give me any error.
PowerShell script:
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process
$FileSourcePath = '/path_to_the_file'
Add-Type -Path "C:\WinSCP\WinSCPnet.dll"
# Set up session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "ip_to_the_server"
UserName = "correct_username"
Password = "correct_password"
SshHostKeyFingerprint = "correct_sshrsa_fingerprint"
}
$session = New-Object WinSCP.Session
try {
# Connect
$session.Open($sessionOptions)
# Transfer files
$session.GetFiles($FileSourcePath, $FileDestinationPath).Check()
} catch {
$_ | Out-File c:\blabla\errors.txt -Append
} finally {
$session.Dispose()
}
I fixed it by moving the scripts from Task Schedule to SQL Server Agent job. Now it works flawless.
Thank you for all the feedback and advices.

PowerShell: cannot convert type SMO.server to type SMO.server

Error I'm getting from PowerShell:
Cannot convert argument "srv", with value: "[DBADEV\SQL2008r2]", for "SqlBackup" to type "Microsoft.SqlServer.Management.Smo.Server": "Cannot convert the "[DBADEV\SQL2008r2]" value of type
"Microsoft.SqlServer.Management.Smo.Server" to type "Microsoft.SqlServer.Management.Smo.Server"."
At line:23 char:1
+ $backup.SqlBackup($srv)
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
I'm attempting to write a PowerShell script to restore a database from our Production box and into our DBADEV box. Below is the code I'm using which is then producing the error.
#Clear Screen
cls
#load assemblies
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
$ErrorActionPreference = "Inquire"
# Restore [SQLSRV2k8-0102\SQL2008] instance
$BackupFile = Get-ChildItem "\\NetworkShare\r`$\MSSQL10.SQL2008\MSSQL\Backup\AdventureWorks2008r2" | select -Last 1
$BackupFile
$srv = New-Object Microsoft.SqlServer.Management.Smo.Server("DBADEV\SQL2008r2")
$res = New-Object Microsoft.SqlServer.Management.Smo.Restore
$backup = New-Object Microsoft.SqlServer.Management.Smo.Backup
$Backup.Devices.AddDevice($BackupFile, [Microsoft.SqlServer.Management.Smo.DeviceType]::File)
$Backup.Database = "AdventureWorks2008r2"
$Backup.Action = [Microsoft.SqlServer.Management.Smo.BackupActionType]::Database
$backup.Initialize = $true
$backup.SqlBackup($srv)
$srv.Databases["AdventureWorks2008r2"].Drop()
$res.Devices.AddDevice($BackupFile, [Microsoft.SqlServer.Management.Smo.DeviceType]::File)
$res.Database = "AdventureWorks2008r2"
$res.NoRecovery = $true
$res.SqlRestore($srv)
The error seems a bit cryptic to me (as do most PowerShell errors). Any thoughts on why this is occurring? I'm also getting the error when I use Mike Fal's powershell script here: http://www.mikefal.net/2014/07/22/restoreautomation-powershell-module/
The one thing that seems to get me past this error is by passing "DBADEV\SQL2008r2" directly into SQLRestore,
i.e. $res.SqlRestore("DBADEV\SQL2008r2") instead of $res.SqlRestore($srv)
This now generate an error stating "Restore failed for Server 'DBADEV\SQL2008r2'
Well from my experiences this kind of errors appear because of wrong name/data type in variable.
I would try to escape \ in "DBADEV\SQL2008r2"
or try what I found on MSDN
$srv = new-Object Microsoft.SqlServer.Management.Smo.Server("(local)")
Write-Host $srv.Information.Version
so it could look like
New-Object Microsoft.SqlServer.Management.Smo.Server("(DBADEV\SQL2008r2)")

Binding to a different active directory ldap instance in Powershell

I am trying to connect to some independent LDAP stores (ADAM - Active Directory Application Mode) using a specific set of credentials to bind with, but having trouble working out the best way to do this. Here is an example which I had hoped would work:
$ldapHost = New-Object System.DirectoryServices.DirectoryEntry("LDAP://{serverip}:{port}/dc=acme,dc=com","cn=myuser,dc=acme,dc=com","myPassw0rd")
$ldapQuery = New-Object System.DirectoryServices.DirectorySearcher
$ldapQuery.SearchRoot = $ldapHost
$ldapQuery.Filter = "(objectclass=*)"
$ldapQuery.SearchScope = "Base"
$ldapQuery.FindAll()
This will get me:
Exception calling "FindAll" with "0" argument(s): "A local error has occurred.
"
At line:1 char:19
+ $ldapQuery.FindAll <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
I also tried:
$ldapHost = New-Object System.DirectoryServices.DirectoryEntry("LDAP://{myip}:{port}/dc=acme,dc=com")
$ldapHost.Username = "cn=myuser,dc=acme,dc=com"
which results:
The following exception occurred while retrieving member "Username": "The specified directory service attribute or valu
e does not exist.
"
At line:1 char:11
+ $ldapHost. <<<< Username = "cn=myuser,DC=acme,dc=com"
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
I've tried a few variations with filter etc. Most of the documentation I can find on this just assumes that I'm connecting to ldap from within the same directory/am connecting with the correct user for the query.
If you're familiar with Python's ldap module, this is how I do it in that:
import ldap
ld = ldap.initialize("ldap://{myip}:{port}")
ld.bind_s("cn=myuser,dc=acme,dc=com","Passw0rd")
ld.search_s("dc=acme,dc=com",ldap.SCOPE_BASE,"objectclass=*")
Any pointers on how to approach this? I can definitely connect via the various LDAP clients out there. I might need to explicitly specify authentication, but I'm not sure because there is so little information on querying from outside the domain.
You can try this...I use it to connect to an OpenLDAP instance and it works well. Works against AD also so it should fit your needs. You'll need to update the $basedn variable and the host/username ones.
$hostname = ''
$username = ''
$Null = [System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.Protocols")
#Connects to LDAP
$LDAPConnect = New-Object System.DirectoryServices.Protocols.LdapConnection "$HostName"
#Set session options (SSL + LDAP V3)
$LDAPConnect.SessionOptions.SecureSocketLayer = $true
$LDAPConnect.SessionOptions.ProtocolVersion = 3
# Pick Authentication type:
# Anonymous, Basic, Digest, DPA (Distributed Password Authentication),
# External, Kerberos, Msn, Negotiate, Ntlm, Sicily
$LDAPConnect.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
# Gets username and password.
$credentials = new-object "System.Net.NetworkCredential" -ArgumentList $UserName,(Read-Host "Password" -AsSecureString)
# Bind with the network credentials. Depending on the type of server,
# the username will take different forms.
Try {
$ErrorActionPreference = 'Stop'
$LDAPConnect.Bind($credentials)
$ErrorActionPreference = 'Continue'
}
Catch {
Throw "Error binding to ldap - $($_.Exception.Message)"
}
Write-Verbose "Successfully bound to LDAP!" -Verbose
$basedn = "OU=Users and Groups,DC=TEST,DC=NET"
$scope = [System.DirectoryServices.Protocols.SearchScope]::Subtree
#Null returns all available attributes
$attrlist = $null
$filter = "(objectClass=*)"
$ModelQuery = New-Object System.DirectoryServices.Protocols.SearchRequest -ArgumentList $basedn,$filter,$scope,$attrlist
#$ModelRequest is a System.DirectoryServices.Protocols.SearchResponse
Try {
$ErrorActionPreference = 'Stop'
$ModelRequest = $LDAPConnect.SendRequest($ModelQuery)
$ErrorActionPreference = 'Continue'
}
Catch {
Throw "Problem looking up model account - $($_.Exception.Message)"
}
$ModelRequest
Credit for most of this goes here..
http://mikemstech.blogspot.com/2013/03/searching-non-microsoft-ldap.html
This worked for me. Only use this for testing purposes since password is not secured at all.
Add-Type -AssemblyName System.DirectoryServices.Protocols
$server = test.com
$username = "CN=username,OU=users,DC=test,DC=com"
$password = "userpassword"
$Credentials = new-object System.Net.NetworkCredential($username, $password)
$LdapConnection = New-Object System.DirectoryServices.Protocols.LdapConnection $server
# Basic auth, cleartext password using port 389
$LdapConnection.AuthType = [System.DirectoryServices.Protocols.AuthType]::Basic
$LdapConnection.Bind($Credentials)
$LdapConnection.Dispose()

Powershell CMDLET Cross Domain Managment

I'm logged in at domain "domain1" with my account. I wish via powershell to be able to update users in domain "domain2" via my supe ruser account "suaccount" with password "password1". Trust is established between the two.
Running PowerShell 2.0 and .NET 3.5 SP1
I have gotten this far:
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ctype = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$context = New-Object -TypeName
System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, "domain2", "OU=TestOU,DC=domain2", "suaccount", "password1"
$usr = New-Object -TypeName System.DirectoryServices.AccountManagement.UserPrincipal -ArgumentList $context
$usr.Name = "AM Test1"
$usr.DisplayName = "AM Test1"
$usr.GivenName = "AM"
$usr.SurName = "Test1"
$usr.SamAccountName = "AMTest1"
$usr.UserPrincipalName = "amtest1#mtest.test"
$usr.PasswordNotRequired = $false
$usr.SetPassword("errr")
$usr.Enabled = $true
$usr.Save()
Pretty new to PowerShell, any pointers? I want to edit/create users on the "other" domain so to speak.
I get the error:
"Exception calling "Save" with "0" argument(s): "General access denied error
"
At C:\Script\Sandbox\Morris PowerShell Application\includes\mo\mo.ps1:104 char:14
+ $usr.Save <<<< ()
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException"
Any pointers?
From comments: Try using for username this format domain2\username, and always use the FQDN for the domain. – Christian yesterday